Android(安卓)Service创建USB HOST通信
之前做了一个关于Android USB通信的Case,通过Android的USB总线给Zigbee供电,和板载的Zigbee(基于Zigbee的自组网)进行通信。要使用Android的USB Host功能,首先你需要确定你的平板(手机)设备是否支持USB Host的功能,你可以从手机开发商的简介里面看到,这个功能是由CPU直接关联的,和软件没有关系,所以,你可以把你的外设模块插进去你的Android设备看看有没有反应(比如:指示灯亮),假如没有反应,那肯定是不支持的。
废话说了那么多了,进入正题。
我的案子是把USB的通信写在Service里面的,有不了解Service的,先看Service的相关内容。下面列出了Android对USB的支持。
点开USB,你可以看到Android USB支持的两种模式:
第一种就是HOST模式:Android设备为USB总线和外设供电,数据传输是双向的。
第二种是Accessory模式:即附件模式,Android作为附件,手机和电脑连接,通常是这种模式,由USB Device端向总线供电,数据传输方向是双向的。这就是为什么手机插到电脑上可以充电的原因。
我们讨论的是第一种模式——HOST。
在讨论具体代码之前,我需要先讲一下再host模式下面的调试办法,因为数据线的端口被外设使用了,那么,传统的连接数据线调试的方法已经不行了。官网给出了解决办法:
1、请把你的Android设备用数据线连接到电脑,当然,你也要把Android设备的wifi打开。
2、在windows命令行下,进入SDKplatform-tools/
目录(具体看你把SDK安装在哪个目录了),执行adb tcpip 5555
回车。这里其实是打开了adb调试的无线端口(Android设备在电脑上的端口映射),其实后面的数字你可以随便来,只要端口没有被占用。
3、adb connect <device-ip-address>:5555键入回车,这里的device-ip-address是你Android端的IP地址。
4、最后adb usb回车,假如没有问题,现在你已经可以在eclipse上看到logcat的输出了。
其实还有个简单的办法,笔者是使用这个办法,你可以在应用商店上下一个无线ADB工具,随便哪个都可以,这类工具就是在Android端做了上面的那些工作,而且还不用连数据线,但是第三步的那个操作还是要你在windows的命令行窗口手动输入的。
通过上面的操作,你已经可以用无线下载你写的程序、调试了。
在写代码之前,我们必须在我们的manifest文件里面声明我们要使用USB Host的功能。
方法:添加权限标签
- <uses-feature
- android:name="android.hardware.usb.host"
- android:required="true"/>
- <uses-permission
- android:name="android.hardware.usb.host"
- android:required="false"/>
除开声明外,你还要添加一个关于设备信息的xml文件,这个文件放在res目录下的xml目录下:
里面写好了我们要发现的具体设备的PID、VID,我只写了这两个,这是我在测试阶段用的一个设备的:
[html] view plain copy
- <?xmlversion="1.0"encoding="utf-8"?>
- <resources>
- <usb-device
- product-id="5800"
- vendor-id="1105"/>
- </resources>
当然,你还要manifest中的某个activity下声明,你使用了这个文件,那么,一旦你打开应用,就会自动跳转到你声明的那个activity:
[html] view plain copy
- <activity
- android:name="cyc.gzpl.gzplv3.WelcomeActivity"
- android:launchMode="standard"
- android:screenOrientation="landscape"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
- <intent-filter>
- <actionandroid:name="android.intent.action.MAIN"/>
- <categoryandroid:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- <intent-filter>
- <actionandroid:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
- </intent-filter>
- <meta-data
- android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
- android:resource="@xml/device_filter"/>
我放在了MainActivity
建立了一个Service类:
[java] view plain copy
- publicclassZigbeeServiceextendsService{
- //这里定义一些必要的变量
- <spanstyle="white-space:pre"></span>@Override
- publicvoidonStart(Intentintent,intstartId){
- //TODOAuto-generatedmethodstub
- super.onStart(intent,startId);//每次startService(intent)时都回调该方法
- System.out.println("进入service的onStart函数");
- myUsbManager=(UsbManager)getSystemService(Context.USB_SERVICE);//获取UsbManager
- //枚举设备
- enumerateDevice(myUsbManager);
- //查找设备接口
- getDeviceInterface();
- //获取设备endpoint
- assignEndpoint(Interface2);
- //打开conn连接通道
- openDevice(Interface2);
- }
[java] view plain copy
- myUsbManager=(UsbManager)getSystemService(Context.USB_SERVICE);//获取UsbManager
这条代码的作用是获取一个UsbManager的实例,通过它我们可以获取USB的状态,并且和USB设备进行通信。总的来说,它管理了USB设备。
你把你的外设插入USB接口后,紧接着就是调用美剧USB总线上的usb设备了,具体函数实现如下:
[java] view plain copy
- //枚举设备函数
- privatevoidenumerateDevice(UsbManagermUsbManager){
- System.out.println("开始进行枚举设备!");
- if(mUsbManager==null){
- System.out.println("创建UsbManager失败,请重新启动应用!");
- info.setText("创建UsbManager失败,请重新启动应用!");
- return;
- }else{
- HashMap<String,UsbDevice>deviceList=mUsbManager.getDeviceList();
- if(!(deviceList.isEmpty())){
- //deviceList不为空
- System.out.println("deviceListisnotnull!");
- Iterator<UsbDevice>deviceIterator=deviceList.values()
- .iterator();
- while(deviceIterator.hasNext()){
- UsbDevicedevice=deviceIterator.next();
- //输出设备信息
- Log.i(TAG,"DeviceInfo:"+device.getVendorId()+","
- +device.getProductId());
- System.out.println("DeviceInfo:"+device.getVendorId()
- +","+device.getProductId());
- //保存设备VID和PID
- VendorID=device.getVendorId();
- ProductID=device.getProductId();
- //保存匹配到的设备
- if(VendorID==1105&&ProductID==5800){
- myUsbDevice=device;//获取USBDevice
- System.out.println("发现待匹配设备:"+device.getVendorId()
- +","+device.getProductId());
- Contextcontext=getApplicationContext();
- Toast.makeText(context,"发现待匹配设备",Toast.LENGTH_SHORT)
- .show();
- }
- }
- }else{
- info.setText("请连接USB设备至PAD!");
- Contextcontext=getApplicationContext();
- Toast.makeText(context,"请连接USB设备至PAD!",Toast.LENGTH_SHORT)
- .show();
- }
- }
- }
接下来的工作:查找外设的usb接口,我的zigbee上面有两个接口,一个0接口,还有一个2接口,我在这里吃了很大的亏,我一直忽视了2接口,一直死在0接口那里,0接口是用来进行配置的,作为用户层,是用不到的,2接口作为数据传输口。具体情况可能是要根据你的外设的情况来定的,推荐你接口的信息都要打印出来,并且都试一下。
下面是查询接口的具体实现:
[java] view plain copy
- //寻找设备接口
- privatevoidgetDeviceInterface(){
- if(myUsbDevice!=null){
- Log.d(TAG,"interfaceCounts:"+myUsbDevice.getInterfaceCount());
- for(inti=0;i<myUsbDevice.getInterfaceCount();i++){
- UsbInterfaceintf=myUsbDevice.getInterface(i);
- if(i==0){
- Interface1=intf;//保存设备接口
- System.out.println("成功获得设备接口:"+Interface1.getId());
- }
- if(i==1){
- Interface2=intf;
- System.out.println("成功获得设备接口:"+Interface2.getId());
- }
- }
- }else{
- System.out.println("设备为空!");
- }
- }
接下来,我们要分配接口的端点:
[java] view plain copy
- //分配端点,IN|OUT,即输入输出;可以通过判断
- privateUsbEndpointassignEndpoint(UsbInterfacemInterface){
- for(inti=0;i<mInterface.getEndpointCount();i++){
- UsbEndpointep=mInterface.getEndpoint(i);
- //lookforbulkendpoint
- if(ep.getType()==UsbConstants.USB_ENDPOINT_XFER_BULK){
- if(ep.getDirection()==UsbConstants.USB_DIR_OUT){
- epBulkOut=ep;
- System.out.println("FindtheBulkEndpointOut,"+"index:"
- +i+","+"使用端点号:"
- +epBulkOut.getEndpointNumber());
- }else{
- epBulkIn=ep;
- System.out
- .println("FindtheBulkEndpointIn:"+"index:"+i
- +","+"使用端点号:"
- +epBulkIn.getEndpointNumber());
- }
- }
- //lookforcontorlendpoint
- if(ep.getType()==UsbConstants.USB_ENDPOINT_XFER_CONTROL){
- epControl=ep;
- System.out.println("findtheControlEndPoint:"+"index:"+i
- +","+epControl.getEndpointNumber());
- }
- //lookforinterrupteendpoint
- if(ep.getType()==UsbConstants.USB_ENDPOINT_XFER_INT){
- if(ep.getDirection()==UsbConstants.USB_DIR_OUT){
- epIntEndpointOut=ep;
- System.out.println("findtheInterruptEndpointOut:"
- +"index:"+i+","
- +epIntEndpointOut.getEndpointNumber());
- }
- if(ep.getDirection()==UsbConstants.USB_DIR_IN){
- epIntEndpointIn=ep;
- System.out.println("findtheInterruptEndpointIn:"
- +"index:"+i+","
- +epIntEndpointIn.getEndpointNumber());
- }
- }
- }
- if(epBulkOut==null&&epBulkIn==null&&epControl==null
- &&epIntEndpointOut==null&&epIntEndpointIn==null){
- thrownewIllegalArgumentException("notendpointisfounded!");
- }
- returnepIntEndpointIn;
- }
分配到的端点,用UsbEndpoint的实例存起来。Endpoint是用来传输数据的,但是,接下来,还是不能进行通信的。。。。。
我们要建立外设和Android设备的连接才行,也就是打开设备的连接,不然怎么通信呢?代码如下:
[java] view plain copy
- //打开设备
- publicvoidopenDevice(UsbInterfacemInterface){
- if(mInterface!=null){
- UsbDeviceConnectionconn=null;
- //在open前判断是否有连接权限;对于连接权限可以静态分配,也可以动态分配权限
- if(myUsbManager.hasPermission(myUsbDevice)){
- conn=myUsbManager.openDevice(myUsbDevice);
- }
- if(conn==null){
- return;
- }
- if(conn.claimInterface(mInterface,true)){
- myDeviceConnection=conn;
- if(myDeviceConnection!=null)//到此你的android设备已经连上zigbee设备
- System.out.println("open设备成功!");
- finalStringmySerial=myDeviceConnection.getSerial();
- System.out.println("设备serialnumber:"+mySerial);
- }else{
- System.out.println("无法打开连接通道。");
- conn.close();
- }
- }
- }
终于可以通信了。。。。。。
下面讲一下USB传输在Android里面的几个函数。。。。。
通常有两种传输方式,一种叫控制传输,具体函数解释如下:
这个传输函数有个特点,它是用于端点0的传输,具体,我也没用过,参考文档上说,0端点是用来配置用的,所以我也没有深究。
另外一个函数bulk传输,它用于usb大流量数据的一个传输,我使用的是这个。
下面是我用这个函数实现读写USB的代码:
[java] view plain copy
- //发送数据
- privatevoidsendMessageToPoint(byte[]buffer){
- //bulkOut传输
- if(myDeviceConnection
- .bulkTransfer(epBulkOut,buffer,buffer.length,0)<0)
- System.out.println("bulkOut返回输出为负数");
- else{
- System.out.println("SendMessageSuccese!");
- }
- }
[java] view plain copy
- //从设备接收数据bulkIn
- privatebyte[]receiveMessageFromPoint(){
- byte[]buffer=newbyte[15];
- if(myDeviceConnection.bulkTransfer(epBulkIn,buffer,buffer.length,
- 2000)<0)
- System.out.println("bulkIn返回输出为负数");
- else{
- System.out.println("ReceiveMessageSuccese!"
- //+"数据返回"
- //+myDeviceConnection.bulkTransfer(epBulkIn,buffer,
- //buffer.length,3000)
- );
- }
- returnbuffer;
- }
上面打开设备的时候得到的UsbDeviceConnection的实例和端点分配分配的端点,将在这里被使用到。
OK!~~~
到这里,大部分的任务都完成了,剩下的工作就是写一些逻辑代码去调用bulk传输函数就可以了,这里我就不多说了。
另外,在Service是需要在manifest里面注册的,程序跑起来以后,Service是完全和程序的其他部分并行的,不影响程序的正常运行,也不会阻塞界面,实现了,后台与Zigbee的通信,这里主要讲的是usb host在Android里面的几个主要的点,关于Zigbee就没有多说了,无非就是一些看用户手册的活。
转自:点击打开链接
接收数据需要另外开启新线程用来接收,并且一直处于监听状态,下面是我自己写的代码:
public void run() {// TODO Auto-generated method stub// System.out.println("进入线程了");UsbRequest request = new UsbRequest();boolean initilzed = request.initialize(conn, endpointIn);if (!initilzed) {Log.e("USB CONNECTION FAILED","Request initialization failed for reading");return;}System.out.println(initilzed);// int bufferMaxLength = endpointIn.getMaxPacketSize();ByteBuffer buffer = ByteBuffer.allocate(38);request.queue(buffer, 38);if (conn.requestWait() == request) {byte[] data = buffer.array();conn.bulkTransfer(endpointIn, data, 38, 1000);// System.out.println(data.toString());final String message = "Read " + data.length + " bytes: \n"+ HexDump.dumpHexString(data) + "\n\n";System.out.println(message);//打印出信息}}还是感谢文章的原作者!
更多相关文章
- Android(安卓)渗透测试学习手册 第四章 对 Android(安卓)设备进
- Android与蓝牙串口模块通信
- Android加速度传感器数值的过滤
- Android物理按键
- Android(安卓)中Observer模式的使用
- android 修改电脑盘符名称 USB连接电脑默认连接方式
- Android(安卓)Debug Bridge(ADB) 技术实现
- Android平台开发-Android(安卓)keypad map-Android按键事件
- android中实现截屏的三种思路