【Android】联通性 -- USB从属模式
类
介绍
UsbManager
允许你列举被连接的USB附件,并跟它们通信。
UsbAccessory
代表一个USB附件,并包含了访问该附件标识信息的方法。
Add-on 类库和平台API之间在使用上的差异
在使用Google APIs (Google API)add-on类库 和 平台API(Android API)之间有两种使用上的差异。
如果使用add-on类库,必须用下列方式获取UsbManager对象:
UsbManager manager=UsbManager.getInstance(this);、
否则,必须用下列方式获取UsbManager对象:
UsbManager manager=(UsbManager) getSystemService(Context.USB_SERVICE);
在使用Intent过滤器过滤一个被连接的附件时,在传递给你的应用程序的Intent对象内部包含了UsbAccessory对象。如果你正在使用add-on类库,就必须使用以下方式来获取UsbAccessory对象:
UsbAccessory accessory=UsbManager.getAccessory(intent);
否则,必须使用以下方式来获取UsbAccessory对象:
UsbAccessory accessory=(UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
三,Android清单要求
下面列出了在使用USB从属模式API工作之前,需要添加到你的应用程序清单文件中的内容:
1. 因为不是所有的Android设备都保证支持USB从属模式API,所以要在<uses-feature>元素中声明使用android.hardware.usb.accessory功能;
2. 如果你是使用add-on类库,还要添加<uses-library>元素来指定要使用的com.android.future.usb.accessory类库;
3. 如果你是使用add-on类库,则要设置该应用程序的SDK的最小版本号是API Level 10,如果使用的是android.hardware.usb包,则最小版本号是API Level 12.
4. 如果你希望获得连接USB附件的通知,就要在你的主Activity中给<intent-filter>和<meta-data>元素指定android.hardware.usb.action.USB_ACCESSORY_ATTACHED类型的Intent。<meta-data>元素要指向一个外部的XML资源文件,该文件中声明了有关你想要检测的附件的标识信息。
在这个XML资源文件中,用<usb-accessory>元素来声明你想要过滤的附件。每个<usb-accessory>元素能够有以下属性:
manufacturer
model
version
该资源文件保存在res/xml目录中。资源文件名称(不含.xml扩展名)必须跟<meta-data>元素中指定的名称相同。下例显示该XML资源文件的格式:
清单和资源文件的示例
下例显示了清单和它对应的资源文件:
<manifest ...>
<uses-feature android:name="android.hardware.usb.accessory" />
<uses-sdk android:minSdkVersion="<version>" />
...
<application>
<uses-library android:name="com.android.future.usb.accessory" />
<activity ...>
...
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter" />
</activity>
</application>
</manifest>
在这种情况下,以下资源文件应该被保存在res/xml/accessory_filter.xml文件中,并且指定了要过滤的附件所对应的模式、制造商和版本号。USB附件会把这些属性发送给Android设备:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessorymodel="DemoKit" manufacturer="Google" version="1.0"/> //发送命令的时候一定要对应
</resources>
获得跟附件通信的权限
在跟USB附件设备进行通信之前,你的应用程序必须要从用户那里获得许可。
注意:如果你的应用程序使用Intent过滤器来发现那些被接入的附件设备,而且用户允许你的应用程序处理该Intent对象,那么会自动的接收许可。否则,在连接附件设备之前,你必须明确的申请接入许可。
明确的申请接入许可,在有些情况下是必须的,如在你的应用程序列举出了已经接入的附件设备,并要跟其中之一进行通信时。在试图跟其通信之前,你必须检查访问该附件设备的权限。否则,如果用户拒绝了你访问该附件设备的请求,你会收到一个运行时错误。
要明确的获得许可,首先就要创建一个广播接收器。这个接收器会监听在调用requestPermission()方法时获得的用于广播的Intent对象。调用requestPermission()方法时会显示一个对话框,向用户请求连接附件设备的权限。以下代码显示了如何创建这个广播接收器:
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) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(accessory != null){
//call method to set up accessory communication
}
}
else {
Log.d(TAG, "permission denied for accessory " + accessory);
}
}
}
}
};
在你的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()方法来显示申请接入附件设备权限的对话框:
UsbAccessory accessory;
...
mUsbManager.requestPermission(accessory, mPermissionIntent);
当用户应答了该对话框,你的广播接收器会收到一个包含了EXTRA_PERMISSION_GRANTED类型附件的Intent对象,它用一个布尔值来代表回答结果。在连接该附件设备之前,要检查该附件的值是否是true。
跟一个附件设备通信
通过使用UsbManagerd对象获取一个文件描述符来跟附件设备通信,你能够使用这个文件描述符建立读写数据的输入和输出流。该流代表了附件设备的输入和数据块端点。你应该在另外一个线程中建立设备和附件之间的通信,以便不至于阻塞主UI线程。下例显示了如何打开跟附件设备的通信:
UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;
...
private void openAccessory() {
Log.d(TAG, "openAccessory: " + accessory);
mFileDescriptor = mUsbManager.openAccessory(mAccessory);
if (mFileDescriptor != null) {
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}
在线程的run()方法中,你可以使用FileInputStream和FileOutputStream对象来读写附件设备。在使用FileInputStream对象从附件设备中读取数据时,要确保有足够大的缓存来存储USB包数据。Android从属模式协议支持的最大包缓存是16384个字节,因此你可以简单的始终把你的缓存声明成这个尺寸。
注意:在底层,对于全速USB附件设备的64字节包和高速USB附件设备的512字节包,Android从属模式协议会简单的把这个两个数据打包成一个逻辑包。
终止跟附件设备的通信
在完成跟附件设备的通信或附件设备被拔出时,你要调用close()方法来关闭被打开的文件描述符。创建以下广播接收器,来监听分离事件:
BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null) {
// call your method that cleans up and closes communication with the accessory
}
}
}
};
在应用程序中创建的广播接收器,而且不在清单文件中,这样的广播接收器允许你的应用程序只在运行时处理分离事件。这种方式,分离事件只发送给当前正在运行的应用程序,并不会广播给所有的应用程序。