安卓USB开发教程 <二> USB Host

来源:互联网 发布:软件著作权年限 编辑:程序博客网 时间:2024/05/03 19:20

USB Host(主机模式)

当 Android 设备处于 USB 主机模式时,它充当 USB 主机,为总线供电,并枚举连接的 USB 设备。Android 3.1 及更高版本支持 USB 主机模式。

API 概述

在开始之前,理解需要使用的类是很重要的。下表描述了 android.hardware.usb 包里的 USB 主机 API 函数。

Table 1. USB Host APIs

ClassDescriptionUsbManager负责枚举和与连接的 USB 设备通讯UsbDevice表示连接的 USB 设备并且包含访问其标识信息,接口和端点的方法UsbInterface表示 USB 设备的接口,它定义了设备的一组功能。 设备可以具有一个或多个接口进行通信。
UsbEndpoint表示接口端点,它是该接口的通信通道。 接口可以有一个或多个端点,并且通常具有与设备进行双向通信的输入和输出端点。
UsbDeviceConnection表示与设备的连接,该设备在端点上传输数据。 该类允许您以同步方式或异步方式来回发送数据。
UsbRequest表示通过UsbDeviceConnection与设备通信的异步请求。
UsbConstants定义与Linux内核的linux / usb / ch9.h中的定义对应的USB常量。

在大多数情况下,与 USB 设备通讯时需要使用所有这些类(UsbRequest 只在异步方式通讯的时候需要)。通常,会获取一个 UsbManager 来检索所需的 UsbDevice。当获取到设备时,需要查找合适的 UsbInterface 与接口中用于通讯的UsbEndpoint。一旦获取到正确的端点,打开一个 UsbDeviceConnection 与 USB 设备通讯。

Android Manifest 要求

下面的清单描述了在使用 USB host API 函数前需要添加到应用清单文件的内容:

1. 由于并非所有安卓设备被授权支持 USB host API 函数,因此需要包含一个 <uses-feature> 元素来声明你的应用使用 android.hardware.usb.host功能

2. 设置应用的最低 SDK 版本为 API 级别 12 或更高。USB host API 函数在更早的 API 级别中不存在。

3. 如果你希望应用收到 USB 设备插入的通知,请在主活动中为 android.hardware.usb.action.USB_DEVICE_ATTACHED 意图指定<intent-filter>  <meta-data> 元素对。<meta-data> 元素指向一个外部 XML 资源文件,它声明了要检测的设备的信息。

4. 在 XML 资源文件中,为你想过滤的 USB 设备声明 <usb-device>元素。下表描述了 <usb-device> 的属性。

通常,如果要过滤特定的设备使用厂商和产品 ID,如果要过滤一组 USB 设备使用 USB 类、子类和协议,如大容量存储类和数码相机。

你可以指定这些属性中的一个或者全部,不指定属性匹配任何 USB 设备,因此在应用需要时才进行指定:

vendor-id

product-id

class

subclass

protocol (device or interface)

将资源文件保存在 res/xml/ 目录中。资源文件名(不含 .xml 扩展名)必须与您在 <meta-data> 元素中指定的文件名相同。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 中,并指定具有指定属性的任何 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 设备时,Android 系统可以确定应用程序是否对连接的设备感兴趣。如果是这样,您可以根据需要建立与设备的通信。为此,您的应用程序必须:

1.通过使用 intent filter 在用户连接设备时收到通知或通过枚举已连接的 USB 设备来发现 USB 设备

2. 请求用户连接 USB 设备的权限,如果尚未获得。

3. 通过在相应的接口端点上读写数据与 USB 设备进行通信。

发现设备

应用程序可以通过使用intent filter 在用户连接设备时收到通知或通过枚举已连接的 USB 设备来发现 USB 设备。如果您希望能够让应用程序自动检测到所需的设备,则使用 intent filter 非常有用。 如果要获取所有连接的设备列表,或者您的应用程序没有为 intent 进行过滤,则枚举已连接的 USB 设备的方法非常有用。

使用 intent filter(意图过滤器)

要使您的应用程序发现一个特定的 USB 设备,可以指定一个 intent filter 来过滤android.hardware.usb.action.USB_DEVICE_ATTACHED intent。 除了此 intent filter,您还需要指定一个资源文件,该资源文件指定USB设备的属性,如产品和供应商ID。 当用户连接与 device filter 匹配的设备时,系统会向他们显示一个对话框,询问他们是否要启动应用程序。 如果用户接受,应用程序自动获得访问设备权限,直到设备断开连接。

以下示例展示如何声明 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>

在你的活动中,你可以像这样从 intent 中获取表示连接设备的 UsbDevice :

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

如果需要,还可以从哈希表中获取 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设备进行通信之前,应用程序必须获得用户的许可。

Note:如果应用程序使用 intent filter 来发现连接时的 USB 设备,则如果用户允许您的应用程序处理 intent,则它将自动接收权限。如果没有,您必须在连接到设备之前在应用程序中明确请求权限。

在某些情况下,显式请求许可可能是必需的,例如当您的应用程序枚举已连接的USB设备,然后要与其进行通信时。在尝试与之通信之前,您必须检查访问设备的权限。如果没有,用户拒绝访问设备的权限时,您将收到 runtime 错误。

要明确获得许可,首先创建一个广播接收器。该接收器侦听当您调用 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);                }            }        }    }};

要注册广播接收器,在活动的 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 的 intent,这是表示答案的布尔值。 在连接设备之前,请检查这个额外值是否为 true。

与设备通信

与USB设备的通信可以是同步或异步的。在任一情况下,您应该创建一个新线程来执行所有数据传输,才不会阻塞UI线程。要正确建立与设备的通信,您需要获得要进行通信的设备的相应的UsbInterface UsbEndpoint,并使用UsbDeviceConnection 在此端点上发送请求。一般来说,您的代码应该:

1. 检查UsbDevice 对象的属性,如产品 ID,供应商 ID或设备类,以确定是否要与设备进行通信;

2. 当您确定要与设备通信时,请找到与合适的UsbEndpoint 进行通信的相应UsbInterface。接口可以具有一个或多个端点,并且通常会具有用于双向通信的输入和输出端点;

3. 找到正确的端点时,在该端点上打开一个UsbDeviceConnection

4. 使用bulkTransfer() 或controlTransfer() 方法提供在端点上传输的数据。您应该在另一个线程中执行此步骤,以防止阻塞主 UI 线程。有关在 Android 中使用线程的更多信息,请参阅 Processes and Threads

以下代码片段是进行同步数据传输的简单方法。您的代码应该有更多的逻辑来正确找到正确的接口和端点进行通信,并且还应该在与主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类来 initialize 和 queue 一个异步请求,然后调用 requestWait() 等待结果。

终止与设备通信

当你与设备通信完成或者设备拔出时,调用 releaseInterface()  close() 方法关闭 UsbInterface  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 中创建广播接收器允许应用在运行时只处理拔除事件。通过这种方式,广播事件只会发送到当前正在运行的应用程序而不是广播到所有应用。

原文链接:https://developer.android.com/guide/topics/connectivity/usb/host.html





原创粉丝点击