当你的Android设备处于USB host 模式时,它扮演USB host的角色,驱动总线,并枚举连接的USB设备。在Android 3.1及更高版本中支持USB host模式。
在你开始之前,理解你将要使用的类是很重要的。下表描述了在android.hardware.usb包中的USB host APIs。
Table 1. USB Host APIs
Class | Description |
---|---|
UsbManager | 允许你枚举连接的USB设备并与之通信。 |
UsbDevice | 表示一个连接的USB设备,并包含访问它的标识信息,接口和endpoints的方法。 |
UsbInterface | 表示一个USB设备的接口,其为设备定义了一系列的功能。一个设备可以包含一个或多个能够通信的接口。 |
UsbEndpoint | 表示一个接口endpoint,它是这个接口的一个通信通道。一个接口具有一个或多个endpoints,但对于与设备的双向通信通常具有输入和输出endpoints。 |
UsbDeviceConnection | 表示一个与设备的连接,其在endpoints生传输数据。这个类允许同步或异步地向前和向后发送数据。 |
UsbRequest | 表示通过一个UsbDeviceConnection与一个设备通信的一个异步请求。 |
UsbConstants | 定义了对应于Linux kernel的linux/usb/ch9.h中所定义的USB常量。 |
下面的列表描述了在使用USB host APIs之前,你需要添加到你应用程序的manifest文件的内容:
在XML资源文件中,为你想要过滤出的USB设备声明<usb-device>元素。下面的列表描述了<usb-device>的属性。通常,如果你想要过滤出一个特定的设备的话,使用vendor和product ID,如果你想要过滤出一个USB设备组时,如大容量存储设备或数码相机,则使用class,subclass和protocol。你可以一个也不指定,或指定所有的这些属性。不指定属性则匹配每个USB设备,因而只在你的应用需要时才这样做:
把资源文件保存在res/xml/目录下。资源文件的名字(不包含.xml扩展名)必须与<meta-data>元素中指定的那个相同。XML资源文件的格式在下面的example中。
下面的例子展示了一个示例manifest和它对应的资源文件:
<manifest ...> <uses-feature android:name="android.hardware.usb.host" /> <uses-sdk android:minSdkVersion="12" /> ... <application> <activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity> </application> </manifest>在这个示例中,下面的资源文件应该被保存在res/xml/device_filter.xml,并指定由特定的属性所描述的应该被过滤出来的USB设备:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" /> </resources>
当用户把USB设备连接到了一个Android设备时,Android系统可以确定你的应用程序是否对连接的设备感兴趣。如果感兴趣,如果喜欢,则你可以设置与设备的通信。要做到这一点,你的应用程序不得不:
你的设备可以通过使用一个在用户连接一个设备时会被通知到的intent filter,或通过枚举已经连接的USB设备来发现USB设备。如果你想要使你的应用程序能够自动地探测一个想要的设备,则使用一个intent filter很有用。如果你想要获取一个所有已连接设备的列表,或如果你的应用程序不过滤一个intent filter,枚举已连接的USB设备很有用。
要使你的应用程序发现一个特定的USB设备,你可以为android.hardware.usb.action.USB_DEVICE_ATTACHED intent指定一个intent filter。伴随着这个intent filter,你需要指定一个资源文件,其描述了这个USB设备的属性,诸如product和vendor ID这些。当用户连接了一个与你的设备filter匹配的设备时,系统将会呈现给用户一个dialog,来向用户询问是否要启动你的应用程序。如果用户接受了,你的应用程序自动地具有访问设备的权限,直到设备断开连接。
下面的例子展示了如何声明intent filter:
<activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity>下面的例子展示了如何声明对应的描述了你所感兴趣的USB设备的资源文件:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" /> </resources>在你的activity中,你可以像这样从intent中获取表示附接的设备的UsbDevice:
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
如果你的应用程序,对于它在运行时当前所连接的所有的USB设备感兴趣,它可以枚举总线上的设备。使用getDeviceList()方法来获取一个所有的已连接USB设备的hash map。如果你想要从hash map中获取一个设备的话,hash map的key是USB设备的名字。
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ... HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); UsbDevice device = deviceList.get("deviceName");如果需要,你也可以从hash map中获取一个iterator,然后一个接一个地处理每个设备。
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ... HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); while(deviceIterator.hasNext()){ UsbDevice device = deviceIterator.next() //your code }
在与USB设备通信前,你的应用程序必须从用户那儿获取相应的权限。
注意:如果你的应用程序使用了一个intent filter来在USB设备连接时发现它们,则如果用户允许你的应用程序处理intent的话,它将自动地接受权限。如果不允许,你必须在你的应用程序中,在与设备连接前显式地请求权限。
在某些情况下,显式地请求权限可能是需要的,比如当你的应用程序枚举已经连接的USB设备并想要与其中的一个通信时。你必须在尝试与之通信前,检查访问一个设备的权限。如果没有,如果用户拒绝了访问设备的权限,你将接收一个runtime error。
要显式地获取权限,首先创建一个broadcast receiver。当你调用requestPermission()时,这个receiver监听被broadcast的intent。对于requestPermission()的调用显示一个dialog给用户,以请求连接设备的权限。下面的示例代码展示了如何创建这个broadcast receiver:
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { 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){ //call method to set up device communication } } else { Log.d(TAG, "permission denied for device " + device); } } } } };要注册broadcast receiver,则把这些添加进你的activity里的onCreate()方法中。
UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; ... mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(mUsbReceiver, filter);要显示请求与设备连接授权的dialog,则调用requestPermission()方法:
UsbDevice device; ... mUsbManager.requestPermission(device, mPermissionIntent);
当用户回复了dialog,你的broadcast receiver接收到intent,其中包含EXTRA_PERMISSION_GRANTED extra,它是一个boolean值,表示用户的回答。在连接到设备之前,检查这个extra的值为true。
与一个USB设备通信可能是同步的或异步的。在每中情况下,你都应该创建一个新的线程,来执行所有的数据传输,以便于不会block了UI thread。要适当地设置与一个设备的通信,你需要获取你想要与之通信的设备的适当的UsbInterface和UsbEndpoint,并通过一个UsbDeviceConnection来在这个endpoint上发送请求。通常,你的代码应当:
下边的代码片段是一个简单的方法来执行一个同步的数据传输。你的代码应该具有更过的逻辑来正确地找到正确的于其上进行通信的interface和endpoints,并且也应该在一个不同的线程中执行数据的传送,而不是在main UI 线程中:
private Byte[] bytes private static int TIMEOUT = 0; private boolean forceClaim = true; ... UsbInterface intf = device.getInterface(0); UsbEndpoint endpoint = intf.getEndpoint(0); UsbDeviceConnection connection = mUsbManager.openDevice(device); connection.claimInterface(intf, forceClaim); connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread
要异步地发送数据,则使用UsbRequest类来初始化并入队一个异步的request,然后通过requestWait()等待结果。
更多信息,请参考AdbTest sample,其展示了如何执行异步的bulk传输,及MissleLauncher sample,其展示了如何在一个interrupt endpoint上异步地监听。
当你完成了与一个设备的通信,或如果设备断开了,则通过调用releaseInterface()和close()来关闭UsbInterface和UsbDeviceConnection。要监听断开事件,则创建一个如下的broadcast receiver:
BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { // call your method that cleans up and closes communication with the device } } } };在应用程序内创建broadcast receiver,而不是在manifest中,以允许你的应用程序只在它运行时处理断开事件。这种方式,则断开事件只发送给当前运行的应用程序,而不是broadcast给所有的应用程序。
Done.
原文:http://developer.android.com/guide/topics/connectivity/usb/host.html