(转)android usb host

本文详细介绍如何在Android设备上实现USB Host功能,包括必要的权限配置、设备识别及HID设备通信过程。提供了完整的示例代码及调试指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【废话一段】

这段时间,我的小组正在开发一个Android主机的系统,这个系统需要外接USB的指纹机、读卡器、U盘,硬件已经有了,主机是一个开发板接口丰富,并且支持Android USB Host模式,外设自然不用说。

但是碰到了一个问题,驱动!本来这个项目是源于Windows的,外设全部是针对Windows而开发的,如果要用专门的驱动,那么开发Android本身就需要复杂的过程。后来经过硬件工程师的改造,我们将USB换成了HID模式,减轻开发难度。

经过一段时间搜索网上资料,关于Android USB Host的资料可以说非常少,不是少数,而是几乎雷同。我是百度+google,更换无数中英文关键字,最后我如愿完成自己的项目,和HID设备正常通讯了,并且识别了U盘。对于网络上中文资料的少而单一的现状,我决定自己写一篇文章,让同行们少走弯路。

我的代码参考了来自“开源中国”部分资料,如果有解决不了的,可以在那里查询。

【基础功能】

注意:本文的步骤,可能需要你具备Root的权限,否则有些操作可能会无法完成。强烈建议你先root设备。

步骤一:你必须确定你的Android设备支持USB Host,具体支持与否请参考自己的说明书。确定了才有必要看本文章。

步骤二:确认Android是否已经开放了USB访问权限,这一步非常重要。操作是:进入系统,找到目录“/system/etc/permissions”,可以用ES或者RE文件管理器进行操作。查看该目录下,是否有一个文件"android.hardware.usb.host.xml",如果没有,则自己创建一个同名文件,内容如下:

<permissions>

<feature name="android.hardware.usb.host"/>

</permissions>

然后,拷贝到“/system/etc/permissions”目录下。(可以用Eclipse的DDMS帮忙,push进去)

步骤三:其实呢,有了步骤三基本也就可以了,但是我自己也不是很确定,于是有了步骤四。继续检查目录“/system/etc/permissions”下,将其中的“handheld_core_hardware.xml (手机)或者 tablet_core_hardware.xml(平板)”拖出来,打开文件,看看<permissions>结点下面有没有下面这个结点:

<feature name="android.hardware.usb.host" />


如果没有,就自己补上一行,保存,并push进去替换原来的文件。比如我的文件内容是:

<?xml version="1.0" encoding="utf-8"?>

<permissions>

<feature name="android.hardware.camera" />

<feature name="android.hardware.location" />

<feature name="android.hardware.location.network" />

<feature name="android.hardware.sensor.compass" />

<feature name="android.hardware.sensor.accelerometer" />

<feature name="android.hardware.bluetooth" />

<feature name="android.hardware.touchscreen" />

<feature name="android.hardware.microphone" />

<feature name="android.hardware.screen.portrait" />

<feature name="android.hardware.screen.landscape" />

<feature name="android.hardware.usb.host" />

</permissions>


步骤四:非常重要,就是重启你的Android设备。
【详细代码】

事实上,做完上面的步骤,剩下的代码就非常地简单了,我之前搜索到的基本就是这些内容了。

先看看AndroidManifest.xml文件,为了写这篇文章,我特意做了大量注释:


<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.usbmanager"

android:versionCode="1"

android:versionName="1.0" >

<!-- 这个权限是必须有的,否则操作不了硬件,google的文档没有说出来,据说是因为有过滤器后自动获得,但是我的项目没这句话不成功。 -->

<uses-permission android:name="android.permission.HARDWARE_TEST" />

<!-- 这句话也是必须的 -->

<uses-feature android:name="android.hardware.usb.host" android:required="true"/>

<!-- SDK必须是12以上的,因为从 Android3.1开始,才正式支持USB Host -->

<uses-sdk

android:minSdkVersion="12"

android:targetSdkVersion="17" />



<application

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".MainActivity"

android:label="@string/app_name"

>

<intent-filter>

<action android:name="android.intent.action.MAIN" />



<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

<!-- 以下这个过滤器是要手工增加上,如果不增加也可以在代码中动态注册,不过我的代码是在这里注册 -->

<intent-filter>

<action

android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />

</intent-filter>

<!-- 以下这个meta-data是要手工增加上,他是用来过滤你的具体USB设备的,其中的device_filter是个xml文件 -->

<meta-data

android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"

android:resource="@xml/device_filter"/>

<!--

<intent-filter>

<action android:name="android.intent.action.VIEW"></action>

<category android:name="android.intent.category.DEFAULT"></category>

<data android:mimeType=""></data>

</intent-filter>

-->



</activity>

</application>



</manifest>

注意看:上面的文件提到了一个文件“device_filter”,他也是你能否成功的一个重要文件”device_filter.xml“,这个文件必须自己创建:

在项目工程中的res结点创建一个新的文件夹叫”xml“(不会操作????右键啊!),然后再在xml文件夹下创建一个xml,文件名就叫做“device_filter",内容如下:


<?xml version="1.0" encoding="utf-8"?>

<resources>

<!-- 以下内容的 vendor-id、product-id就是USB的vid和pid了,请大家自己在Windows中找到这个设备的属性,检查设备的VID_PID值-->

<!-- 还要说明一点的是:Windows设备属性中看到的ID值是16进制表示的,必须转换成10进制,并配置 。-->

<!-- 测试用的亿捷U盘,Windows中显示的VID是090C,转换为十进制是2316,其他的道理是一样的 -->

<usb-device vendor-id="2316" product-id="4096"/>

<!-- 测试用的M1读卡器 -->

<usb-device vendor-id="1155" product-id="22352"/>

<!-- USB-HUB -->

<usb-device vendor-id="1507" product-id="1544" />

</resources>

我必须讲清楚,上面的设备是我测试用的,和大家手上的设备根本不一样,请自行查看USB设备VID\PID然后转换。


我们再来看看布局文件activity_main.xml,这个布局是测试用的,非常随意,请大家根据自己的项目布局测试,不一定要用我这个,也不要问我为什么要这么设计布局,哥我只是玩玩而已,不要那么认真嘛~~~~~


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context=".MainActivity" >



<TextView

android:id="@+id/tvtitle"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/title" />



<EditText

android:id="@+id/etxsend"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/tvtitle"

android:layout_below="@+id/tvtitle"

android:ems="10"

android:hint="@string/sendhint"

android:visibility="invisible" />



<EditText

android:id="@+id/etxreceive"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/btsend"

android:layout_below="@+id/btsend"

android:layout_marginTop="37dp"

android:ems="10"

android:hint="@string/receivehint"

android:visibility="invisible" />



<Button

android:id="@+id/btreceive"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/etxreceive"

android:layout_below="@+id/etxreceive"

android:text="@string/btreceive" />



<Button

android:id="@+id/btsend"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/etxsend"

android:layout_below="@+id/etxsend"

android:layout_marginTop="16dp"

android:text="@string/btsend" />



<ListView

android:id="@+id/lsv1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/btreceive"

android:layout_below="@+id/btreceive" >

</ListView>



</RelativeLayout>

特别是上面提到的一些按钮上的text,大家发挥自己想象力,我不想再贴上values下面的那些文件了。



这回,我们进入了直接的代码模块:MainActivity.java


package com.example.usbmanager;



import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;



import android.os.Bundle;

import android.R.string;

import android.app.Activity;

import android.app.PendingIntent;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.DialogInterface;

import android.content.Intent;

import android.content.IntentFilter;

import android.database.DataSetObserver;

import android.hardware.usb.UsbConstants;

import android.hardware.usb.UsbDevice;

import android.hardware.usb.UsbDeviceConnection;

import android.hardware.usb.UsbEndpoint;

import android.hardware.usb.UsbInterface;

import android.hardware.usb.UsbManager;

import android.util.Log;

import android.view.View.OnClickListener;

import android.view.Gravity;

import android.view.Menu;

import android.view.View;

import android.view.ViewGroup;

import android.widget.ArrayAdapter;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ListAdapter;

import android.widget.ListView;

import android.widget.Toast;



public class MainActivity extends Activity {

private static final String TAG = "MainActivity"; //记录标识

private Button btsend; //发送按钮

private UsbManager manager; //USB管理器

private UsbDevice mUsbDevice; //找到的USB设备

private ListView lsv1; //显示USB信息的

private UsbInterface mInterface;

private UsbDeviceConnection mDeviceConnection;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btsend = (Button) findViewById(R.id.btsend);



btsend.setOnClickListener(btsendListener);



lsv1 = (ListView) findViewById(R.id.lsv1);

// 获取USB设备

manager = (UsbManager) getSystemService(Context.USB_SERVICE);

if (manager == null) {

return;

} else {

Log.i(TAG, "usb设备:" + String.valueOf(manager.toString()));

}

HashMap<String, UsbDevice> deviceList = manager.getDeviceList();

Log.i(TAG, "usb设备:" + String.valueOf(deviceList.size()));

Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();

ArrayList<String> USBDeviceList = new ArrayList<String>(); // 存放USB设备的数量

while (deviceIterator.hasNext()) {

UsbDevice device = deviceIterator.next();



USBDeviceList.add(String.valueOf(device.getVendorId()));

USBDeviceList.add(String.valueOf(device.getProductId()));



// 在这里添加处理设备的代码

if (device.getVendorId() == 1155 && device.getProductId() == 22352) {

mUsbDevice = device;

Log.i(TAG, "找到设备");

}

}

// 创建一个ArrayAdapter

lsv1.setAdapter(new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1, USBDeviceList));

findIntfAndEpt();



}



private byte[] Sendbytes; //发送信息字节

private byte[] Receiveytes; //接收信息字节

private OnClickListener btsendListener = new OnClickListener() {

int ret = -100;

@Override

public void onClick(View v) {

/*

* 请注意,本模块通信的内容使用的协议是HID读卡器协议,不会和大家手上的设备一样

* 请大家在测试时参考自己手上的设备资料,严格按照HID标准执行发送和接收数据

* 我的范例使用的设备是广州微云电子的WY-M1RW-01非接触式读卡器,outputreport是64,因此我发送的字节长度是64

* 我发送的字节内容是要求读卡器蜂鸣器响两短一长

*/

String testString = "90000CB20301F401F401F401F407D447FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";

Sendbytes = clsPublic.HexString2Bytes(testString);



// 1,发送准备命令

ret = mDeviceConnection.bulkTransfer(epOut, Sendbytes, Sendbytes.length, 5000);

Log.i(TAG,"已经发送!");



// 2,接收发送成功信息

Receiveytes=new byte[64]; //这里的64是设备定义的,不是我随便乱写,大家要根据设备而定

ret = mDeviceConnection.bulkTransfer(epIn, Receiveytes, Receiveytes.length, 10000);

Log.i(TAG,"接收返回值:" + String.valueOf(ret));

if(ret != 64) {

DisplayToast("接收返回值"+String.valueOf(ret));

return;

}

else {

//查看返回值

DisplayToast(clsPublic.Bytes2HexString(Receiveytes));

Log.i(TAG,clsPublic.Bytes2HexString(Receiveytes));

}

}

};



// 显示提示的函数,这样可以省事,哈哈

public void DisplayToast(CharSequence str) {

Toast toast = Toast.makeText(this, str, Toast.LENGTH_LONG);

// 设置Toast显示的位置

toast.setGravity(Gravity.TOP, 0, 200);

// 显示Toast

toast.show();

}



// 寻找接口和分配结点

private void findIntfAndEpt() {

if (mUsbDevice == null) {

Log.i(TAG,"没有找到设备");

return;

}

for (int i = 0; i < mUsbDevice.getInterfaceCount();) {

// 获取设备接口,一般都是一个接口,你可以打印getInterfaceCount()方法查看接

// 口的个数,在这个接口上有两个端点,OUT 和 IN

UsbInterface intf = mUsbDevice.getInterface(i);

Log.d(TAG, i + " " + intf);

mInterface = intf;

break;

}



if (mInterface != null) {

UsbDeviceConnection connection = null;

// 判断是否有权限

if(manager.hasPermission(mUsbDevice)) {

// 打开设备,获取 UsbDeviceConnection 对象,连接设备,用于后面的通讯

connection = manager.openDevice(mUsbDevice);

if (connection == null) {

return;

}

if (connection.claimInterface(mInterface, true)) {

Log.i(TAG,"找到接口");

mDeviceConnection = connection;

//用UsbDeviceConnection 与 UsbInterface 进行端点设置和通讯

getEndpoint(mDeviceConnection,mInterface);

} else {

connection.close();

}

} else {

Log.i(TAG,"没有权限");

}

}

else {

Log.i(TAG,"没有找到接口");

}

}





private UsbEndpoint epOut;

private UsbEndpoint epIn;

//用UsbDeviceConnection 与 UsbInterface 进行端点设置和通讯

private void getEndpoint(UsbDeviceConnection connection, UsbInterface intf) {

if (intf.getEndpoint(1) != null) {

epOut = intf.getEndpoint(1);

}

if (intf.getEndpoint(0) != null) {

epIn = intf.getEndpoint(0);

}

}





}


好吧!已经把该说的代码都说了,我必须吐槽一些百度空间,我写文章很容易吗?为什么我发文章后根本无法查看,老是404,我都已经发消息给百度了,并且我告知他们说,是因为我上传的图片没有正常解析造成的,只要他们将我的文章中图片链接修正即可,结果他们回复我说”尊敬的百度用户:您好!首先非常感谢您对百度产品的使用与支持!对于您所投诉的内容,希望如下答案可以帮助到您。请您确认您的网络状态良好,尝试清空浏览器缓存、cookie,然后刷新页面重试。再次感谢您对百度的关注、支持与理解!“。

这已经是我第二次碰到百度简直是推脱责任的回复(我一共也就反映过2次),比163的客服差不知道多少,由此我总结一下:百度后台的技术员应该也就那样。

反正,你现在看得到的这篇文章,是我第二次用键盘敲出来的。


【调试】

调试的内容应该我不需要讲了,Eclipse会帮你搞定的。


转载网址:http://hi.baidu.com/intel88888/item/9a194171438dd9356dc37ca7
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值