USB Host

Wesley13
• 阅读 603

当你的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

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
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:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这