【废话一段】
这段时间,我的小组正在开发一个Android主机的系统,这个系统需要外接USB的指纹机、读卡器、U盘,硬件已经有了,主机是一个开发板接口丰富,并且支持Android USB Host模式,外设自然不用说。
但是碰到了一个问题,驱动!本来这个项目是源于Windows的,外设全部是针对Windows而开发的,如果要用专门的驱动,那么开发Android本身就需要复杂的过程。后来经过硬件工程师的改造,我们将USB换成了HID模式,减轻开发难度。
经过一段时间搜索网上资料,关于Android USB Host的资料可以说非常少,不是少数,而是几乎雷同。我是百度+google,更换无数中英文关键字,最后我如愿完成自己的项目,和HID设备正常通讯了,并且识别了U盘。对于网络上中文资料的少而单一的现状,我决定自己写一篇文章,让同行们少走弯路。
我的代码参考了来自“开源中国”部分资料,如果有解决不了的,可以在那里查询。
【基础功能】
注意:本文的步骤,可能需要你具备Root的权限,否则有些操作可能会无法完成。强烈建议你先root设备。
步骤一:你必须确定你的Android设备支持USB Host,具体如何确定啊,还是看设备的说明书吧。如果支持,进入下一步骤。
步骤二:确定Android有没有开启USB Host的权限,必须是开启的才能通讯。首先用RE文件管理器(或者连接Eclipse时使用DDMS查看),反正要能进入以下目录:/system/etc/permissions。
你应该要能看到目录有一个“android.hardware.usb.host.xml”,一个“handheld_core_hardware.xml(手机)”或者“tablet_core_hardware.xml(平板)”,
如果看不到“android.hardware.usb.host.xml”,那么就用记事本写入以下代码,保存,然后PUSH或粘贴到/system/etc/permissions目录下。
步骤三:拷出“handheld_core_hardware.xml(手机)”或者“tablet_core_hardware.xml(平板)”文件,怎么操作?我是用Eclipse的DDMS中的File Explorer把文件pull出来的,还可以用其他方法。
打开文件,你应该可以看到
确定没有就补上去,比如我的是:
步骤四:以上步骤做完后,请重启你的设备。
【代码内容】
步骤一: 这里有个细节,就是【AndroidManifest.xml】文件中的权限,网上的多数文章,包括google的官方文档都只讲到其一,其实还有一个,留意代码。
先贴上【AndroidManifest.xml】
步骤二:有没有发现,我们还需要一个device_filte.xml文件,这个文件包含有你的USB设备的ProductID和VendorID,即我们常说的PID和VID,不知道这2个值的,请查Windows的设备管理器属性。记住:Windows用的是16进制表示的,你必须将它转为10进制,以下有说明。
添加步骤:在Eclipse项目中找到res结点,看有没有xml文件夹,如果没有就点右键--new---folder,新增文件夹名xml,然后在该xml文件夹下用同样的方法新增一个device_filte.xml文件,文件名一定不能错,内容如下:
怎么转16进制为10进制???用windows计算器啊,1.查看类型是科学型,2.选中16进制,3.输入值,4.再选择回10进制就可以了。
步骤三:布局文件其实应该是因人而异的,由于我刚做的时候是随意列出的,不建议大家仿照。我的文件名是activity_main.xml,你们的可能是main.xml,不一定要一模一样,只要啊Activity代码描述清楚就行。我的代码基本没用到多少控件,请适当过滤。
步骤四:好吧,我承认我比较啰嗦,到现在才进入步骤四的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 deviceList = manager.getDeviceList();
Log.i(TAG, "usb设备:" + String.valueOf(deviceList.size()));
Iterator deviceIterator = deviceList.values().iterator();
ArrayList USBDeviceList = new ArrayList(); // 存放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(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);
}
}
}
步骤五:我在步骤四的代码中包含有个类clsPublic,这个类是用来转换十六进制和字符串的,一般来说大家也不需要,但是考虑代码完整性,我也贴上来。这个类和MainActivity是在同一个包名下的文件clsPublic.java。
package com.example.usbmanager;
public class clsPublic {
// 整数到字节数组转换
public static byte[] int2bytes(int n) {
byte[] ab = new byte[4];
ab[0] = (byte) (0xff & n);
ab[1] = (byte) ((0xff00 & n) >> 8);
ab[2] = (byte) ((0xff0000 & n) >> 16);
ab[3] = (byte) ((0xff000000 & n) >> 24);
return ab;
}
// 字节数组到整数的转换
public static int bytes2int(byte b[]) {
int s = 0;
s = ((((b[0] & 0xff) << 8 | (b[1] & 0xff)) << 8) | (b[2] & 0xff)) << 8
| (b[3] & 0xff);
return s;
}
// 字节转换到字符
public static char byte2char(byte b) {
return (char) b;
}
private final static byte[] hex = "0123456789ABCDEF".getBytes();
private static int parse(char c) {
if (c >= 'a')
return (c - 'a' + 10) & 0x0f;
if (c >= 'A')
return (c - 'A' + 10) & 0x0f;
return (c - '0') & 0x0f;
}
// 从字节数组到十六进制字符串转换
public static String Bytes2HexString(byte[] b) {
byte[] buff = new byte[2 * b.length];
for (int i = 0; i < b.length; i++) {
buff[2 * i] = hex[(b[i] >> 4) & 0x0f];
buff[2 * i + 1] = hex[b[i] & 0x0f];
}
return new String(buff);
}
// 从十六进制字符串到字节数组转换
public static byte[] HexString2Bytes(String hexstr) {
byte[] b = new byte[hexstr.length() / 2];
int j = 0;
for (int i = 0; i < b.length; i++) {
char c0 = hexstr.charAt(j++);
char c1 = hexstr.charAt(j++);
b[i] = (byte) ((parse(c0) << 4) | parse(c1));
}
return b;
}
}
步骤六:
制作完成软件后,安装到设备上,或者直接用Eclipse调试运行。然后插入USB-HID设备,幸运的话,你会看到系统弹出一个打开方式的提示(我的设备是这样的,其他设备不知道是什么结果)。
转自:百度博客(已经关闭)