Android USB HOST API

USB Host通讯html

当你的可供电Android设备处理USB host模式时,它担任着为USB总线供电,枚举链接的USB从设备等等一个主设备应用的工做。java

Android 3.1及之后版本号開始支持USB host模式。linux

 

 

API概述android

開始以前,有必要弄明确之后要用到的类。下表中描写叙述了包括在android.hardware.usb包中的USB host APIs。app

 

表1. USB host APIs.异步

Classthis

Descriptionspa

UsbManger线程

赞成你枚举USB从设备以及和其通讯code

UsbDevice

表明一个USB从设备,获取其信息。

如接口,端点等等。

 

UsbInterface

表明一个USB从设备的接口,一个设备可以拥有多个接口用于通讯。

 

UsbEndpoint

表明接口的一个端点。它是该端点一个通讯通道。

一个接口可以拥有一个或者多个端点,一般输入输出端点用于和设备进行双向通讯。

 

UsbDeviceConnection

表明一个设备的链接,经过端点数据传输,该类赞成你发送接收数据,通讯方式可以是同步或者异步。

UsbRequest

表明经过UsbDeviceConnection通讯的异步请求。

UsbConstants

定义USB常量相应Linux内核中linux/usb/ch9.h中的宏。

 

 

多数状况下。在和USB设备进行通讯的时候,你需要使用这里所有的类(UsbRequest仅仅用在异步通讯一个请求)。

一般是这种,使用UsbManager来获取UsbDevice。肯定了设备。需要查找用于通讯接口的UsbInterfaceUsbEndpoint。一旦获取了恰当的端点。打开UsbDeviceConnection与USB设备进行通讯。

 

下列表中描写叙述了使用USB host APIs以前需要加入到应用程序的manifest文件里的内容:

由于并不是所有的Android带供电设备确保支持USB host APIs,加入 <uses-feature>对象来代表你的应用使用了android.hardware.usb.host特性。

SDK最低版本号设置为12或者更高。USB host APIs不支持更老版本号的API。

假设你想让你的应用检測一个USB设备的话,在主Activity指定 <intent-filter> 和<meta-data> 对象去匹配android.hardware.usb.action.USB_DEVICE_ATTACHED。

<meta-data>对象指向一个外部的XML资源文件,文件里描写叙述你想要探測的USB设备的过滤信息。

 

在这个XML资源文件里,为你想要过滤的USB设备声明<usb-device>对象。

下列表中描写叙述了<usb-device>的属性。

一般,使用vendor 和 product ID过滤特殊设备。使用类。子类和协议来过滤一组设备。比方大容量存储设备(优盘)或者数码相机。你可以全然指定这个属性值。也可以一个也不指定。没有属性值说明会过滤每个一个USB设备。因此说。你的应用程序有这个需求的状况下可以指定。

 

 

 

  • vendor-id
  • product-id
  • class
  • subclass
  • protocol (device or interface)

 

保存资源文件到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设备链接到Android上时,无论你的应用程序是否对此USB设备感兴趣Android系统都会检測到。

你可以和设备创建通讯。你的应用程序需要作例如如下事情:

1.探測USB设备经过意图过滤器检測用户链接的设备。或者枚举已经链接的USB设备。

 

2.假设以前没有请求过,向用户请求权限以链接USB设备。

3.和USB设备进行通讯。经过接口端点读写数据。

 

探測设备

 

应用程序可以经过两种方式进行USB设备的探測,使用意图过滤器监听用户链接设备或者枚举已经链接的USB设备。

前者可以方便的实现本身主动检測设备。

假设想列出所有的已链接设备或者没有使用意图过滤器过滤的设备,可以使用枚举设备来完毕。

 

使用意图过滤器

 

探測设备可以指定意图过滤器来过滤android.hardware.usb.action.USB_DEVICE_ATTACHED意图。此外需要指定一个资源文件描写叙述USB设备,比方product and vendor ID. 当用户链接上和你探測的设备匹配时,系统会弹出一个对话框询问用户是否赞成启动的你的应用程序。假设赞成,你的应用程序将会本身主动被赋予权限和该设备通讯,直到设备被移除。

 

下面演示样例显示怎样描写叙述意图过滤器:

 

<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中。你要得到UsbDevice,它表明使用意图探測到的设备。如:

 

 

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

 

 

枚举设备

 

在你的应用程序执行时,假设对因此当前正在链接的USB设备感兴趣。可以枚举设备。枚举设备使用的是getDeviceList()方法获取所有链接设备的哈希表。假设你想获取一个设备可以依据USB设备的名字为keyword进行索引。

 

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ...   HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); UsbDevice device = deviceList.get("deviceName");

必要时,你从哈希表中迭代设备依次处理每个设备:

 

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设备,假设用户赞成处理意图,那么你的应用程序本身主动会获取权限。

反之,通讯以前你必须向用户请求权限。

在一些状况下请求权限是必须的,好比你的应用程序枚举已经链接的USB设备以及与设备通讯。通讯以前你需要检查权限。不然假设用户权限拒绝你将会收到一个执行错误。

 

申请权限。首先建立建立一个广播接收器。

调用requestPermission()时,接收器监听广播意图。调用requestRermission()向给用户显示一个对话框请求链接该设备的权限。

 

下列代码显示怎样建立这样一个广播接收器:

 

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_PERMISSION_GRANTED意图。它是一个布尔表明答案。通讯以前检查这个额外值是否为真。

 

与设备通讯

 

通讯方式可以是同步或者异步。

在这两种状况下,你应该建立一个线程运行所有数据的传输。不要堵塞界面线程。

创建一个通讯。你需要获取需要通讯设备的UsbInterfaceUsbEndpoint,以及使用UsbDeviceConnection申请端点。通常地,你的代码应该是:

 

  • 检查UsbDevice对象的属性,比方 product ID, vendor ID或者设备类别。以识别是否是需要通讯的设备。
  • 假设肯定了是要通讯的设备,检索出你需要通讯设备的接口。以及该接口的端点。接口可以有一个或者多个端点,一般会有输入输出各一的双向通讯的端点。
  • 假设肯定了端点。在该端点上打开 UsbDeviceConnection。 
  • 使用bulkTransfer()或者controlTransfer()方法将需要传输的数据提供到端点上。

     

    你需要将上述操做实现在其余线程以避免堵塞当前主界面线程。

    不少其余关于Android中线程的使用,见Processes and Threads.

 

 

如下的代码片断简单的实现了同步传输数据。

你的代码应该使用不少其它逻辑上的来查找出正确的通讯接口和端点,相同需要在主界面之外的线程中进行数据的传输:

 

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类初始化(initialize)和排队(queue)等候异步请求。而后使用requestWait()等待结果。

 

不少其它信息见AdbTest样例,它显示了怎样进行块传输的方法异步通讯。以及MissleLauncher sample它显示了怎样监听中断端点异步。

 

终止通讯

 

当完毕了通讯或者设备被移除了,调用releaseInterface()close()来关闭UsbInterfaceUsbDeviceConection。监听设备移除事件,建立一个广播接收器例如如下:

 

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             }         }     } };

 

建立广播接收器是在应用程序内部,而非manifest中。仅在应用程序程序执行时处理设备移除事件。

如此,移除事件仅仅会发送给正在执行的应用而不是所有的应用。

 

感悟:

 

  • 与原文无关纯属我的感想,USB通讯越向下越复杂。

     

    Android封装好一些,其次是libusb。再而后就是Linux内核中进行的USB通讯。依次需要了解的知识多少都不同,比方这个完整的通讯过程当中并没实用到urb等等。

     

  • 眼下測试的Android4.2.2上通讯的最大块为:16KiB。因此数据要切割为16KiB大小进行传输。
  • 文件里提到的那个样例AdbTest和MissleLauncher的源代码位于:development/samples/USB/中。前者功能是读取usb设备(相同也是Android系统)的LOG,后者功能是控制一个火箭发射器玩具。
  • Android这套API并不能检測所有的USB设备,有些设备检測不到。比方无线数据的适配器。眼下測试成功的有打印机。
  • 关于对其了解的程度,我一直有个毛病就是了解一个东西没有上限。相似写文章没有主线。想到哪里就深刻到哪里。我要逐渐改点这个习惯。

     

    我对这个的练习是将数据发送打印机。完毕打印。当我刚刚实现的时候,我就又開始幻想我是否是可以作一个很是牛叉的USB打印APP了。这个是可以实现的,但并不是眼下的主要任务,假设我坚持作。很是多东西都需要去实现。这也违背了我当前不过了解Android USB host API的初衷。还好,此次我刹住了闸。毕竟我此次是真实的了解了,以前只知道在Linux内核中的通讯以及基于Libusb通讯,直接使用java通讯的优势是解析xml等等在C语言中相对复杂的东西到java中都不显示复杂了。

     

  • l 測试APK和上文提到的那两个样例打包上传到Here,方便那些没有Android系统源代码的同志。