Android USB 主机(Host)和配件(Accessory)

来源:互联网 发布:2017平码固定算法公式 编辑:程序博客网 时间:2024/04/30 04:22

转载请注明来源: http://blog.csdn.net/kjunchen/article/details/52244617

Android USB 主机(Host)和配件(Accessory)

原文地址: https://developer.android.com/guide/topics/connectivity/usb/host.html

《英语水平有限,翻译必定有误,方便自己阅读,读者小心观看》

Android 支持多种 USB 外设和 Android USB 配件(硬件实现 Android 配件协议),通过两种模式: USB 配件和 USB 主机。在 USB 配件模式中,外部 USB 硬件担当 USB 主机。在 USB 主机模式中,Android 设备担当主机。这样的设备包括数码相机、键盘、鼠标和游戏控制器等。

USB 配件和主机模式在 Android 3.1(API 等级12) 或者更高的版本中支持。

注意: 对于支持 USB 主机和配件模式最终依赖于设备的硬件配置,不管平台的API等级是多少。你可以设置 元素过滤设备是否支持 USB 主机和配件的使用。

USB 主机

当你的 Android 设备在主机模式中,它担当 USB 主机,提供电源,罗列连接的 USB 设备。 USB 主机模式在 Android 3.1 或更高的版本中支持。

API 概述

在开发之前,去理解开发中将要用到的类是很重要的。下表描述了在 android.hardware.usb 包中的 USB 主机的 API。

类 描述 UsbManager 允许你罗列已连接的 USB 设备并与其通信 UsbDevice 代表一个已连接的 USB 设备,且包含访问它的识别信息、 interface 和 endpoint 的方法 UsbInterface 代表一个 USB 设备的 interface,设备功能的一个集合。一个连接的设备有一个或多个 interface UsbEndpoint 代表一个 interface 的 endpoint ,它是这个 interface 的通信的信道。一个 interface 可以有一个或多个 endpoint 。通常在双向通信设备中有输入和输出的 endpoint UsbDeviceConnection 代表设备的一个连接,在 endpoint 上传输数据。这个类允许你发送和接收同步或异步返回的数据 UsbRequest 代表通过 UsbDeviceConnection 和一个设备通信的异步请求 UsbConstants 定义的 USB 常量和 Linux 内核中 linux/usb/ch9.h 文件中定义的一致

在大多数情况下,和一个 USB 通信时你需要使用以上这些类。通常,你得到一个 UsbManager 去获取一个期望的 UsbDevice 。你需要去寻找合适的 UsbInterface 以及通信接口的 UsbEndpoint 。一旦你获得正确的endpoint,就可以打开一个 UsbDeviceConnection 去和 USB 设备通信。

Android Manifest 约束

下面的描述是你在使用 USB 主机 API 开发时需要在你的应用的 Manifest 文件中添加的内容:

  • 因为不是所有的 Android 设备都保证支持 USB 主机 API ,在 元素中声明你的应用使用 android.hardware.usb.host 特性。

  • 设置应用的最小 SDK 的 API 等级为 12 或者更高。 USB 主机 API 在之前的版本中没有提出。

  • 如果你想要你的应用在连接上 USB 设备时进行提示,在你的主 activity 中的 和 元素对中添加 android.hardware.usb.action.USB_DEVICE_ATTACHED 意图。 元素指向一个外部 XML 资源文件,它声明了你想要监测的设备的识别信息。

    在 XML 资源文件中,在 元素中声明你想要过滤的 USB 设备。下面列表描述了 的属性。通常,如果你想要过滤一个指定的设备可以使用供应商和产品编号,如果你想要过滤一类 USB 设备,如大量的存储设备或者数码相机,可以使用类、子类和协议。你可以不指定或者指定所有的属性。没有指定这些属性会匹配每一个 USB 设备,如果你的应用有需要只要指定这些:

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

    在 res/xml 目录中保存这个资源文件。资源文件的名字必须和你在 元素中只指定的一样。

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 ,指定要过滤设备的属性:

<?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 设备时,Android 系统可以判定你的应用是否对连接的设备感兴趣。如果是这样,你可以和期望的设备建立通信。要这样做,你的应用必须:

  1. 去发现已连接的 USB 设备,可以使用一个 intent 过滤器在用户连接到一个 USB设备时进行通知,也可以通过枚举已连接的 USB 设备。

  2. 对已连接的 USB 设备请求用户权限,是否已获得。

  3. 与 USB 设备通信,在合适的 interface 上的 endpoint 的进行数据读写。

发现设备

你的应用可以使用 intent 过滤器在用户连接上一个设备时进行通知或者枚举已连接的 USB 设备去发现 USB设备。如果你想要你的应用自动去检测期望的设备,那么使用 intent 过滤器是非常有用的。如果你想要获得已连接的设备列表或者如果你的应用没有 intent 过滤器,则枚举已连接的 USB 设备是游泳的。

使用 intent 过滤器

想要你的应用发现一个指定的 USB 设备,你可以指定一个 intent 过滤器去进行过滤,添加 android.hardware.usb.action.USB_DEVICE_ATTACHED 。除了这个 intent 过滤器,你需要指定一个资源文件,这个文件指定 USB 设备设备的属性,如产品和供应商的ID。当用户连接一个设备匹配你的设备过滤器时,系统会出现一个提示框询问是否启动你的应用。如果用户接受,你的应用自动拥有访问设备的权限直到设备断开连接。

下面示例展示如何声明 intent 过滤器:

<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 ,它代表从 intent 过滤器连接的设备,像这样:

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

枚举设备

在你的应用运行期间,如果你的应用对当前所有连接的 USB 设备感兴趣,它可以枚举设备。使用 getDeviceList() 方法获得一个所有已连接的 USB 设备的散列集合。这个集合的键是 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 设备进行通信之前,你的应用必须要有来自用户的同意。

注意:如果你的应用使用 intent 过滤器发现设备作为他们的连接,如果用户允许你的应用去处理 intent 他会自动接受许可。如果没有,在连接设备前你必须在你的应用里明确地请求许可。

明确请求许可在某些情况里是必需的,如当你的应用枚举已经连接的 USB 设备,然后想要与其中一个进行通信时。你必须在尝试与其通信前检查访问设备设备的许可。如果没有得到许可,如果用户拒绝访问设备的许可你将会接收到一个运行时错误。

为了明确地获取许可,首先创建一个广播接收器。当你调用 requestPermission() 方式时这个接收器监听获得的广播的意图。调用 requestPermission() 方法显示一个对话框去询问用户是否同意连接设备。下面的示例代码展示了如何创建广播接收器:

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

显示对话框询问用户是否同意连接到设备,调用 requestPermisson() 方法:

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

当用户对窗口做出响应后,你的广播接收器会接收到 intent 且包含 EXTRA_PERMISSION_GRANTED 的附加值,一个表示回复的布尔型的值。连接设备前检查这个值是否为 true 。

和一个设备通信

和一个 USB 设备通信可以是同步的也可以是异步的。无论那种情况,你都应该创建一个新的线程去进行所有的数据传输,这样就不会阻塞 UI 线程。要正确的和设备建立通信,你需要获得你想要进行通信的设备的合适的 USBInterface 和 UsbEndpoint ,然后在这个 endpoint 上用一个 UsbDeviceConnection 发送请求。通常,你的编码应该这样:

  • 检查 UsbDevice 对象的属性,如产品 ID ,供应商 ID ,或者设备类,关于是否想要进行通信的设备。

  • 当你确定想要和一个设备通信时,找到合适的 UsbInterface ,并且在该 interface 中找到合适的 UsbEndpoint 。 interface 可以有一个或多个 endpoint ,通常,在双向通信中会有一个输入和出书的 endpoint 。

  • 当你找到正确的 endpoint ,在这个 endpoint 上打开 一个 UsbDeviceConnection 。

  • 提供你想要在 endpoint 上用 bulkTransfer() 或者 controlTransfer() 方法传输的数据。你应该在另一个线程中执行这个步骤,以避免阻塞 UI 主线程。

下面的代码片段是做一个同步数据传输的一般方式。你的代码应该有更多的逻辑去寻找适合通信的 interface 和 endpoint ,而且你做任何数据传输应该在其他的线程中执行而不是 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 类去初始化和排队等候一个异步请求,然后在 requestWait() 中等待结果。

结束与设备的通信

当你完成和设备的通信时或者设备已经断开,应该调用 releaseInterface() 和 close() 方法去关闭 USsbInterface 和 UsbDeviceConnection 。要监听分离事件,应该创建如下的广播接收器:

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 中,仅仅允许你的应用在运行的时候去处理分离事件。这种方式,分离事件仅在应用运行的时候发送而不是对所有的应用广播。

欢迎加QQ群交流: 365532949
Homepage: http://junkchen.com

0 0
原创粉丝点击