Skip to content

天楚锐齿

人工智能 云计算 大数据 物联网 IT 通信 嵌入式

天楚锐齿

  • 下载
  • 物联网
  • 云计算
  • 大数据
  • 人工智能
  • Linux&Android
  • 网络
  • 通信
  • 嵌入式
  • 杂七杂八

两个Android设备间建立配件模式

2021-11-29

Android配件模式简介:

    如下图所示,Android设备比如手机作为USB device方,配件可以是电脑也可以是android设备作为USB host方(必须能提供500ma的5v供电)。注意配件和配件模式的区别,配件是作为host来连接android设备,配件模式是指android device设备被host设置成了配件模式。

建立配件模式的过程:

  1. 配件通过USB驱动的控制通道(0通道,端点0)获取手机的VID和PID。
  2. 配件分析获取的vendor id是否为0x18d1,以及product id是否为0x2D01,0x2D02,0x2D03,0x2D04,0x2D05。如果均符合,则说明手机现在已经是配件模式,可以按照配件模式的要求直接重新配置USB端点和接口。否则启动尝试进入配件模式流程。
  3. 确定手机已经进入配件模式,重新枚举USB设备,手机重新进行USB协商。
  4. 按照配件模式重新配置USB端点和接口,建立配件模式的数据通道。

尝试启动配件交互模式流程:

  1. 配件发送序号51的USB请求报文,手机收到后查询自己的AOA协议版本,发送响应报文给配件。
  2. 配件校验协议版本号,目前为1或2,其他的均不支持。
  3. 配件发送序号52的USB请求报文,通过Index字段携带配件自身信息,包括制造商、型号及版本、设备描述、序列号、URI等,手机根据这些信息启动响应的APP。
  4. 配件发送序号53的USB请求报文,切换USB模式,主要是根据切换VID/PID。
  5. 重新枚举USB设备,准备建立AOA数据通道。

AOA设备的握手过程:

    绝大多数Android设备,在缺省情况下都不挂载Accessory驱动,在Accessory和Android设备建立USB连接时;Accessory会通过握手协议查询该设备是否为android设备且具有AOA支持,如果获得正确应答,Accessory会向Adnroid设备发出切换到AOA模式的请求,Android设备会执行该请求,将USB切换成AOA模式;在这个过程中,USB连接会出现一次逻辑插拔,USB host一端会重新枚举设备。
    在握手过程中,Accessory会向Android提供AOA约定的描述信息,其中有3个信息是Android系统用于绑定Accessory设备与APP的;分别是:manufacturer、model、version。Android系统根据这3个字符串匹配相应的APP。
    如果系统内无任何APP可以匹配Accessory发来的握手消息;则Adnroid设备会弹出一个对话框,向用户提供Accessory设备发送来的描述信息和URL信息,用户可以点击URL所指向的web页面。
    如果系统有app可以匹配accessory发来的握手消息;则android会弹出一个对话框询问用户是否立刻启动该app;如果用户选择ok则启动该app;同时该对话框提供一个勾选项,勾选之后每次accessory设备连接后会自动启动该app;应该要求用户勾选该对话框,否则app启动后向UsbManager获取accessory设备后可能因为permission问题无法打开文件描述符建立通信连接。

注意:

  1. pid为0x2D00是保留给支持附件模式的Android设备。0x2D01保留用于支持附件模式以及android调试桥adb协议的设备,第二个接口为adb并且具有两个批量端点。如果要在计算机上模式附件,可以使用这些端点来调试附件程序。一般来说,不要使用此接口,除非配件在设备上实现传输到adb。
            0x2D00 具有一个拥有2个批量端点的接口,用于输出和输入通信的。
            0x2D01 具有两个接口,每个接口有2个批量端点,用于输入和输出通信。第一个接口处理标准通信,第二个接口处理adb通信。要使用接口,请找到第一个批量输入和输出端点,会用SET_CONFIGURATION(0x09)设备请求将设备配置设置为1,然后使用端点进行通信。
  1. AOA目前不支持同时进行AOA和MTP(媒体传输协议)连接。要从AOA切换到MTP,配件必须首先断开USB设备(物理上或电气上等效方式),然后使用MTP重新连接。

实现:

关于AOA的host端在android上实现的代码在网上有很多,这里不再贴出来。

关于AOA的accessory配件端在android上怎么实现呢,网上一般找出来的都是linux下模式的adk方式,其实用android做配件原理是一样的,就是得转换成java代码方式来实现。

涉及到的几个常量定义:

/*
    配件模式AOA支持:
    VID  固定为Google的官方VID – 0x18D1
    PID 在不同的模式下定义如下:
        ●          0x2D00 - V1 accessory
        ●          0x2D01 - V1 accessory + adb
        ●          0x2D02 - V2 audio, 注意V2兼容V1,上面两个仍然支持。,注意:AOAv2音频支持在Android 8.0中已被弃用
        ●          0x2D03 - V2 audio + adb,注意:AOAv2音频支持在Android 8.0中已被弃用
        ●          0x2D04 - V2 accessory + audio,注意:AOAv2音频支持在Android 8.0中已被弃用
        ●          0x2D05 - V2 accessory + audio + adb,注意:AOAv2音频支持在Android 8.0中已被弃用
 */
/**
 * USB请求类型requestType按位定义:
 * define USB_SETUP_HOST_TO_DEVICE                0x00    // Device Request bmRequestType transfer direction - host to device transfer
 * define USB_SETUP_DEVICE_TO_HOST                0x80    // Device Request bmRequestType transfer direction - device to host transfer
 * define USB_SETUP_TYPE_STANDARD                 0x00    // Device Request bmRequestType type - standard
 * define USB_SETUP_TYPE_CLASS                    0x20    // Device Request bmRequestType type - class
 * define USB_SETUP_TYPE_VENDOR                   0x40    // Device Request bmRequestType type - vendor
 * define USB_SETUP_RECIPIENT_DEVICE              0x00    // Device Request bmRequestType recipient - device
 * define USB_SETUP_RECIPIENT_INTERFACE           0x01    // Device Request bmRequestType recipient - interface
 * define USB_SETUP_RECIPIENT_ENDPOINT            0x02    // Device Request bmRequestType recipient - endpoint
 * define USB_SETUP_RECIPIENT_OTHER               0x03    // Device Request bmRequestType recipient - other
 * USB配件模式下PID/VID定义:
 * #define USB_ACCESSORY_VENDOR_ID         0x18D1
 * #define USB_ACCESSORY_PRODUCT_ID        0x2D00
 * #define USB_ACCESSORY_ADB_PRODUCT_ID    0x2D01
 * USB请求序号:
 * #define ACCESSORY_STRING_MANUFACTURER   0
 * #define ACCESSORY_STRING_MODEL          1
 * #define ACCESSORY_STRING_DESCRIPTION    2
 * #define ACCESSORY_STRING_VERSION        3
 * #define ACCESSORY_STRING_URI            4
 * #define ACCESSORY_STRING_SERIAL         5
 * #define ACCESSORY_GET_PROTOCOL          51
 * #define ACCESSORY_SEND_STRING           52
 * #define ACCESSORY_START                 53
 * 配件发送序号51的USB请求报文,得到协议信息,即对方(手机)是否支持aoa模式
 * 配件发送序号52的USB请求报文,通过Index字段携带配件自身信息,包括制造商、型号、版本、设备描述、序 列号URI等。手机根据这些信息启动响应的APP
 * 配件发送序号53的USB请求报文,切换USB模式,主要是根据切换的vendorID和productID
 * 重新枚举USB设备,准备建立AOA数据通道
**/
1. 配件作为host,插入或拔出usb线时都会收到UsbManager.ACTION_USB_DEVICE_DETACHED和UsbManager.ACTION_USB_DEVICE_ATTACHED广播。
2. 用getDeviceList()扫描所有usb设备,找到自己所需的usb配件端口。
3. 查看该配件是否已经处于配件模式,如果是2d00/18d1,则用openDevice(device)直接进入通信:
//最好关闭掉Host端Android设备的USB ADB调试功能(android11以后可以用wifi无线调试替代usb调试),
// 否则在0X2D01下2个interface难以区分那个接口是adb,哪个接口是aoa。
mUsbInterface = device.getInterface(0);
for(int j=0; j< 2; j++) {
if (UsbConstants.USB_DIR_IN == mUsbInterface.getEndpoint(j).getDirection()) {
mUsbEndpointIn = mUsbInterface.getEndpoint(j);
mUsbInMax = mUsbEndpointIn.getMaxPacketSize();
}
if (UsbConstants.USB_DIR_OUT == mUsbInterface.getEndpoint(j).getDirection()) {
mUsbEndpointOut = mUsbInterface.getEndpoint(j);
mUsbOutMax = mUsbEndpointOut.getMaxPacketSize();
}
}
// requestType: USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE
// request: ACCESSORY_SET_CONFIGURATION
int rtn = connection.controlTransfer(0x00, 9, 1, 0,
null, 0, IAoaConst.USB_TIMEOUT_IN_MS);
if(rtn < 0){
tmpStr = "enterCommunication: controlTransfer return error:" + rtn;
Log.i(TAG,tmpStr);
SendMsgToMainActivity(tmpStr);
return false;
}
//发送一次
byte[] buffer = new byte[]{'a', 'b', 'c', 'd'};
connection.bulkTransfer(mUsbEndpointOut, buffer, buffer.length, IAoaConst.USB_TIMEOUT_IN_MS);
tmpStr = "enterCommunication: connection.bulkTransfer to peer.";
Log.i(TAG,tmpStr);
SendMsgToMainActivity(tmpStr);
//接收消息的线程
new CommunicationThread().start();

private class CommunicationThread extends Thread {
    @Override
    public void run() {
        Log.w(TAG, "CommunicationThread: start receive.");
        running = true;
        byte[] buffer = new byte[1024];
        while (running) {
            //Handle incoming messages
            if(mUsbEndpointIn!=null) {
                int len = connection.bulkTransfer(mUsbEndpointIn, buffer, buffer.length, IAoaConst.USB_TIMEOUT_IN_MS);
                if (len >= 0) {
                    Log.w(TAG, "CommunicationThread: received msg, len: " + len);
                    //Log.w(TAG, "CommunicationThread: received msg, content: " + new String(buffer));
                }else{
                    Log.w(TAG, "CommunicationThread: received error!");
                }
            }
        }
    }
}
4. 如果不是,则需要进入初始化过程,还是openDevice(device)打开设备,然后用51号请求得到协议号:
private int getProtocol(UsbDeviceConnection connection)
{
    byte[] buffer = new byte[]{0,0};
    // requestType: USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE
    // request: ACCESSORY_GET_PROTOCOL
    int rtn = connection.controlTransfer(0xC0, 51, 0, 0,
            buffer, 2, IAoaConst.USB_TIMEOUT_IN_MS);
    if(rtn <=0){
        String tmpStr = "getProtocol: controlTransfer return error:" + rtn;
        Log.i(TAG,tmpStr);
        SendMsgToMainActivity(tmpStr);
    }
    String tmpStr = "getProtocol: protocol:" + buffer[0]+"."+buffer[1];
    Log.i(TAG,tmpStr);
    SendMsgToMainActivity(tmpStr);
    return rtn;
}

5. 然后通过52号请求设置参数:
//支持aoa协议,则控制对端(手机)启动特定的application。
boolean res = initStringControlTransfer(connection, 0, IAoaConst.USB_AOA_MANUFACTURER); // MANUFACTURER
res = res&&initStringControlTransfer(connection, 1, IAoaConst.USB_AOA_MODEL); // MODEL
res = res&&initStringControlTransfer(connection, 2, IAoaConst.USB_AOA_DESCRIPTION); // DESCRIPTION
res = res&&initStringControlTransfer(connection, 3, IAoaConst.USB_AOA_VERSION); // VERSION
res = res&&initStringControlTransfer(connection, 4, IAoaConst.USB_AOA_URI); // URI
res = res&&initStringControlTransfer(connection, 5, IAoaConst.USB_AOA_SERIAL); // SERIAL
private boolean initStringControlTransfer(final UsbDeviceConnection deviceConnection,
                                       final int index, final String string) {
    // requestType: USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE
    // request: ACCESSORY_SEND_STRING
    int rtn = deviceConnection.controlTransfer(0x40, 52, 0, index,
            string.getBytes(), string.length(), IAoaConst.USB_TIMEOUT_IN_MS);
    if(rtn <=0){
        String tmpStr = "initStringControlTransfer: controlTransfer return error:" + rtn;
        Log.e(TAG,tmpStr);
        SendMsgToMainActivity(tmpStr);
        return false;
    }
    return true;
}
6. 最后使用53号请求启动AOA模式:
// requestType: USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE
// request: ACCESSORY_START
connection.controlTransfer(0x40, 53, 0, 0, new byte[]{}, 0, IAoaConst.USB_TIMEOUT_IN_MS);
7. 此时USB会做一次逻辑插拔,等attach到来后,会发现之前的usb pid/vid已经成了2d00/18d1,此时即可进入通信了。






 

763次阅读

Post navigation

前一篇:

React在16.8版本开始增加Hook支持

后一篇:

LVGL嵌入式开源图形库

发表评论 取消回复

邮箱地址不会被公开。 必填项已用*标注

个人介绍

需要么,有事情这里找联系方式:关于天楚锐齿

=== 美女同欣赏,好酒共品尝 ===

微信扫描二维码赞赏该文章:

扫描二维码分享该文章:

分类目录

  • Linux&Android (79)
  • Uncategorized (1)
  • 下载 (28)
  • 云计算 (37)
  • 人工智能 (8)
  • 大数据 (24)
  • 嵌入式 (34)
  • 杂七杂八 (34)
  • 物联网 (59)
  • 网络 (23)
  • 通信 (21)

文章归档

近期文章

  • 使用Python渲染OpenGL的.obj和.mtl文件
  • 用LVGL图形库绘制二维码
  • Android使用Messenger和SharedMemory实现跨app的海量数据传输
  • CAN信号的c语言解析代码
  • QT qml下DBus的使用例子

近期评论

  • 硕发表在《使用Android的HIDL+AIDL方式编写从HAL层到APP层的程序》
  • maxshu发表在《使用Android的HIDL+AIDL方式编写从HAL层到APP层的程序》
  • Ambition发表在《使用Android的HIDL+AIDL方式编写从HAL层到APP层的程序》
  • Ambition发表在《使用Android的HIDL+AIDL方式编写从HAL层到APP层的程序》
  • maxshu发表在《Android9下用ethernet 的Tether模式来做路由器功能》

阅读量

  • 使用Android的HIDL+AIDL方式编写从HAL层到APP层的程序 - 16,804次阅读
  • 卸载深信服Ingress、SecurityDesktop客户端 - 12,078次阅读
  • 车机技术之Android Automotive - 6,661次阅读
  • 车机技术之车规级Linux-Automotive Grade Linux(AGL) - 5,857次阅读
  • Linux策略路由及iptables mangle、ip rule、ip route关系及一种Network is unreachable错误 - 5,709次阅读
  • 在Android9下用ndk编译vSomeIP和CommonAPI以及使用例子 - 5,658次阅读
  • linux下的unbound DNS服务器设置详解 - 5,600次阅读
  • linux的tee命令导致ssh客户端下的shell卡住不动 - 4,998次阅读
  • 车机技术之360°全景影像(环视)系统 - 4,896次阅读
  • libwebp(处理webp图像)的安装和使用 - 4,749次阅读

功能

  • 文章RSS
  • 评论RSS

联系方式

地址
深圳市科技园

时间
周一至周五:  9:00~12:00,14:00~18:00
周六和周日:10:00~12:00

标签

android AT命令 centos Hadoop hdfs ip ipv6 kickstart linux mapreduce mini6410 modem OAuth openstack os python socket ssh uboot 内核 协议 安装 嵌入式 性能 报表 授权 操作系统 数据 数据库 月报 模型 汽车 测试 深信服 深度学习 源代码 神经网络 统计 编译 网络 脚本 虚拟机 调制解调器 车机 金融
© 2023 天楚锐齿