Android开放配件 (AOA) 协议
一、背景
自Android 3.1之后的版本,Google引入了USB Accessories的概念,并提供了相关的开发库。Android3.1之后的版本不仅可以让Android设备作为USB Host的角色支持USB鼠标、键盘、游戏手柄等,还可以以USB Device的角色与一些具有USB Host功能,但却扮演着配件角色的设备相连,Google把这种设备称为“Accessory”(附件)。这类Accessory可能是如下设备:机器人控制器、Dock(基座)、诊断设备、音响设备、配电设备、读卡器等等。
Google引入USB Accessory概念的原因应该主要有如下:
- 非常多的Android设备不具有USB Host的功能而只具有USB Device功能(例如绝大部分Android手机),或者即使具备USB Host的功能,也承担不起对USB外设供电的任务,因为便携式Android设备本身的电池容量就很有限。
- 原来的Android设备,作为USB Device所实现的功能相对比较简单,内置的功能只有U盘或ADB调试设备等,Google希望提供应用层的USB开发库,让更多的软硬件厂商来开发新的功能,比如说安装一个APK应用,然后通过USB连接到一个与电视机配套的Dock上,就可以让一台Android手机变身为一个电视机遥控器。
以此借助一套标准的AOA(Android Open Accessory)协议,方便Android设备和外围设备通过USB进行交互,实现各种 Android 设备功能扩展。
二、Usb Accessory的设计实现
Android 设备可以通过主机模式和配件模式和各种USB设备通信。
- 主机模式 Host Mode : Android设备充当USB HOST,USB配件充当 USB DEVICE,Android 设备负责给总线供电及枚举。
- 配件模式 Accessory Mode :USB配件充当USB HOST,Android设备充当USB DEVICE,USB配件为 Android 设备提供电源并进行枚举,与主机模式相反。
两种模式如下图所示:
软件架构图:
AOA 协议有 1.0 和 2.0 两个版本,2.0版本是对对1.0版本的补充,增加了对Audio和HID类Accessory设备的支持:
版本 | 产品 ID | 通信 | 说明 |
---|---|---|---|
AOAv1 | 0x2D00 |
配件 | 提供两个批量端点,用于与 Android 应用通信。 |
0x2D01 |
配件 + adb | 在配件开发过程中用于调试。仅当用户在 Android 设备设置中启用了“USB 调试”时才可用。 | |
AOAv2 | 0x2D02 |
音频 | 将音频从 Android 设备流式传输至配件。 |
0x2D03 |
音频 + adb | ||
0x2D04 |
配件 + 音频 | ||
0x2D05 |
配件 + 音频 + adb |
AOA 协议将上述3种接口组合出6种 USB 接口层设备,这些USB设备的厂商 ID 统一为 0x18D1 (Google Inc)。如需确定连接的 Android 设备是否支持配件和支持的协议版本,该配件必须发送 getProtocol()
命令并检查结果。仅支持 AOAv1 功能的 Android 设备必须返回 1
作为协议版本,支持 AOAv2 的额外功能的设备必须返回 2
作为协议版本。AOAv2 向后兼容 AOAv1,因此基于原始配件协议设计的配件将可以兼容更高版本的 Android 设备。
注:音频输出在 Android 8.0 中已被弃用。谷歌官网 https://source.android.com/docs/core/interaction/accessories/aoa2?hl=zh-cn 中有详细介绍 。
这里简单介绍一下USB Composite(复合)设备,对于大部分USB Device设备来说,它仅仅只有一个功能,比如大部分U盘,单个的USB鼠标等,但是也有些USB设备不止实现一个功能,比如某些USB上网卡有无线上网的功能,同时还有U盘存储的功能,又比如有的鼠标和键盘二合一设备,它只有一个USB接口,却同时支持了鼠标和键盘两个功能。这种一个USB接口扩展出多个设备功能的实现方法有两种,一种是在设备外部或内部加Hub扩展;另一种就是以Usb Composite Device方式实现(一般称为复合设备)。复合设备其实只是一个USB设备,只有一个USB设备地址,它实现多个功能的原因主要在于它扩展实现了多个USB接口,每个接口具有不同的设备类型。
Usb accessory模式也基于复合设备驱动实现,具体源码在内核的 drivers/usb/gadget/function/ 目录下,有 f_accessory.c 、f_audio_source.c、f_hid.c 等等,详细的驱动剖析可参考: Android USB之复合设备(gadget)详解 。
三、使用Usb Accessory模式
1.Device端, 也就是当前Android设备作为配件。
原生就有配置,只要设置属性:setprop sys.usb.config accessory ,即可开启Accessory模式,对应的usb口要支持OTG功能,并且切成device模式。
然后可以向驱动节点: /dev/usb_accessory 写入数据即可,会通过usb往外发送给Host端。参考如下3种测试方法:
echo "123" > /dev/usb_accessory //echo字符串 cat /sdcard/DCIM/test.mp4 > /dev/usb_accessory //cat文件,注意默认是一次写入4k大小,跟驱动缓存大小有关。 busybox dd if=/sdcard/DCIM/test.mp4 of=/dev/usb_accessory bs=128K count=100 // dd方式写入,可以随意指定每次写入的大小。
App测试工程:https://github.com/dragonforgithub/AccessoryChart.git
也可以用C写个简单的demo,核心实现都是读写 /dev/usb_accessory 节点:
#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #define DRIVER_NAME "/dev/usb_accessory" #define MAX_USBFS_BUFFER_SIZE 16384 int open_accessory() { int fd = open(DRIVER_NAME, O_RDWR); if (fd < 0) { fprintf(stderr, "Error: could not open %s\n", DRIVER_NAME); return -1; } return fd; } static void usb_accessory_read(int fd) { char c; char buf[MAX_USBFS_BUFFER_SIZE]; int n; while((n = read(fd, buf, MAX_USBFS_BUFFER_SIZE)) >= 0) { fprintf(stderr, "Error: %d\n", n); write(1, buf, n); putchar('\n'); } } static void usb_accessory_write(int fd, char* buff, int length) { char write_buff[MAX_USBFS_BUFFER_SIZE + 1] = {0x00}; length = (length >= MAX_USBFS_BUFFER_SIZE) ? MAX_USBFS_BUFFER_SIZE : length; fprintf(stderr, "Error: length %d\n", length); memcpy(write_buff, buff, length); int result = write(fd, write_buff, length); fprintf(stderr, "Error: result %d\n", result); } int main() { usb_accessory_write(open_accessory(), "123456789", 9); //usb_accessory_read(open_accessory()); return 0; }
2.Host端,也就是当前Android设备作为主设备,可以读写连接过来的配件。
host端不需要额外的usb config, 只要确认同样支持对应版本AOA协议的版本, 测试demo可以是app 使用标准的API,也可以在native层基于libusbhost库实现。
- App参考github上的工程: https://github.com/dragonforgithub/AccessoryChartHost.git
- Native层可参考Android源码里的测试代码:frameworks/base/libs/usb/tests/accessorytest/
编译好的执行程序下载:https://download.csdn.net/download/qq446065466/88579525
posted on 2023-11-29 12:28 sheldon_blogs 阅读(1688) 评论(0) 编辑 收藏 举报