Android 使用 Usb Accessory 模式与 linux 下位机进行通信
2017-04-10 00:25
387 查看
这段时间研究了下Usb Accessory模式, 这个模式正常的使用方法是用来和专为Android设备设计的USB主机硬件通讯的.
这种设备需要使用Accessory Development Kit (ADK)来开发, 遵循Android Open Accessory (AOA)协议.
但是对于我们安卓开发者来说想尝试下这个模式总不可能自己去用ADK来编写一个驱动吧, 所以本文会在linux里使用libusb库来模拟aoa协议来作为外设端.
首先在AndroidManifest文件内添加如下配置
在res文件夹下建立xml文件夹, 添加accessory_filter.xml文件
获取Usb Accessory设备后打开Accessory模式
注意: 官方文档内提供了两种方法获取UsbAccessory设备, 第一种就是我使用的方法(UsbAccessory[] accessoryList = usbManager.getAccessoryList();)可以一次性获取多个Accessory设备, 但是目前Android系统还不支持同时与多个设备进行通信, 第二种方法是在广播内获取Accessory设备, 但是需要取得权限, 具体可以去官方文档查看.
Accessory的开启和关闭, 传输数据使用FileInputStream和FileOutputStream来进行读写
读取消息线程实现, 通过循环来不断读取不同的消息
界面部分具体看demo
安装好后用usb连接android和linux, 在linux下使用lsusb命令查看usb设备
我的设备是0e8d:2008, 记录下自己设备的VID和PID号
然后添加规则, 在ATTR{idVendor}==”0e8d”内替换你自己的VID
打开demo里的usbacc.c文件, 更改你自己的VID和PID
将makefile和usbacc.c放在同一目录后使用make编译并运行
这时如果出现Error setting up accessory的话, 使用lsusb查看一下usb信息
用你accessory模式的vid和pid替换usbacc.c里相应的数值
Android设备如果打开了调试模式的话,在如下位置使用ACCESSORY_PID_ALT来代替ACCESSORY_PID
如果出现LIBUSB_ERROR_IO错误如下:
就使用如下命令可以看到bEndpointAddress值, 18d1:2d01是你进入accessory后的vid和pid
在这里进行相应更改
如果一切正常的话, 第一次进入accessory应该会出现如下提示
如果没有出现该提示但是却进入accessory模式了, 可能会出现linux端能接收但是不能发送的情况, 这时试试换台手机测试, 我在这里卡了两天……
最后看一下通讯的效果
demo下载: https://github.com/GavinAndre/UsbAccessoryDemo
参考:
https://github.com/LucianZala/linuxUsbAccessory
https://github.com/quandoo/android2android-accessory
这种设备需要使用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"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.gavinandre.usbaccessory"> <uses-feature android:name="android.hardware.usb.accessory"/> <application ... <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文件
<?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-dev sudo 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_IO Input/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
相关文章推荐
- Android网络编程(使用socket进行通信)
- Android网络开发中如何使用JSON进行网络通信---Android JSON数据通讯方法解析
- 使用Event Bus模式解耦Android App组件间通信
- Android 使用 HttpClient 进行网络通信,包括Get方式和Post方式
- Android网络开发中如何使用JSON进行网络通信
- Android中基于TCP协议的网络通信之使用Socket进行通信
- Android网络开发中如何使用JSON进行网络通信---Android JSON数据通讯方法解析
- Android中使用Json和Xml与服务器进行通信,使用代码发送Get和Post请求,http请求辅助类
- Linux 环境下如何使用NDK进行Android开发
- Android网络开发中如何使用JSON进行网络通信---Android_JSON数据通讯方法解析
- Android中使用Intent和IntentFilter进行通信
- Android学习13--使用Intent和IntentFilter进行通信
- linux 多次创建不同的UDP socket进行通信,最终通信用的UDP 源端口以最后一次创建的socket使用的源端口为准
- 使用Socket进行通信(Android)
- 使用 java.io 进行服务器-Android通信
- android使用socket于本地PC进行通信的问题
- Android 使用 HttpClient 进行网络通信,包括Get方式和Post方式
- 使用sipp对FreeSwitch进行测试(Linux环境,会议模式)
- Android中基于TCP协议的网络通信之使用Socket进行通信
- 使用用户模式linux(UML, User mode linux)来进行内核Debug