【Android】联通性 -- USB主机模式
类
介绍
UsbManager
用于列举被连接的USB设备,并跟其通信。
UsbDevice
代表了一个被连接的USB设备,并且包含了访问它的识别信息、接口、和端点的方法。
UsbInterface
代表了一个USB设备的接口,该接口定义了设备的一组功能。一个设备可以有一个或多个用于通信的接口。
UsbEndpoint
代表一个接口端点,它是跟接口通信的通道。一个接口可以有一个或多个端点,通常会有跟设备进行双工通信的输入和输出端点。
UsbDeviceConnection
代表一个设备连接,它在端点之上传输数据。这个类用于在连接的两个设备间使用同步或异步的方式来回发送数据。
UsbRequest
代表一个通过UsbDeviceConnection对象跟设备通信的异步请求。
UsbConstants
定义了跟Linux内核中linux/usb/ch9.h文件定义对应的USB常量。
在大多数情况中,跟USB设备通信时,需要使用所有这些类(如果你是在使用异步通信,那么只需要使用UsbRequest类)。通常,要使用UsbManager对象来获取期望的UsbDevice对象。当你有了这个UsbDevice对象时,就需要查找对应的UsbInterface对象和基于该接口通信的UsbEndpoint对象。一旦获取了正确的端点,就可以打开UsbDeviceConnect对象来跟USB设备进行通信了。
二,Android清单要求
在使用USB主机模式API工作之前,你需要把以下介绍的内容添加到你的应用程序清单中:
1. 因为不是所有的Android设备都保证支持USB主机模式,所以要在你的应用程序声明中包含<uses-feature>元素,以声明你的应用程序要使用android.hardware.usb.host功能。
2. 把应用程序的最小SDK设置为API Level 12或更高的版本。在较早的API版本中不存在USB主机模式API。
3. 如果你希望你的应用程序能够获得接入USB设备时的通知,那么还要在你的主Activity中指定用android.hardware.usb.action.USB_DEVICE_ATTACHED类型的Intent来配对的<intent-filter>和<meta-data>元素。<meta-data>元素要指向一个外部的XML资源文件,该文件声明了希望检测的设备的识别信息(对方设备发过来的信息)。
在这个XML资源文件中,要用<usb-device>元素来声明你想要过滤的USB设备。以下列出了<usb-device>元素的属性。通常,使用vendor-id和product-id来指定你所希望过滤的特定的设备,并且使用class、subclass和protocol来指定你所希望过滤的USB设备组,如大容量存储设备或数码相机。你可以不指定任何属性,或所有全部属性。不指定任何属性,就会跟所有USB设备匹配,如果应用程序需要,就可以这样做:
A. vendor-id
B. product-id
C. class
D. subclass
E. protocol(设备或接口)
在res/xml目录中保存这个资源文件。该资源文件的名称(不含.xml扩展名)必须跟<meta-data>元素中指定的名称相同。XML资源文件的格式请看下例。
三,清单和资源文件的示例
以下是一个简单的清单文件和它所对应的资源文件:
<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设备进行通信之前,你的应用程序必须要获取用户的许可。
注意:如果你的应用程序使用Intent过滤器来发现接入的USB设备,而且用户允许你的应用程序处理该Intent,那么它会自动的接收权限,否则,在你的应用程序接入该设备之前,必须明确的申请权限。
明确的申请权限在某些情况下是必须的,如你的应用程序列举已经接入的USB设备并想要跟其中的一个设备通信的时候。在试图跟一个设备通信之前,你必须要检查是否有访问设备的权限。否则,如果用户拒绝了你访问该设备的请求,你会收到一个运行时错误。
要明确的获取这个权限,首先要创建一个广播接收器。这个接收器用于监听你调用requestPermission()方法时,系统所发出的Intent对象。调用requestPermission()方法时,系统会显示一个对话框,询问用户是否允许跟该USB设备进行连接。下列代码演示如何创建这个广播接收器:
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);
}
}
}
}
};
在你的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);
调用requestPermission()方法,显示申请接入设备的权限的对话框:
UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);
当用户应答了该对话框时,你的广播接收器就会收到一个包含EXTRA_PERMISSINO_GRANTED类型附加字段的Intent对象,该字段用一个布尔值来代表回答结果。在连接该设备之前,要检查这个字段的值是否是true。
九,跟设备进行通信
跟USB设备的通信既可以是异步的,也可以是同步的。在异步的情况下,你应该创建一个线程来执行所有的数据传输,以便不至于阻塞UI线程。要正确的建立跟设备的通信,你需要获得准备与其通信的设备所对应的UsbInterface和UsbEndpoint对象,并且使用UsbDeviceConnection对象把请求发送给这个端点。通常步骤如下:
1. 检查UsbDevice对象的属性,如产品ID、供应商ID、或者设备的分类,判断该设备是否是你所想要的设备;
2. 当你确认它是你想要与其通信的设备时,就要找到该设备对应的UsbInterface对象以及跟该接口对象一起的UsbEndpoint对象。接口可以有一个或多个端点,通常会有用于双工通信的输入和输出端点;
3. 当你找正确的端点时,就可以打开一个该端点上的UsbDeviceConnection对象;
4. 使用bulkTransfer()或controlTransfer()方法,把你想要传输的数据提供给端点。你应该在另外一个线程中执行本步骤的操作,以便防止阻塞主UI线程。
以下代码片段是一个普通的同步传输数据的方法。你的代码应该有更多的逻辑用于查找用来通信的正确的接口和端点,并且还应该在一个不同于主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类来进行初始化,并发送一个异步请求,让后用requestWait()方法等待结果。
更多的信息请看Adb test sample,它显示了怎样进行异步块数据数据传输,MissleLauncher sample显示了如何监听异步的中断端点。
十,中断跟设备的通信
在你完成跟设备的通信,或者设备被分离时,就要调用releaseInterface()方法和close()方法来关闭UsbInterface和UsbDeviceConnection对象。创建下面这样的广播接收器来监听分离事件:
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
}
}
}
};
如果在应用程序中创建广播接收器,但没有在清单中注册,那么就允许你的应用程序只在运行时处理分离事件。这种情况下,分离事件只会发送给当前运行的应用程序,并且不是广播给所有的应用程序学习