这段时间研究了下Usb Accessory模式, 这个模式正常的使用方法是用来和专为Android设备设计的USB主机硬件通讯的.
这种设备需要使用Accessory Development Kit (ADK)来开发, 遵循Android Open Accessory (AOA)协议.

但是对于我们安卓开发者来说想尝试下这个模式总不可能自己去用ADK来编写一个驱动吧, 所以本文会在linux里使用libusb库来模拟aoa协议来作为外设端.

Android端:

USB Accessory有两种API可供选择, 第一种是android.hardware.usb包, 只能用在Android 3.1及之后的系统上, 不需要额外添加library, 第二种是com.android.future.usb包, 需要额外添加 Google APIs add-on library , 也就是谷歌服务, 但是可以运行在Android 2.3.4以及之后的系统上, 这里我们使用第一种来演示, 第二种可以去官方文档进行了解 .

首先在AndroidManifest文件内添加如下配置

<?xml version="1.0" encoding="utf-8"?>"http://schemas.android.com/apk/res/android"          package="com.gavinandre.usbaccessory">    "android.hardware.usb.accessory"/>    ...        ...                            "android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>                        "android.hardware.usb.action.USB_ACCESSORY_ATTACHED"                android:resource="@xml/accessory_filter"/>            

在res文件夹下建立xml文件夹, 添加accessory_filter.xml文件

<?xml version="1.0" encoding="utf-8"?><resources>    <usb-accessory        manufacturer="Lutixia"        model="Demo"        version="1.0"        />resources>

获取Usb Accessory设备后打开Accessory模式

public AccessoryCommunicator(final Context context) {        this.context = context;        usbManager = (UsbManager) this.context.getSystemService(Context.USB_SERVICE);        final UsbAccessory[] accessoryList = usbManager.getAccessoryList();        if (accessoryList == null || accessoryList.length == 0) {            onError("no accessory found");        } else {            openAccessory(accessoryList[0]);        }    }

注意: 官方文档内提供了两种方法获取UsbAccessory设备, 第一种就是我使用的方法(UsbAccessory[] accessoryList = usbManager.getAccessoryList();)可以一次性获取多个Accessory设备, 但是目前Android系统还不支持同时与多个设备进行通信, 第二种方法是在广播内获取Accessory设备, 但是需要取得权限, 具体可以去官方文档查看.

Accessory的开启和关闭, 传输数据使用FileInputStream和FileOutputStream来进行读写

private void openAccessory(UsbAccessory accessory) {        fileDescriptor = usbManager.openAccessory(accessory);        if (fileDescriptor != null) {            FileDescriptor fd = fileDescriptor.getFileDescriptor();            inStream = new FileInputStream(fd);            outStream = new FileOutputStream(fd);            //接收消息的线程            new CommunicationThread().start();            //发送消息的线程            sendHandler = new Handler() {                public void handleMessage(Message msg) {                    try {                        outStream.write((byte[]) msg.obj);                    } catch (final Exception e) {                        onError("USB Send Failed " + e.toString() + "\n");                    }                }            };        } else {            onError("could not connect");        }    }public void closeAccessory() {        running = false;        try {            if (fileDescriptor != null) {                fileDescriptor.close();            }        } catch (IOException e) {        } finally {            fileDescriptor = null;        }}

读取消息线程实现, 通过循环来不断读取不同的消息

private class CommunicationThread extends Thread {        @Override        public void run() {            running = true;            while (running) {                byte[] msg = new byte[Constants.BUFFER_SIZE_IN_BYTES];                try {                    //Handle incoming messages                    int len = inStream.read(msg); //等待消息时会阻塞在这里                    while (inStream != null && len > 0 && running) {                        receive(msg, len);                        Thread.sleep(10);                        len = inStream.read(msg);                    }                } catch (final Exception e) {                    onError("USB Receive Failed " + e.toString() + "\n");                    closeAccessory();                }            }        }    }

界面部分具体看demo

Linux端:

首先需要安装libusb和usb的头文件

sudo apt-get install libusb-devsudo apt-get install libusb-1.0-0-dev

安装好后用usb连接android和linux, 在linux下使用lsusb命令查看usb设备

我的设备是0e8d:2008, 记录下自己设备的VID和PID号

然后添加规则, 在ATTR{idVendor}==”0e8d”内替换你自己的VID

sudo vim /etc/udev/rules.d/51-android.rules//在51-android.rules内添加两条规则SUBSYSTEM=="usb", ATTR{idVendor}=="0e8d", MODE="0666", GROUP="plugdev"SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0666", GROUP="plugdev"//保存后要添加权限sudo chmod a+r /etc/udev/rules.d/51-android.rules

打开demo里的usbacc.c文件, 更改你自己的VID和PID

/* Android device vendor/product */#define AD_VID 0x0e8d#define PID 0x2008

将makefile和usbacc.c放在同一目录后使用make编译并运行

make..../usbacc

这时如果出现Error setting up accessory的话, 使用lsusb查看一下usb信息

用你accessory模式的vid和pid替换usbacc.c里相应的数值

#define GOOGLE_VID 0x18d1#define ACCESSORY_PID 0x2d01 /* accessory with adb */#define ACCESSORY_PID_ALT 0x2d00 /* accessory without adb */

Android设备如果打开了调试模式的话,在如下位置使用ACCESSORY_PID_ALT来代替ACCESSORY_PID

if ((handle = libusb_open_device_with_vid_pid(NULL,              GOOGLE_VID, ACCESSORY_PID_ALT)) == NULL) {

如果出现LIBUSB_ERROR_IO错误如下:

Error: LIBUSB_ERROR_IOInput/output error.Done, no errors

就使用如下命令可以看到bEndpointAddress值, 18d1:2d01是你进入accessory后的vid和pid

lsusb -v -d 18d1:2d00 | grep bEndpointAddress        bEndpointAddress     0x81  EP 1 IN        bEndpointAddress     0x02  EP 2 OUT

在这里进行相应更改

#define EP_IN 0x81#define EP_OUT 0x02

如果一切正常的话, 第一次进入accessory应该会出现如下提示

如果没有出现该提示但是却进入accessory模式了, 可能会出现linux端能接收但是不能发送的情况, 这时试试换台手机测试, 我在这里卡了两天……

最后看一下通讯的效果

demo下载: https://github.com/GavinAndre/UsbAccessoryDemo

参考:
https://github.com/LucianZala/linuxUsbAccessory
https://github.com/quandoo/android2android-accessory

更多相关文章

  1. No.11 使用firewall配置的防火墙策略的生效模式
  2. Android线程的一些问题
  3. Android开发AsyncTask异步处理任务使用方法及注意事项
  4. android开发笔记(一)Android(安卓)studio 输入法
  5. Android(安卓)IOS WebRTC 音视频开发总结(十二)-- sufaceview
  6. Delphi XE5 for Android(安卓)(一)
  7. Android入门教程(十三)之自定义下拉菜单模式----Spinner与setDro
  8. Android之EventBus1.0 和EventBus3.0的使用详解
  9. Google Android如何分析和研究Log文件 ,如何看日志信息

随机推荐

  1. 8.24 linux c socket 简单实现
  2. 谁能讲一下搞网站后端开发的刚进公司每天
  3. Linux的时间函数(转载)
  4. 在红帽企业Linux 4中设置 device-mapper
  5. Linux与Windows平台的一些兼容性注意事项
  6. Linux下安装Mathematica 9的说明
  7. linux下rar包的解压方法
  8. linux性能监测工具perf
  9. Linux源码中的mktime算法解析
  10. Linux下运行.cpp文件