If there is a chance that the application will close, then you must close and release the device. If you don't to this, then the user probably won't be able to open the device again until it is re-plugged. In order to prevent this issue, then you might need to consider creating the
device interface as a service.
Documentation for using USB Host can be found at
http://developer.android.com/guide/topics/usb/host.html. The following are some of the feature that I had to implement in order to get the interface working.
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
<uses-sdk android:minSdkVersion="12" /> <uses-feature android:name="android.hardware.usb.host" />
HIDInterface.java
BroadcastReceiver - This is required to receive the registered events
/*
* Receives a requested broadcast from the operating system.
* In this case the following actions are handled:
* USB_PERMISSION
* UsbManager.ACTION_USB_DEVICE_DETACHED
* UsbManager.ACTION_USB_DEVICE_ATTACHED
*/
private final BroadcastReceiver usbReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
// Validate the action against the actions registered
...
}
};
Requesting Permission - In order to connect to the device, the user has to give their permission. This must be implemented in the BroadcastReceiver that will handle the response to the question when it is displayed to the user.
The variable
private static final String ACTION_USB_PERMISSION = "com.access.device.USB_PERMISSION";
is a predefined variable for registering to receive device permissions.
The BroadcastReceiver code
if (ACTION_USB_PERMISSION.equals(action))
{
// A permission response has been received, validate if the user has
// GRANTED, or DENIED permission
synchronized (this)
{
UsbDevice deviceConnected = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
{
// Permission has been granted, so connect to the device
// If this fails, then keep looking
if (deviceConnected != null)
{
// call method to setup device communication
currentDevice = deviceConnected;
if (ShowDebuggingToasts)
{
Toast.makeText(hostDisplayActivity, "Device Permission Acquired", Toast.LENGTH_SHORT).show();
}
if (!ConnectToDeviceInterface(currentDevice))
{
if (ShowDebuggingToasts)
{
Toast.makeText(hostDisplayActivity, "Unable to connect to device", Toast.LENGTH_SHORT).show();
}
}
sendEnabledMessage();
// Create a listener to receive messages from the host
...
}
}
else
{
// Permission has not been granted, so keep looking for another
// device to be attached....
if (ShowDebuggingToasts)
{
Toast.makeText(hostDisplayActivity, "Device Permission Denied", Toast.LENGTH_SHORT).show();
}
currentDevice = null;
}
}
}
}
Registering the intent
usbManager = (UsbManager) hostDisplayActivity.getSystemService(Context.USB_SERVICE);
permissionIntent = PendingIntent.getBroadcast(hostDisplayActivity, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter permissionFilter = new IntentFilter(ACTION_USB_PERMISSION);
hostDisplayActivity.registerReceiver(usbReceiver, permissionFilter);
Requesting permission
UsbDevice currentDevice = .....;
usbManager.requestPermission(currentDevice, permissionIntent);
Device Attached and Detached Intents - Handling a device attached or detached intent, requires that the user register them first.
Registering the intent
usbManager = (UsbManager) hostDisplayActivity.getSystemService(Context.USB_SERVICE);
IntentFilter deviceAttachedFilter = new IntentFilter();
deviceAttachedFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
deviceAttachedFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
hostDisplayActivity.registerReceiver(usbReceiver, deviceAttachedFilter);
The BroadcastReceiver code
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action))
{
// A device has been detached from the device, so close all the connections
// and restart the search for a new device being attached
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if ((device != null) && (currentDevice != null))
{
if (device.equals(currentDevice))
{
// call your method that cleans up and closes communication with the device
CleanUpAndClose();
}
}
}
else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action))
{
// A device has been attached. If not already connected to a device,
// validate if this device is supported
UsbDevice searchDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if ((searchDevice != null) && (currentDevice == null))
{
// call your method that cleans up and closes communication with the device
ValidateFoundDevice(searchDevice);
}
}
Validating the Connected Device - Before asking for permission to connect to the device, it is essential that you ensure that this is a device that you support or expect to connect to. This can be done by validating the devices Vendor ID and Product ID.
A vendor id is a global identifier for the manufacturer. A product id refers to the product itself, and is unique to the manufacturer. The vendor id, product id combination refers to a particular product manufactured by a vendor.
searchDevice.getProductId()
searchDevice.getVendorId()
Connecting to the Device - If you are reading and writing, then the device can either have two end points on a single interface, or two interfaces each with a single end point. Either way, it is best if you know which interface you need to use and which end points.
UsbInterface usbInterfaceRead = null;
UsbInterface usbInterfaceWrite = null;
UsbEndpoint ep1 = null;
UsbEndpoint ep2 = null;
boolean UsingSingleInterface = false;
if (UsingSingleInterface)
{
// Using the same interface for reading and writing
usbInterfaceRead = connectDevice.getInterface(0x00);
usbInterfaceWrite = usbInterfaceRead;
if (usbInterfaceRead.getEndpointCount() == 2)
{
ep1 = usbInterfaceRead.getEndpoint(0);
ep2 = usbInterfaceRead.getEndpoint(1);
}
}
else // if (!UsingSingleInterface)
{
usbInterfaceRead = connectDevice.getInterface(0x00);
usbInterfaceWrite = connectDevice.getInterface(0x01);
if ((usbInterfaceRead.getEndpointCount() == 1) && (usbInterfaceWrite.getEndpointCount() == 1))
{
ep1 = usbInterfaceRead.getEndpoint(0);
ep2 = usbInterfaceWrite.getEndpoint(0);
}
}
if ((ep1 == null) || (ep2 == null))
{
return false;
}
// Determine which endpoint is the read, and which is the write
if (ep1.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)
{
if (ep1.getDirection() == UsbConstants.USB_DIR_IN)
{
usbEndpointRead = ep1;
}
else if (ep1.getDirection() == UsbConstants.USB_DIR_OUT)
{
usbEndpointWrite = ep1;
}
}
if (ep2.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)
{
if (ep2.getDirection() == UsbConstants.USB_DIR_IN)
{
usbEndpointRead = ep2;
}
else if (ep2.getDirection() == UsbConstants.USB_DIR_OUT)
{
usbEndpointWrite = ep2;
}
}
if ((usbEndpointRead == null) || (usbEndpointWrite == null))
{
return false;
}
connectionRead = usbManager.openDevice(connectDevice);
connectionRead.claimInterface(usbInterfaceRead, true);
if (UsingSingleInterface)
{
connectionWrite = connectionRead;
}
else // if (!UsingSingleInterface)
{
connectionWrite = usbManager.openDevice(connectDevice);
connectionWrite.claimInterface(usbInterfaceWrite, true);
}
// Start the read thread
Writing to the Device - Some devices require the controlTransfer method for writing data. I don't cover this command in this blog. As USB devices work with reports of a specific length. It is best to format the data being sent into a ByteBuffer of the correct length. The length required can be retrieved from the endpoint.
int bufferDataLength = usbEndpointWrite.getMaxPacketSize();
The report must be formatted correctly for the device being connected to. On some devices, this requires that a specific value must be the first byte in the report. This can be followed by the length of the data in the report. This format is determined by the device, and isn't specified here.
int bufferDataLength = usbEndpointWrite.getMaxPacketSize();
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
UsbRequest request = new UsbRequest();
buffer.put(DataToSend);
request.initialize(connectionWrite, usbEndpointWrite);
request.queue(buffer, bufferDataLength);
try
{
if (request.equals(connectionWrite.requestWait()))
{
return true;
}
}
catch (Exception ex)
{
// An exception has occured
}
Reading from the Device - As USB devices work with reports of a specific length. The data received from the device will always be the size specified by the report length. Even if there isn't enough data to fill the report. Some devices require the controlTransfer method for reading data. I don't cover this command in this blog.
If you are expecting unsolicited data from the device, then a read thread should be started so that the data can be processed as soon as it arrives.
int bufferDataLength = usbEndpointRead.getMaxPacketSize();
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
UsbRequest requestQueued = null;
UsbRequest request = new UsbRequest();
request.initialize(connectionRead, usbEndpointRead);
try
{
while (!getStopping())
{
request.queue(buffer, bufferDataLength);
requestQueued = connectionRead.requestWait();
if (request.equals(requestQueued))
{
byte[] byteBuffer = new byte[bufferDataLength + 1];
buffer.get(byteBuffer, 0, bufferDataLength);
// Handle data received
...
buffer.clear();
}
else
{
Thread.sleep(20);
}
}
}
catch (Exception ex)
{
// An exception has occured
}
try
{
request.cancel();
request.close();
}
catch (Exception ex)
{
// An exception has occured
}
This isn't the most comprehensive or complete document, but hopefully it will help someone to implement a USB Host Device interface.