翻译AndroidUSBHOSTAPI

源码地址:http://developer.android.com/guide/topics/connectivity/usb/host.html

译者注:翻译的好不好不是太重要,重点是在翻译的过程中会把每句话都看认真看一遍,或者说是抱着翻译的思想来完成一个读懂的目的。

USBHost通信

当你的可供电Android设备处理USBhost模式时,它担任着为USB总线供电,枚举连接的USB从设备等等一个主设备应用的工作。Android3.1及以后版本开始支持USBhost模式。

API概述

开始之前,有必要弄明白以后要用到的类。下表中描述了包含在android.hardware.usb包中的USBhostAPIs

1.USBhostAPIs.

Class

Description

UsbManger

允许你枚举USB从设备以及和其通信

UsbDevice

代表一个USB从设备,获取其信息。如接口,端点等等。

UsbInterface

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

UsbEndpoint

代表接口的一个端点,它是该端点一个通信通道。一个接口可以拥有一个或者多个端点,通常输入输出端点用于和设备进行双向通信。

UsbDeviceConnection

代表一个设备的连接,通过端点传输数据,该类允许你发送接收数据,通信方式可以是同步或者异步。

UsbRequest

代表通过UsbDeviceConnection通信的异步请求。

UsbConstants

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

多数情况下,在和USB设备进行通信的时候,你需要使用这里所有的类(UsbRequest只用在异步通信一个请求)。一般是这样的,使用UsbManager来获取UsbDevice。确定了设备,需要查找用于通信接口的UsbInterfaceUsbEndpoint。一旦获取了恰当的端点,打开UsbDeviceConnectionUSB设备进行通信。

下列表中描述了使用USBhostAPIs之前需要添加到应用程序的manifest文件中的内容:

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

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

如果你想让你的应用检测一个USB设备的话,在主Activity指定<intent-filter>和<meta-data>对象去匹配android.hardware.usb.action.USB_DEVICE_ATTACHED。<meta-data>对象指向一个外部的XML资源文件,文件中描述你想要探测的USB设备的过滤信息。

在这个XML资源文件中,为你想要过滤的USB设备声明<usb-device>对象。下列表中描述了<usb-device>的属性。通常,使用vendorproductID过滤特殊设备,使用类,子类和协议来过滤一组设备,比如大容量存储设备(优盘)或者数码相机。你可以完全指定这个属性值,也可以一个也不指定。没有属性值说明会过滤每个一个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设备,比如productandvendorID.当用户连接上和你探测的设备匹配时,系统会弹出一个对话框询问用户是否允许启动的你的应用程序。如果允许,你的应用程序将会自动被赋予权限和该设备通信,直到设备被移除。

以下示例显示如何描述意图过滤器:

<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设备的名字为关键字进行索引。

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

添加如下代码到activityonCreate()方法中注册广播接收器:

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对象的属性,比如productID,vendorID或者设备类别。以识别是不是需要通信的设备。
  • 如果确定了是要通信的设备,检索出你需要通信设备的接口,以及该接口的端点。接口可以有一个或者多个端点,一般会有输入输出各一的双向通信的端点。
  • 如果确定了端点,在该端点上打开UsbDeviceConnection
  • 使用bulkTransfer()或者controlTransfer()方法将需要传输的数据提供到端点上。你需要将上述操作实现在其它线程以免阻塞当前主界面线程。更多关于Android中线程的使用,见ProcessesandThreads.

下面的代码片段简单的实现了同步数据传输。你的代码应该使用更多逻辑上的来查找出正确的通信接口和端点,同样需要在主界面以外的线程中进行数据的传输:

private Byte[] bytesprivate 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例子,它显示了如何进行块传输的方法异步通信,以及MissleLaunchersample它显示了如何监听中断端点异步。

终止通信

当完成了通信或者设备被移除了,调用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大小进行传输。
  • 文件中提到的那个例子AdbTestMissleLauncher的源码位于:development/samples/USB/中。前者功能是读取usb设备(同样也是Android系统)LOG,后者功能是控制一个火箭发射器玩具。
  • Android这套API并不能检测所有的USB设备,有些设备检测不到。比如无线数据的适配器。目前测试成功的有打印机。
  • 关于对其了解的程度,我一直有个毛病就是了解一个东西没有上限。类似写文章没有主线,想到哪里就深入到哪里。我要逐渐改点这个习惯。我对这个的练习是将数据发送打印机,完成打印。当我刚刚实现的时候,我就又开始幻想我是不是可以做一个很牛叉的USB打印APP了。这个是可以实现的,但并不是目前的主要任务,如果我坚持做,很多东西都需要去实现,这也违背了我当前仅仅是了解AndroidUSBhostAPI的初衷。还好,这次我刹住了闸,毕竟我这次是真实的了解了,之前只知道在Linux内核中的通信以及基于Libusb通信,直接使用java通信的好处是解析xml等等在C语言中相对复杂的东西到java中都不显示复杂了。
  • l测试APK和上文提到的那两个例子打包上传到Here,方便那些没有Android系统源码的同志。

更多相关文章

  1. Android应用程序组件概述
  2. [置顶] 【电子书下载】《Android应用程序开发与典型案例》完整版
  3. android设备唯一码的获取之二
  4. Android设备Root检测方法
  5. HwBinder原理总结-Android10.0 HwBinder通信原理(十一)
  6. Android应用程序与SurfaceFlinger服务的关系概述
  7. Android修改系统时间(应用程序获得系统权限)

随机推荐

  1. new TextView
  2. Android(安卓)SDK 版本对应版本号
  3. Android关于图片和Base64转码的工具类
  4. android将视频文件转化为图片
  5. Android自定义viewpager且高度自适应
  6. android 强制关闭软键盘
  7. Android(安卓)日期对话框 DatePickerDial
  8. Android(安卓)Tools Project Site
  9. 【Content Provider】一个完整的ContentP
  10. Android(安卓)控件之进度加载