安卓USB开发教程 USB Accessory

USB Accessory(配件模式)

USB 配件模式允许用户连接专为 Android 设备设计的 USB 主机硬件。配件必须遵守 Android Accessory Development Kit 文档中列出的 Android 配件协议。 这使得 Android 设备无法充当 USB 主机时仍然可以与 USB 硬件交互。 当 Android 设备处于 USB 配件模式时,所连接的 Android USB 配件充当主机,为 USB 总线供电,并枚举所连接的设备。 Android 3.1(API 级别12)支持 USB 配件模式,该功能也被以附加组件库的方式回溯到 Android 2.3.4(API 级别10),以支持更广泛的设备。前提是厂商必须在系统镜像中添加附加组件库。

选择正确的 USB Accessory API 函数

尽管 USB accessory API 函数在 Android 3.1 引入到平台,通过使用 Google APIs 附加库的方式用在 Android 2.3.4 上。由于这些 APIs 使用外部库的方式回溯,有两个包可以导入来支持 USB 配件模式。根据你要支持的安卓设备,您可能要使用一个库而不是另一个:

com.android.future.usb: 为了在 Android 2.3.4 中支持 USB accessory 模式,Google APIs add-on library 包括回溯的 USB accessory  API,它们包含在此命名空间中。 Android 3.1 还支持在此命名空间中导入和调用类,以支持使用附加库编写的应用程序。 这个附加库是围绕 android.hardware.usb accessory API 的简版 wrapper,不支持USB主机模式。 如果您想支持最广泛的支持 USB accessory 模式的设备,请使用附加库并导入此软件包。 重要的是要注意,并非所有的 Android 2.3.4 设备都需要支持 USB 配件功能。 每个单独的设备制造商决定是否支持此功能,这就是为什么必须在 manifest 文件中声明它。
android.hardware.usb: 此命名空间包含在 Android 3.1 中支持 USB accessory 模式的类。 该软件包作为 framework API 的一部分,Android 3.1 支持 USB accessory 模式,而不需要附加库。 如果您只关心具有 USB accessory 模式硬件支持的 Android 3.1 或更新版本,您可以在清单文件中声明使用此软件包。

安装 Google APIs 附加库

如果要安装该附加库,可以通过使用 SDK Manager 安装 Google API Android API 10 软件包。有关安装附加库的更多信息,请参阅  Installing the Google APIs Add-on

API 概述

由于附加库是 framework API 的 wrapper,因此支持 USB accessory 功能的类很相似。 即使您正在使用附加库,也可以使用 android.hardware.usb 的参考文档。

Note:但是,您应该注意的附加库和 framework API 之间存在较小的用法差异。
下表描述支持 USB accessory API 的类:
Class Description
UsbManager 允许枚举以及与已连接 USB 配件通讯
UsbAccessory 表示USB 配件,包含访问其标识信息的方法

附加库和平台 APIs 的使用差别

在使用 Google APIs 和平台 APIs 之间有两处使用区别。
如果正在使用附加库,必须以下列方式获取 UsbManager

UsbManager manager = UsbManager.getInstance(this);

如果没在使用附加库,必须以下列方式获取 UsbManager

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);

当您使用 intent filter 过滤连接的配件时,UsbAccessory 对象包含在传递给应用程序的 intent 内。 如果正在使用附加库,则必须以下列方式获取 UsbAccessory 对象:

UsbAccessory accessory = UsbManager.getAccessory(intent);

如果没在使用附加库,必须以下列方式获取 UsbAccessory

UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

安卓 Manifest 要求

以下列表描述了在使用 USB accessory API 之前需要添加到应用程序的 manifest 文件中的内容。manifest and resource file examples 展示了如何声明这些项目:

1. 因为并非所有 Android 设备被授权支持 USB accessory API,因此包含一个 <uses-feature> 元素,声明您的应用程序使用 android.hardware.usb.accessory 功能。

2. 如果您正在使用附加库,请添加指定 com.android.future.usb.accessory 的 <uses-library> 元素。

扫描二维码关注公众号,回复: 10119559 查看本文章

3. 如果您正在使用附加库,请将应用程序的最小 SDK 设置为 API Level 10,如果使用的是android.hardware.usb 包,则将其设置为12。

4. 如果您希望应用程序在 USB 配件连接时收到通知,请在主要活动中为 android.hardware.usb.action.USB_ACCESSORY_ATTACHED 意图指定 <intent-filter><meta-data> 元素对。<meta-data> 元素指向一个外部XML资源文件,它声明了您想检测的配件的标识信息。

在XML资源文件中,为要过滤的配件声明 <usb-accessory> 元素。每个 <usb-accessory> 可以具有以下属性:

  • manufacturer
  • model
  • version
将资源文件保存在 res/xml/ 目录中。资源文件名(不含 .xml 扩展名)必须与您在 <meta-data> 元素中指定的文件名相同。 XML资源文件的格式也展示在下面的 example 中

Manifest 和 资源文件示例

以下示例展示了一个 manifest 样例及相应的资源文件:

<manifest ...>
    <uses-feature android:name="android.hardware.usb.accessory" />
    
    <uses-sdk android:minSdkVersion="<version>" />
    ...
    <application>
      <uses-library android:name="com.android.future.usb.accessory" />
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
            </intent-filter>

            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/accessory_filter" />
        </activity>
    </application>
</manifest>

在这个案例中,下面的资源文件应该保存在 res/xml/accessory_filter.xml 中,并且指定具有相关 model、manufacturer、version 的配件应该被过滤。配件将这些属性发送给 Android 设备:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
</resources>

使用配件

当用户将 USB 配件连接到 Android 设备时,Android 系统可以决定您的应用程序是否对连接的配件感兴趣。 如果是这样,如果需要,您可以设置与附件的通信。如果是这样,您可以根据需要建立与设备的通信。为此,您的应用程序必须:

1. 通过使用 intent filter 过滤配件连接事件来发现配件或通过枚举已连接的配件找到正确的配件。

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

3. 在正确的接口端点上读写数据与 USB 配件进行通信。

发现配件

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

使用 intent filter(意图过滤器)

要使您的应用程序发现一个特定的 USB peijia,可以指定一个 intent filter 来过滤 android.hardware.usb.action.USB_ACCESSORY_ATTACHED intent。 除了此 intent filter,您还需要指定一个资源文件,该资源文件指定 USB 配件的属性,如 manufacturer、model 和 version。 当用户连接与 accessory filter 匹配的配件时,

以下示例展示如何声明 intent filter:

<activity ...>
    ...
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
    </intent-filter>

    <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
        android:resource="@xml/accessory_filter" />
</activity>

以下示例展示如何声明相应资源文件,其指定了感兴趣的 USB 配件:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>

在你的活动中,你可以像这样从 intent 中获取表示连接附件的 UsbAccessory(使用附加库)

UsbAccessory accessory = UsbManager.getAccessory(intent);

或者像这样(使用平台 APIs):

UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

枚举配件

当应用程序运行时,可以让应用程序枚举已经标识自己的配件。

使用 getAccessoryList() 方法获取所有已连接 USB 配件的数组:

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAcccessoryList();

Note:同一时间只可以支持一个已连接配件。

获取与配件进行通信的权限

在与 USB 配件进行通信之前,应用程序必须获得用户的许可。

Note如果应用程序 uses an 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) {
                UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(accessory != null){
                        //call method to set up accessory communication
                    }
                }
                else {
                    Log.d(TAG, "permission denied for accessory " + accessory);
                }
            }
        }
    }
};

要注册广播接收器,在活动的 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() 方法

UsbAccessory accessory;
...
mUsbManager.requestPermission(accessory, mPermissionIntent);

当用户响应对话框时,广播接收器收到包含额外值 EXTRA_PERMISSION_GRANTED 的 intent,这是表示答案的布尔值。 在连接配件之前,请检查这个额外值是否为 true。

与配件通信

您可以使用 UsbManager 与配件通信,以获取文件描述符,您可以配置输入和输出流来读取和写入数据到描述符。 数据流表示配件的输入和输出批量端点。 您应该在另一个线程中建立设备和配件之间的通信,因而不会阻塞主UI线程。 以下示例展示如何打开配件进行通信:

UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;

...

private void openAccessory() {
    Log.d(TAG, "openAccessory: " + accessory);
    mFileDescriptor = mUsbManager.openAccessory(mAccessory);
    if (mFileDescriptor != null) {
        FileDescriptor fd = mFileDescriptor.getFileDescriptor();
        mInputStream = new FileInputStream(fd);
        mOutputStream = new FileOutputStream(fd);
        Thread thread = new Thread(null, this, "AccessoryThread");
        thread.start();
    }
}

在线程的 run() 方法中,可以使用 FileInputStream 或 FileOutputStream 对象来读写配件。 使用 FileOutputStream 对象从配件读取数据时,请确保使用的缓冲区足够大以存储 USB 数据包数据。 Android 配件协议支持高达16384字节的数据包缓冲区,因此为了简单起见,您可以选择始终声明缓冲区为此大小。

Note:注意:在较低级别,USB 全速配件的数据包为64字节,USB 高速配件为512字节。 为了简单起见,Android 配件协议将两个速度的数据包捆绑在一起成为一个逻辑数据包。

有关在 Android 中使用线程的更多信息,请参阅 Processes and Threads

终止与配件通信

当你与配件通信完成或者配件拔出时,调用 close() 方法关闭你打开的描述符为了监听拔除事件,如下所示创建广播接收器:

BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
            UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
            if (accessory != null) {
                // call your method that cleans up and closes communication with the accessory
            }
        }
    }
};

在应用程序中而不是 manifest 中创建广播接收器允许应用在运行时只处理拔除事件。通过这种方式,广播事件只会发送到当前正在运行的应用程序而不是广播到所有应用。


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









发布了131 篇原创文章 · 获赞 464 · 访问量 62万+

猜你喜欢

转载自blog.csdn.net/JAZZSOLDIER/article/details/73849057
usb