Android USB 主机模式

Android USB Host(Android USB 主机通讯)

     翻译很烂,纯当练手,大家权当消遣好了…   :》
    

    当你的Android手机是在USB主机模式下,它充当USB主机,开启设备,并列举已连接的USB设备。 这种模式,在Android 3.1及以上版本的支持。
    

    API 预览

    在开始学习USB Host之前,理解并掌握你所需要用到的类是很重要的。下面的表格描述了 android.hardware.usb包下的USB host 的API。
    
Class Description
UsbManager 可以让你列举USB设备,并和设备交互;
UsbDevice 表示已连接的USB设备, 包含了访问设备标识信息的方法、接口和挂在点。
UsbInterface 表示设备接口,定义了一些列的功能函数。一个设备可以包含一个或者多个接口。
UsbEndpoint
表示一个挂载点接口,定义了一个交互通道。一个接口可以包含一个或者多个挂载点,
通常都包含用于和USB设备交互的输入输出双向端点  。
UsbDeviceConnection 表示到USB设备的一个基于端点通信的接口,这个类支持同步或者异步数据的来回通讯;
UsbRequest 代表一个异步的,基于UsbDeviceConnection通讯的异步请求。
UsbConstants 定义了一些USB常量,与linux内核中的 linux/usb/ch9.h 中的定义一致。
    在和USB设备交互的时候,通常情况下,你需要用到所有的这些类(除了交互的时候,只需要用到 UsbRequest)。总的来说,你首先需要获取   UsbManager对象来查询目标设备 UsbDevice。当获得设备 UsbDevice之后,你需要找到适当的   UsbInterface以及该接口对应的 UsbEndpoint  ,从而进行交互。一旦拿到真确的端点,打开连接 UsbDeviceConnection,以此和USB设备进行交互。

    Manifest文件的必要节点

    下面的列表,列出了在使用USB host的API之前,你所需要添加到Manifest文件中的东西:
     由于不能确保所有的Android设备都支持USB host 的API , 我们可以在Manifest文件中加入  <uses-feature>节点,来声明我们的应用需要 android.hardware.usb.host 支持.设置应用程序的最低支持的SDK版本为12或者更高. USB 主机模式的 API 在12以前的版本上不适用.如果希望你的应用能够接收到USB设备挂在的通知,则需要在你的主Activity的 android.hardware.usb.action.USB_DEVICE_ATTACHED过滤器中指定  <intent-filter> 和 <meta-data> 节点对.   <meta-data> 节点对应着一个外部XML 资源文件,这个资源文件定义了你想检测的设备的一些唯一标识信息.
在XML资源文件中,通过声明节点 <usb-device>  来过滤你想要过滤的设备. 以下列表列出了 <usb-device> 属性. 总的来说, 使用 vendor-id 和 product-id 来过滤指定的USB设备,使用 class, subclass 和 protocol 来过滤一组设备, 比如说大容量存储设备和数码相机. 你可以指定所有的属性或者一个都不指定.如果你什么属性都不设置,就将匹配所有的USB设备,当程序需要的时候,你可以这么指定:
  • vendor-id
  • product-id
  • class
  • subclass
  • protocol (device or interface)

该资源文件保存在 res/xml/ 目录下. 它的文件名(不包含.xml后缀名) 必须和<meta-data> 节点指定的名字一样. 文件的格式样式如下example:

Manifest文件 和资源文件

下面是一个简单的manifest文件及其对应的资源文件:
[html]  view plain copy
  1. <manifest ...>  
  2.     <uses-feature android:name="android.hardware.usb.host" /> <uses-sdk android:minSdkVersion="12" />  
  3.     ...  
  4.     <application>  
  5.         <activity ...>  
  6.                 ...  
  7.         <intent-filter>  
  8.             <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />  
  9.         </intent-filter>  
  10.         <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" />  
  11.         </activity>  
  12.     </application>  
  13. </manifest>  


在上面的manifest文件下,对应的资源文件必须保存为 res/xml/device_filter.xml,并且使用特定的属性来过滤任何USB设备:
[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" />  
  4. </resources>  

使用USB设备

当用户将一个USB设备连接到Android设备, Android系统可以决定你的程序是否有兴趣连接这个USB设备. 若确实有, 你的程序可以和设备如愿的进行交互. 为了让系统知道我们的应用程序确实有兴趣, 你必须做到:

  1. 你可以通过意图过滤器(Intent Filter)使得当用户插入USB设备时,程序能够接受到USB设备挂载通知,或者列举出已经连接的USB设备,这样,程序就能够找到那些已连接的USB设备.
  2. 若程序还没有获得连接USB设备的权限,跟用户请求.
  3. 使用适当的接口端点进行USB数据的读写.

寻找USB设备

你的应用可以使用意图过滤器(Intent Filter)使得程序可以接收到用户USB挂载通知或者列举出那些已连接的USB设备. 如果你希望你的应用能够自动的探寻的目标设备,使用一个意图过滤器是很有用的. 如果你希望获得一个所有的已连接的设备的列表或者你的应用并没有使用意图来过滤,那么列举出所有已连接的USB设备这个方式很有用.

使用意图过滤器(Intent Filter)

为了使得你的应用能够发现某个USB设备, 你可以指定一个android.hardware.usb.action.USB_DEVICE_ATTACHED 意图来过滤设备. 指定了这个意图之后, 你需要为它指定一个包含了某个USB设备的特定属性(比如说 product-id 和 vendor-id)的资源文件. 当用户插入一个与意图过滤器符合的设备时, 系统将弹出一个包含了这些信息的对话框,来询问用户是否能够启动你的应用 ,  如果用户接受了请求,你的应用将自动有权限去访问这个USB设备,直到USB设备断开连接.

下面的例子表明了如何声明意图过滤器:

[html]  view plain copy
  1. <activity ...>  
  2.     ...  
  3.     <intent-filter>  
  4.         <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />  
  5.     </intent-filter>  
  6.     <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" />  
  7. </activity>  
 

下面的例子表明了如何声明对应的指定了你所感兴趣的USB设备的资源文件:

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <usb-device vendor-id="1234" product-id="5678" />  
  4. </resources>  


在你的Activity 中, 你可以像下面这样获取 UsbDevice 对象,该对象代表了从意图(Intent)中检测到的设备:

[java]  view plain copy
  1. UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);   

 

列举USB设备

如果你的应用在运行时,想要获得所有的当前已经连接的USB设备, 可以枚举出所有挂在在IO上的设备. 使用方法 getDeviceList() 获取一个包含所有已挂载的USB设备的HashMap. 这个HashMap使用USB设备的名字作为键值,如果你想从这个HashMap获取设备.

[java]  view plain copy
  1. UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);  
  2. ...  
  3. HashMap<String, UsbDevice> deviceList = manager.getDeviceList();  
  4. UsbDevice device = deviceList.get("deviceName");  

 
如果你想, 你同样也可以从这个HashMap种获取一个迭代器(iterator)来一个接一个的处理每一个设备对象 :
[java]  view plain copy
  1. UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);  
  2. ...  
  3. HashMap<String, UsbDevice> deviceList = manager.getDeviceList();  
  4. Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();  
  5. while(deviceIterator.hasNext()){  
  6.     UsbDevice device = deviceIterator.next()  
  7.     //your code  
  8. }  

 

获取与设备交互的权限

在和USB设备交互的时候,应用程序必须从用户那获得交互的权限。
 

注意: 当USB设备连接的时候,如果你的应用使用意图过滤器来发现设备, 并且在应用安装的时候,用户允许你的应用处理这些事件,则应用将自动获得权限,否则, 在连接设备之前,你的应用必须明确的跟用户请求权限.

在一些情况下,明确的请求权限是必须的,比如当你的应用试图枚举已连接的USB设备并期望和其中一个交互的时候. 在应用于设备交互之前,你必须检查应用是否具有访问设备的权限. 否则的话,如果用户拒绝给予应用访问设备的权限,你将收到一个运行时错误.

为了明确获得权限,首先创建一个广播接收者(Broadcast Receiver). 当你调用方法requestPermission()的时候, 这个接收者能够得到广播, 从而监听到你的意图. 调用方法 requestPermission() 将为用户弹出对话框,以询问是否允许应用连接设备. 以下的简单的例子表明了如何创建这个广播接收者:

[java]  view plain copy
  1. private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";  
  2. private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {  
  3.     public void onReceive(Context context, Intent intent) {  
  4.         String action = intent.getAction();  
  5.     if (ACTION_USB_PERMISSION.equals(action)){  
  6.         synchronized (this) {  
  7.             UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);  
  8.         if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)){  
  9.             if(device != null){  
  10.                 //call method to set up device communication  
  11.             }  
  12.         }else{  
  13.             Log.d(TAG, "permission denied for device " + device);  
  14.         }  
  15.         }  
  16.     }  
  17.     }  
  18. };  

 

为了注册这个广播接收者,你需要在Activity的 onCreate() 中添加如下代码:

[java]  view plain copy
  1. UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);  
  2. private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";  
  3. ...  
  4. mPermissionIntent = PendingIntent.getBroadcast(this0new Intent(ACTION_USB_PERMISSION), 0);  
  5. IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);registerReceiver(mUsbReceiver, filter);  


为了显示对话框以询问用户是否给予应用连接设备的权限,可以调用方法requestPermission():

[java]  view plain copy
  1. UsbDevice device;  
  2. ...  
  3. mUsbManager.requestPermission(device, mPermissionIntent);  

 
 

当用户回应对话框,你的广播接收者将接收到包含EXTRA_PERMISSION_GRANTED 的额外数据的意图, 这些额外的数据是一个代表着用户的回应的boolean值. 只有检测到这个额外的数据为true, 才有权限连接设备.

 

与设备交互

与设备交互,可以是同步的,也可以是异步的. 不管哪种情况, 你都需要创建一个新的线程,用于所有的数据传输,以致不阻塞UI主线程. 为了正确的建立与设备的交互, 你需要获得对应的UsbInterface 和 设备的 UsbEndpoint ,这个UsbEndpoint是你使用UsbDeviceConnection进行交互和发送请求的基本. 总的来说, 你的代码应该像这样:

  • 检测遍历 UsbDevice 对象的属性, 比如 product-id, vendor-id, 或者是决定你是否想要和设备交互的设备类class节点.
  • 当你确定你的应用需要和设备进行交互的时候, 找出合适的UsbInterface 以及用于和这个接口交互的合适的UsbEndpoint . 接口可以含有一个或者多个端点, 而通常情况下,都含有一个输入输入和输出的交互端点对.
  • 当找到正确的端点的时候,在这个端点上打开连接UsbDeviceConnection .
  • 在这个端点上,使用方法bulkTransfer() 或者controlTransfer() 来提供传输的数据. 你应该在另一个线程中实现这一步,从而避免阻塞UI主线程. 关于如何在Android中使用线程,请查看Processes and Threads.

下面的一小段代码,是一种简单的同步的数据传输方式. 在实际中,你的代码应该包含更多的逻辑来正确的获取到正确的交互接口和端点,而且应该在不同的线程中做数据的传输操作,而不是在UI主线程中:

[java]  view plain copy
  1. <pre name="code" class="java">private Byte[] bytesprivate static int TIMEOUT = 0;  
  2. private boolean forceClaim = true;  
  3. ...  
  4. UsbInterface intf = device.getInterface(0);  
  5. UsbEndpoint endpoint = intf.getEndpoint(0);  
  6. UsbDeviceConnection connection = mUsbManager.openDevice(device);  
  7.   
  8. connection.claimInterface(intf, forceClaim);  
  9. connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT);   
  10. //do in another thread</pre>  
  11. <pre></pre>  
  12. <pre></pre>  
  13. <pre></pre>  
 

为了异步的发送数据, 我们使用类UsbRequest 来初始化initialize 和序列化 queue 一个异步的请求, 然后调用方法requestWait()等待请求.

更多信息请看AdbTest sample, 例中展示了如何进行大容量的异步传输, 还有例子MissleLauncher sample, 这个例子展示了如何异步的监听一个可阻断的端点。

 

中断与USB设备的交互

当完成了与设备的交互、设备断开、关闭接口UsbInterface或者连接UsbDeviceConnection调用了方法releaseInterface()close(). 为了监听设备断开事件,我们创建一个像下面这样创建一个广播接收者:

[java]  view plain copy
  1. BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {  
  2.     public void onReceive(Context context, Intent intent) {  
  3.         String action = intent.getAction();  
  4.         if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {  
  5.             UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);  
  6.             if (device != null) {  
  7.                 // call your method that cleans up and closes communication with the device  
  8.             }  
  9.         }  
  10.     }  
  11. };  

 
在应用中创建,而不是在manifest文件中创建广播接收者,可以使你的应用只在应用运行的时候才处理连接断开事件. 这种方法,断开事件只被发送到当前运行的应用,而不是发送给所有的应用. 
发布了40 篇原创文章 · 获赞 17 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/rendawei636/article/details/40828457