Android USB Host的使用详解

Android USB Host的使用详解

【废话一段】java

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

     可是碰到了一个问题,驱动!原本这个项目是源于Windows的,外设所有是针对Windows而开发的,若是要用专门的驱动,那么开发Android自己就须要复杂的过程。后来通过硬件工程师的改造,咱们将USB换成了HID模式,减轻开发难度。windows

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

    个人代码参考了来自“开源中国”部分资料,若是有解决不了的,能够在那里查询。网络


【基础功能】app

    注意:本文的步骤,可能须要你具有Root的权限,不然有些操做可能会没法完成。强烈建议你先root设备。ide

    步骤一:你必须肯定你的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目录下。

  

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

  步骤三:拷出“handheld_core_hardware.xml(手机)”或者“tablet_core_hardware.xml(平板)”文件,怎么操做?我是用Eclipse的DDMS中的File Explorer把文件pull出来的,还能够用其余方法。

         打开文件,你应该能够看到<permissions>结点下面有很多东西,检查有没有一段:

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


         肯定没有就补上去,好比个人是:

<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>

     步骤四:以上步骤作完后,请重启你的设备。

【代码内容】

          步骤一: 这里有个细节,就是【AndroidManifest.xml】文件中的权限,网上的多数文章,包括google的官方文档都只讲到其一,其实还有一个,留意代码。

     先贴上【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"/>
                                            
        </activity>
    </application>
                                          
</manifest>

    步骤二:有没有发现,咱们还须要一个device_filte.xml文件,这个文件包含有你的USB设备的ProductID和VendorID,即咱们常说的PID和VID,不知道这2个值的,请查Windows的设备管理器属性。记住:Windows用的是16进制表示的,你必须将它转为10进制,如下有说明。

       添加步骤:在Eclipse项目中找到res结点,看有没有xml文件夹,若是没有就点右键--new---folder,新增文件夹名xml,而后在该xml文件夹下用一样的方法新增一个device_filte.xml文件,文件名必定不能错,内容以下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 亿捷U盘,vid的16进制是090C,pid是1000,转10进制是分别是2316,4096 -->
    <usb-device vendor-id="2316" product-id="4096"/>
    <!-- M1读卡器 ,vid的16进制是0483,pid是5750,转10进制是分别是1155,22352-->
    <usb-device vendor-id="1155" product-id="22352"/>
</resources>

        怎么转16进制为10进制???用windows计算器啊,1.查看类型是科学型,2.选中16进制,3.输入值,4.再选择回10进制就能够了。



步骤三:布局文件其实应该是因人而异的,因为我刚作的时候是随意列出的,不建议你们仿照。个人文件名是activity_main.xml,大家的多是main.xml,不必定要如出一辙,只要啊Activity代码描述清楚就行。个人代码基本没用到多少控件,请适当过滤。

 

<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="usb简易调试工具" />
                          
    <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="发送内容"
        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="接收内容"
        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="接收" />
                          
    <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="发送" />
                          
    <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>

步骤四:好吧,我认可我比较啰嗦,到如今才进入步骤四的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);         }     }                             }

步骤五:我在步骤四的代码中包含有个类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设备,幸运的话,你会看到系统弹出一个打开方式的提示(个人设备是这样的,其余设备不知道是什么结果)。


转自:百度博客(已经关闭)