当前位置:   article > 正文

Android USB Host的使用详解_android.hardware.usb.host

android.hardware.usb.host


      前段时间,我的小组开发一个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>
  • 1
  • 2

然后,拷贝到“/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>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    步骤四:非常重要,就是重启你的Android设备。
    【详细代码】
           事实上,做完上面的步骤,剩下的代码就非常地简单了,我之前搜索到的基本就是这些内容了。


          强烈建议大家参考SDK文档下面的google示例:ADBTest,他写的类非常有用,我下面的内容可以忽略过去。我后来就是参考了ADBTest之后,改装成一个通用类,可以让我的主机接多个USB-HID外设,实现同时异步收发。当然,你通过我下面的代码应该也能实现举一反三。


       先看看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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    注意看:上面的文件提到了一个文件“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>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

         我必须讲清楚,上面的设备是我测试用的,和大家手上的设备根本不一样,请自行查看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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

           特别是上面提到的一些按钮上的text,大家发挥自己想象力,我不想再贴上values下面的那些文件了。其中有些按钮根本没有用到。我是调试用的。
          
           这回,我们进入了直接的代码模块:MainActivity.java

    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);
            }
        } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179

             
    【调试】

          如果一切顺利的话,你接上你的USB设备后,android会弹出一个访问硬件的权限提示框,你要点击“确定“。

    转载:https://www.xuebuyuan.com/702736.html

    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/247013
    推荐阅读
    相关标签
      

    闽ICP备14008679号