Saturday, February 1, 2014

Read iManufacturer and iProduct of USB Device from raw Device Descriptors

The device descriptor of a USB device represents the entire device. It specifies some basic, such as iManufacturer and iProduct. (reference: http://www.beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors)

This example, modify from last post "List UsbDevice, UsbInterface and UsbEndpoint in USB Host mode", to read iManufacturer and iProduct of USB Device from raw Device Descriptors using UsbDeviceConnection.getRawDescriptors() (API level 13) and UsbDeviceConnection.controlTransfer() methods.

iManufacturer and iProduct of USB Device
iManufacturer and iProduct of Arduino Esplora
To call UsbDeviceConnection.getRawDescriptors(), modify AndroidManifest.xml to set android:minSdkVersion="13". And also  specify uses-feature of "android.hardware.usb.host".

In order to make connection to USB Devices in your app, you have to get permission from user:
- Implement and register BroadcastReceiver for USB Permission Request Dialog.
- Check UsbManager's hasPermission(device) before openDevice(device) called, and call requestPermission(device, PermissionIntent) to open USB Permission Request Dialog to user if needed.
- Then read raw Device Descriptors with UsbDeviceConnection's getRawDescriptors() and controlTransfer().
- Close UsbInterface and UsbDeviceConnection by calling releaseInterface() and close() after used.

package com.example.androidusbhost;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.text.InputFilter.LengthFilter;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

public class MainActivity extends Activity {

 Button btnCheck;
 TextView textInfo;
 TextView textInfoInterface;
 TextView textEndPoint;
 
 Spinner spDeviceName;
 ArrayList<String> listDeviceName;
 ArrayList<UsbDevice> listUsbDevice;
 ArrayAdapter<String> adapterDevice;
 
 Spinner spInterface;
 ArrayList<String> listInterface;
 ArrayList<UsbInterface> listUsbInterface;
 ArrayAdapter<String> adapterInterface;
 
 Spinner spEndPoint;
 ArrayList<String> listEndPoint;
 ArrayList<UsbEndpoint> listUsbEndpoint;
 ArrayAdapter<String> adapterEndpoint;
 
 Button btnReadRawDescriptors;

 private static final String ACTION_USB_PERMISSION = 
   "com.android.example.USB_PERMISSION";
 PendingIntent mPermissionIntent;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  spDeviceName = (Spinner)findViewById(R.id.spinnerdevicename);
  spInterface = (Spinner)findViewById(R.id.spinnerinterface);
  spEndPoint = (Spinner)findViewById(R.id.spinnerendpoint);
  textInfo = (TextView) findViewById(R.id.info);
  textInfoInterface = (TextView)findViewById(R.id.infointerface);
  textEndPoint = (TextView)findViewById(R.id.infoendpoint);
  
  btnCheck = (Button) findViewById(R.id.check);
  btnCheck.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View arg0) {
    checkDeviceInfo();
   }
  });
  
  btnReadRawDescriptors = (Button)findViewById(R.id.readRawDescriptors);
  btnReadRawDescriptors.setEnabled(false);
  btnReadRawDescriptors.setOnClickListener(btnReadRawDescriptorsOnClickListener);
  
  //register the broadcast receiver
  mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
  IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
  registerReceiver(mUsbReceiver, filter);
 }
 
 OnClickListener btnReadRawDescriptorsOnClickListener =
   new OnClickListener(){

    @Override
    public void onClick(View arg0) {
     UsbDevice deviceToRead = listUsbDevice
       .get(spDeviceName.getSelectedItemPosition());
     UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
     
     Boolean permitToRead = manager.hasPermission(deviceToRead);
     
     if(permitToRead){
      doReadRawDescriptors(deviceToRead);
     }else{
      manager.requestPermission(deviceToRead, mPermissionIntent);
      Toast.makeText(MainActivity.this, 
        "Permission: " + permitToRead, 
        Toast.LENGTH_LONG).show();
     }
    }
  
 };
 
 private final BroadcastReceiver mUsbReceiver = 
   new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
     String action = intent.getAction();
     if (ACTION_USB_PERMISSION.equals(action)) {
                  synchronized (this) {
                      UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                      if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                          if(device != null){
                           doReadRawDescriptors(device);
                         }
                      } 
                      else {
                          Toast.makeText(MainActivity.this, 
                            "permission denied for device " + device, 
                            Toast.LENGTH_LONG).show();
                      }
                  }
              }
    }
  
 };
 
 private void doReadRawDescriptors(UsbDevice device){
  final int STD_USB_REQUEST_GET_DESCRIPTOR = 0x06;
  final int LIBUSB_DT_STRING = 0x03;
  
  boolean forceClaim = true;
  
  byte[] buffer = new byte[255];
        int indexManufacturer = 14;
        int indexProduct = 15;
        String stringManufacturer = "";
        String stringProduct = "";
  
  UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
  UsbDeviceConnection connection = manager.openDevice(device);
  if(connection != null){
   UsbInterface intf = device.getInterface(0);
   connection.claimInterface(intf, forceClaim);
   
   byte[] rawDescriptors = connection.getRawDescriptors();
   
   int lengthManufacturer = connection.controlTransfer(
     UsbConstants.USB_DIR_IN|UsbConstants.USB_TYPE_STANDARD,
     STD_USB_REQUEST_GET_DESCRIPTOR,
     (LIBUSB_DT_STRING << 8) | rawDescriptors[indexManufacturer],
     0,
     buffer,
     0xFF,
     0);
   try {
    stringManufacturer = new String(buffer, 2, lengthManufacturer-2, "UTF-16LE");
   } catch (UnsupportedEncodingException e) {
    Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
   }
   
   int lengthProduct = connection.controlTransfer(
     UsbConstants.USB_DIR_IN|UsbConstants.USB_TYPE_STANDARD,
     STD_USB_REQUEST_GET_DESCRIPTOR,
     (LIBUSB_DT_STRING << 8) | rawDescriptors[indexProduct],
     0,
     buffer,
     0xFF,
     0);
   try {
    stringProduct = new String(buffer, 2, lengthProduct-2, "UTF-16LE");
   } catch (UnsupportedEncodingException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
   Toast.makeText(MainActivity.this, 
     "Manufacturer: " + stringManufacturer + "\n" +
     "Product: " + stringProduct, 
     Toast.LENGTH_LONG).show();
   
   connection.releaseInterface(intf);
   connection.close();
  }else{
   Toast.makeText(MainActivity.this, 
     "open failed", 
     Toast.LENGTH_LONG).show();
  }
 }

 private void checkDeviceInfo() {
  
  listDeviceName = new ArrayList<String>();
  listUsbDevice = new ArrayList<UsbDevice>();
  
  UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
  HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
  Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();

  btnReadRawDescriptors.setEnabled(false);
  while (deviceIterator.hasNext()) {
   UsbDevice device = deviceIterator.next();
   listDeviceName.add(device.getDeviceName());
   listUsbDevice.add(device);
   
   btnReadRawDescriptors.setEnabled(true);
  }
  
  textInfo.setText("");
  textInfoInterface.setText("");
  textEndPoint.setText("");
  
  adapterDevice = new ArrayAdapter<String>(this, 
    android.R.layout.simple_spinner_item, listDeviceName);
  adapterDevice.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  spDeviceName.setAdapter(adapterDevice);
  spDeviceName.setOnItemSelectedListener(deviceOnItemSelectedListener);
 }
 
 OnItemSelectedListener deviceOnItemSelectedListener = 
   new OnItemSelectedListener(){

  @Override
  public void onItemSelected(AdapterView<?> parent, 
    View view, int position, long id) {
   UsbDevice device = listUsbDevice.get(position);
   
   String i = device.toString() + "\n" + 
     "DeviceID: " + device.getDeviceId() + "\n" +
     "DeviceName: " + device.getDeviceName() + "\n" +
     "DeviceClass: " + device.getDeviceClass() + " - " 
      + translateDeviceClass(device.getDeviceClass()) + "\n" +
     "DeviceSubClass: " + device.getDeviceSubclass() + "\n" +
     "VendorID: " + device.getVendorId() + "\n" +
     "ProductID: " + device.getProductId() + "\n" +
     "InterfaceCount: " + device.getInterfaceCount();
   textInfo.setText(i);
   
   checkUsbDevicve(device);
  }

  @Override
  public void onNothingSelected(AdapterView<?> parent) {}
  
 };
 
 private void checkUsbDevicve(UsbDevice d) {
  listInterface = new ArrayList<String>();
  listUsbInterface = new ArrayList<UsbInterface>();
  
  for(int i=0; i<d.getInterfaceCount(); i++){
   UsbInterface usbif = d.getInterface(i);
   listInterface.add(usbif.toString());
   listUsbInterface.add(usbif);
  }
  
  adapterInterface = new ArrayAdapter<String>(this, 
    android.R.layout.simple_spinner_item, listInterface);
  adapterDevice.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  spInterface.setAdapter(adapterInterface);
  spInterface.setOnItemSelectedListener(interfaceOnItemSelectedListener);
 }
 
 OnItemSelectedListener interfaceOnItemSelectedListener = 
   new OnItemSelectedListener(){

  @Override
  public void onItemSelected(AdapterView<?> parent, 
    View view, int position, long id) {
   
   UsbInterface selectedUsbIf = listUsbInterface.get(position);
   
   String sUsbIf = "\n" + selectedUsbIf.toString() + "\n"
     + "Id: " + selectedUsbIf.getId() + "\n"
     + "InterfaceClass: " + selectedUsbIf.getInterfaceClass() + "\n"
     + "InterfaceProtocol: " + selectedUsbIf.getInterfaceProtocol() + "\n"
     + "InterfaceSubclass: " + selectedUsbIf.getInterfaceSubclass() + "\n"
     + "EndpointCount: " + selectedUsbIf.getEndpointCount();
   
   textInfoInterface.setText(sUsbIf);
   checkUsbInterface(selectedUsbIf);
  }

  @Override
  public void onNothingSelected(AdapterView<?> parent) {}
  
 };
 
 private void checkUsbInterface(UsbInterface uif) {
  listEndPoint = new ArrayList<String>();
  listUsbEndpoint = new ArrayList<UsbEndpoint>();

  for(int i=0; i<uif.getEndpointCount(); i++){
   UsbEndpoint usbEndpoint = uif.getEndpoint(i);
   listEndPoint.add(usbEndpoint.toString());
   listUsbEndpoint.add(usbEndpoint);
  }
  
  adapterEndpoint = new ArrayAdapter<String>(this, 
    android.R.layout.simple_spinner_item, listEndPoint);
  adapterEndpoint.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  spEndPoint.setAdapter(adapterEndpoint);
  spEndPoint.setOnItemSelectedListener(endpointOnItemSelectedListener);
 }
 
 OnItemSelectedListener endpointOnItemSelectedListener = 
   new OnItemSelectedListener(){

  @Override
  public void onItemSelected(AdapterView<?> parent, 
    View view, int position, long id) {
   
   UsbEndpoint selectedEndpoint = listUsbEndpoint.get(position);
   
   String sEndpoint = "\n" + selectedEndpoint.toString() + "\n"
    + translateEndpointType(selectedEndpoint.getType());
   
   textEndPoint.setText(sEndpoint);
  }

  @Override
  public void onNothingSelected(AdapterView<?> parent) {}
  
 };
 
 private String translateEndpointType(int type){
  switch(type){
  case UsbConstants.USB_ENDPOINT_XFER_CONTROL:
   return "USB_ENDPOINT_XFER_CONTROL (endpoint zero)";
  case UsbConstants.USB_ENDPOINT_XFER_ISOC:
   return "USB_ENDPOINT_XFER_ISOC (isochronous endpoint)";
  case UsbConstants.USB_ENDPOINT_XFER_BULK :
   return "USB_ENDPOINT_XFER_BULK (bulk endpoint)";
  case UsbConstants.USB_ENDPOINT_XFER_INT:
   return "USB_ENDPOINT_XFER_INT (interrupt endpoint)";
  default: 
   return "unknown";
  }
 }
 
 private String translateDeviceClass(int deviceClass){
  switch(deviceClass){
  case UsbConstants.USB_CLASS_APP_SPEC: 
   return "Application specific USB class";
  case UsbConstants.USB_CLASS_AUDIO: 
   return "USB class for audio devices";
  case UsbConstants.USB_CLASS_CDC_DATA: 
   return "USB class for CDC devices (communications device class)";
  case UsbConstants.USB_CLASS_COMM: 
   return "USB class for communication devices";
  case UsbConstants.USB_CLASS_CONTENT_SEC: 
   return "USB class for content security devices";
  case UsbConstants.USB_CLASS_CSCID: 
   return "USB class for content smart card devices";
  case UsbConstants.USB_CLASS_HID: 
   return "USB class for human interface devices (for example, mice and keyboards)";
  case UsbConstants.USB_CLASS_HUB: 
   return "USB class for USB hubs";
  case UsbConstants.USB_CLASS_MASS_STORAGE: 
   return "USB class for mass storage devices";
  case UsbConstants.USB_CLASS_MISC: 
   return "USB class for wireless miscellaneous devices";
  case UsbConstants.USB_CLASS_PER_INTERFACE: 
   return "USB class indicating that the class is determined on a per-interface basis";
  case UsbConstants.USB_CLASS_PHYSICA: 
   return "USB class for physical devices";
  case UsbConstants.USB_CLASS_PRINTER: 
   return "USB class for printers";
  case UsbConstants.USB_CLASS_STILL_IMAGE: 
   return "USB class for still image devices (digital cameras)";
  case UsbConstants.USB_CLASS_VENDOR_SPEC: 
   return "Vendor specific USB class";
  case UsbConstants.USB_CLASS_VIDEO: 
   return "USB class for video devices";
  case UsbConstants.USB_CLASS_WIRELESS_CONTROLLER: 
   return "USB class for wireless controller devices";
  default: return "Unknown USB class!";
  
  }
 }

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Button
        android:id="@+id/check"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Check USB devices" />

    <Spinner
        android:id="@+id/spinnerdevicename"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    
    <Button
        android:id="@+id/readRawDescriptors"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Read Raw Descriptors" />

    <Spinner
        android:id="@+id/spinnerinterface"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    
    <Spinner
        android:id="@+id/spinnerendpoint"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/info"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <TextView
                android:id="@+id/infointerface"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textStyle="italic" />
            
            <TextView
                android:id="@+id/infoendpoint"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textStyle="bold" />
            
        </LinearLayout>
    </ScrollView>

</LinearLayout>



download filesDownload the files.



Step-by-step: Android USB Host Mode programming

No comments: