USB Host

Wesley13
• 阅读 395

当你的Android设备处于USB host 模式时,它扮演USB host的角色,驱动总线,并枚举连接的USB设备。在Android 3.1及更高版本中支持USB host模式。

API 总览


在你开始之前,理解你将要使用的类是很重要的。下表描述了在android.hardware.usb包中的USB host APIs。

Table 1. USB Host APIs

Class

Description

UsbManager

允许你枚举连接的USB设备并与之通信。

UsbDevice

表示一个连接的USB设备,并包含访问它的标识信息,接口和endpoints的方法。

UsbInterface

表示一个USB设备的接口,其为设备定义了一系列的功能。一个设备可以包含一个或多个能够通信的接口。

UsbEndpoint

表示一个接口endpoint,它是这个接口的一个通信通道。一个接口具有一个或多个endpoints,但对于与设备的双向通信通常具有输入和输出endpoints。

UsbDeviceConnection

表示一个与设备的连接,其在endpoints生传输数据。这个类允许同步或异步地向前和向后发送数据。

UsbRequest

表示通过一个UsbDeviceConnection与一个设备通信的一个异步请求。

UsbConstants

定义了对应于Linux kernel的linux/usb/ch9.h中所定义的USB常量。

在大多数情况下,当你与一个USB设备通信时,你需要使用所有的这些类(UsbRequest只有在你执行异步通信时才需要)。通常,你获取一个UsbManager来提取一个想要的UsbDevice。当你有了device,你需要找到与之通信的接口的适当的UsbInterfaceUsbEndpoint。一旦你获取了正确的endpoint,则打开一个UsbDeviceConnection来与USB设备通信。

Android Manifest Requirements


下面的列表描述了在使用USB host APIs之前,你需要添加到你应用程序的manifest文件的内容:

  • 不是所有的android设备都保证支持USB host APIs,则包含一个元素来声明你的应用程序使用了android.hardware.usb.host feature。

  • 设置应用程序的minimum SDK为API Level 12或更高。在更早的API levels上不存在USB host APIs。

  • 如果你想要你的应用程序在USB 设备连接时得到通知,则在你的main activity中为android.hardware.usb.action.USB_DEVICE_ATTACHED intent指定一个元素对。元素指向一个外部XML resource文件,其声明了关于你想要探测的设备的标识信息。

    在XML资源文件中,为你想要过滤出的USB设备声明元素。下面的列表描述了的属性。通常,如果你想要过滤出一个特定的设备的话,使用vendor和product ID,如果你想要过滤出一个USB设备组时,如大容量存储设备或数码相机,则使用class,subclass和protocol。你可以一个也不指定,或指定所有的这些属性。不指定属性则匹配每个USB设备,因而只在你的应用需要时才这样做:

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

    把资源文件保存在res/xml/目录下。资源文件的名字(不包含.xml扩展名)必须与元素中指定的那个相同。XML资源文件的格式在下面的example中。

Manifest和资源文件示例

下面的例子展示了一个示例manifest和它对应的资源文件:

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

使用Devices


当用户把USB设备连接到了一个Android设备时,Android系统可以确定你的应用程序是否对连接的设备感兴趣。如果感兴趣,如果喜欢,则你可以设置与设备的通信。要做到这一点,你的应用程序不得不:

  1. 通过使用一个在用户连接了一个USB设备时被通知到的intent filter,或通过枚举已经连接的USB设备来发现连接的USB设备。
  2. 向用户请求连接USB设备的权限,如果还没有获取的话。
  3. 通过在适当的接口endpoints上读取和写入数据,来与USB设备通信。

发现一个设备

你的设备可以通过使用一个在用户连接一个设备时会被通知到的intent filter,或通过枚举已经连接的USB设备来发现USB设备。如果你想要使你的应用程序能够自动地探测一个想要的设备,则使用一个intent filter很有用。如果你想要获取一个所有已连接设备的列表,或如果你的应用程序不过滤一个intent filter,枚举已连接的USB设备很有用。

使用一个intent filter

要使你的应用程序发现一个特定的USB设备,你可以为android.hardware.usb.action.USB_DEVICE_ATTACHED intent指定一个intent filter。伴随着这个intent filter,你需要指定一个资源文件,其描述了这个USB设备的属性,诸如product和vendor ID这些。当用户连接了一个与你的设备filter匹配的设备时,系统将会呈现给用户一个dialog,来向用户询问是否要启动你的应用程序。如果用户接受了,你的应用程序自动地具有访问设备的权限,直到设备断开连接。

下面的例子展示了如何声明intent filter:

<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中,你可以像这样从intent中获取表示附接的设备的UsbDevice

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

枚举设备

如果你的应用程序,对于它在运行时当前所连接的所有的USB设备感兴趣,它可以枚举总线上的设备。使用getDeviceList()方法来获取一个所有的已连接USB设备的hash map。如果你想要从hash map中获取一个设备的话,hash map的key是USB设备的名字。

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

如果需要,你也可以从hash map中获取一个iterator,然后一个接一个地处理每个设备。

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设备通信前,你的应用程序必须从用户那儿获取相应的权限。

注意:如果你的应用程序使用了一个intent filter来在USB设备连接时发现它们,则如果用户允许你的应用程序处理intent的话,它将自动地接受权限。如果不允许,你必须在你的应用程序中,在与设备连接前显式地请求权限。

在某些情况下,显式地请求权限可能是需要的,比如当你的应用程序枚举已经连接的USB设备并想要与其中的一个通信时。你必须在尝试与之通信前,检查访问一个设备的权限。如果没有,如果用户拒绝了访问设备的权限,你将接收一个runtime error。

要显式地获取权限,首先创建一个broadcast receiver。当你调用requestPermission()时,这个receiver监听被broadcast的intent。对于requestPermission()的调用显示一个dialog给用户,以请求连接设备的权限。下面的示例代码展示了如何创建这个broadcast receiver:

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

要注册broadcast receiver,则把这些添加进你的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);

要显示请求与设备连接授权的dialog,则调用requestPermission()方法:

UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);

当用户回复了dialog,你的broadcast receiver接收到intent,其中包含EXTRA_PERMISSION_GRANTED extra,它是一个boolean值,表示用户的回答。在连接到设备之前,检查这个extra的值为true。

与一个设备通信

与一个USB设备通信可能是同步的或异步的。在每中情况下,你都应该创建一个新的线程,来执行所有的数据传输,以便于不会block了UI thread。要适当地设置与一个设备的通信,你需要获取你想要与之通信的设备的适当的UsbInterfaceUsbEndpoint,并通过一个UsbDeviceConnection来在这个endpoint上发送请求。通常,你的代码应当:

  • 检查一个UsbDevice对象的属性,比如product ID,vendor ID,或者device class,来计算出是否想要与设备通信。
  • 当你确认了你想要与设备通信,则查找你想要用来进行通信的适当的UsbInterface及那个接口的适当的UsbEndpoint。接口可能具有一个或多个endpoints,对于双向通信则通常将具有一个input或output endpoint。
  • 当你找到了正确的endpoint时,则在那个endpoint上面打开一个UsbDeviceConnection
  • 通过bulkTransfer()controlTransfer()方法,来在endpoint上提供你想要传输的数据。你应该在另外一个线程中完成这一步,以防止阻塞了main UI thread。更多关于在Android下使用threads的信息,请参考Processes and Threads

下边的代码片段是一个简单的方法来执行一个同步的数据传输。你的代码应该具有更过的逻辑来正确地找到正确的于其上进行通信的interface和endpoints,并且也应该在一个不同的线程中执行数据的传送,而不是在main 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类来初始化入队一个异步的request,然后通过requestWait()等待结果。

更多信息,请参考AdbTest sample,其展示了如何执行异步的bulk传输,及MissleLauncher sample,其展示了如何在一个interrupt endpoint上异步地监听。

终止与一个设备的通信

当你完成了与一个设备的通信,或如果设备断开了,则通过调用releaseInterface()close()来关闭UsbInterfaceUsbDeviceConnection。要监听断开事件,则创建一个如下的broadcast receiver:

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

在应用程序内创建broadcast receiver,而不是在manifest中,以允许你的应用程序只在它运行时处理断开事件。这种方式,则断开事件只发送给当前运行的应用程序,而不是broadcast给所有的应用程序。

Done.

原文:http://developer.android.com/guide/topics/connectivity/usb/host.html

点赞
收藏
评论区
推荐文章
刚刚好 刚刚好
2个月前
css问题
1、 在IOS中图片不显示(给图片加了圆角或者img没有父级) <div<img src""/</div div {width: 20px; height: 20px; borderradius: 20px; overflow: h
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录 问题 用navicat导入数据时,报错: 原因这是因为当前的MySQL不支持datetime为0的情况。 解决修改sql\mode: sql\mode:SQL Mode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。 全局s
晴空闲云 晴空闲云
2个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。 盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
1个月前
快速入门|使用MemFire Cloud构建React Native应用程序
> MemFire Cloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Stella981 Stella981
1年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置 1、virsh edit centos7 找到“memory”和“vcpu”标签,将 <name>centos7</name> <uuid>2220a6d1-a36a-4fbb-8523-e078b3dfe795</uuid>
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序 select * from table_name order id desc; 2.按照指定(多个)字段排序 select * from table_name order id desc,status desc; 3.按照指定字段和规则排序 selec
Stella981 Stella981
1年前
Angular material mat
Icon Icon Name mat-icon code _add\_comment_ add comment icon <mat-icon> add\_comment</mat-icon> _attach\_file_ attach file icon <mat-icon> attach\_file</mat-icon> _attach\
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
#### 背景描述 # Time: 2019-01-24T00:08:14.705724+08:00 # User@Host: **[**] @ [**] Id: ** # Schema: sentrymeta Last_errno: 0 Killed: 0 # Query_time: 0.315758 Lock_
helloworld_34035044 helloworld_34035044
4个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。 uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid() 或 uuid(sep)参数说明:sep 布尔值,生成的uuid中是否包含分隔符'',缺省为
helloworld_28799839 helloworld_28799839
2个月前
常用知识整理
# Javascript ## 判断对象是否为空 ```js Object.keys(myObject).length === 0 ``` ## 经常使用的三元运算 > 我们经常遇到处理表格列状态字段如 `status` 的时候可以用到 ``` vue