Android USB Host接入(USB OTG)

接入前先了解一下USB OTG的概念

USB OTG:USB On-The-Go通常缩写为USB OTG,是USB2.0规格的补充标准。它可使USB设备,例如播放器或手机,从USB周边设备变为USB主机,与其他USB设备连接通信。在正常情况下,这些支持OTG的USB设备和USB主机(如台式机或者手提电脑),仍然作为USB周边设备使用。

这段话是wiki对USB OTG的说明。

举一个简单的例子:如果你的安卓机可以通过一根OTG线直接读取U盘里的内容(有些U盘直接支持OTG输出不需要OTG线),这就是OTG功能了,它能允许你的手机像电脑一样直接读取U盘里的内容,现在的手机一般都支持OTG功能。

OTG线长这个样子,公头插进手机,母头插入U盘等外设:

otg.jpg

支持OTG的U盘长这个样子(电脑手机双用>.<):

otgU盘.jpg

有些文件管理器(ES文件浏览器等)直接支持OTG挂载功能,也就是将读取U盘功能完成了。

想要接入OTG功能还需要了解USB Host and Accessory的概念。

USB Host and Accessory

Android官网的介绍如下:
Android系统可以通过USB配件模式和USB主机模式支持各种各样的USB外设和Android USB配件(这些硬件需要先实现USB配件协议)。

在USB配件模式下,外部的USB硬件就相当于USB的主机端了,Android设备就是配件端。像机器人控制器,扩展坞,音乐设备,读卡器等这些配件都可以作为主机端。USB配件模式就给了不支持USB主机模式的Android设备和其它USB硬件交互的能力。Android USB配件必须支持Android配件交流协议,并且它们在被设计的时候就必须能够和Android设备协同工作。

在USB主机模式下,Android设备就成为了主机端,像数码相机,键盘鼠标和游戏手柄等就是附属设备了。那些为了各种应用和不同场景而设计的USB设备依然可以和那些能和正常设备沟通的Android应用交互。

图1是这两种模式的区别。


figure 1.png

这两种模式在Android3.1(API 12)及以上都是直接支持的,USB配件模式在Android2.3.4(API 10)上作为一个支持库也可以使用。设备制造商可以选择是否将这个库添加进他们的系统镜像中。

USB Host接入

Android Manifest 要求

  • 添加<uses-feature> 标签申明应用将使用 android.hardware.usb.host特性

  • 将minimum SDK设置为API 12及以上

  • 添加<intent-filter>和<meta-data> 以便USB设备插入时可以捕获你想要识别的设备并获取提示。
    将<intent-filter>中的action设为android.hardware.usb.action.USB_DEVICE_ATTACHED时,USB设备插入系统底部就会弹出一个提示框里边有声明了这个action的所有应用(这里就是一个隐式的intent),里边就有自己的应用了。
    <meta-data>会指向一个外部的xml文件,在这个xml资源文件里我们可以指定我们想要捕获的设备因为并不是所有设备我们都想检测到。我们可以通过在xml中声明vendor-id,product-id,class,subclass,protocol字段来过滤设备,如果不声明的话则会默认检测到所有接入的USB设备。

举个xml(device_filter.xml)的例子:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" />
</resources>

Manifest 的例子

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.onionpie.otgexplorer">
    <uses-feature android:name="android.hardware.usb.host"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

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

                <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                    android:resource="@xml/device_filter" />
            </intent-filter>
        </activity>
    </application>

</manifest>

申请权限

UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
    "com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);

使用广播接收权限申请结果和设备插入拔出的的消息

private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(ACTION_USB_PERMISSION)) {
                synchronized (this) {
                    UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if (usbDevice != null) {
                            mInfo = "";
                            collectDeviceInfo(usbDevice);
                            mInfoTextView.setText(String.format("权限获取成功,设备信息:%s", mInfo));
                            //读取usbDevice里的内容
                        }
                    } else {
                        mInfoTextView.setText("权限被拒绝了");
                        Log.v(LOGTAG, "permission is denied");
                    }
                }
            } else if (action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)) {
                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (usbDevice != null) {
                    //close connection
                    mInfoTextView.setText("与设备断开连接");
                }
            }else if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)){
                //当设备插入时执行具体操作
                Log.v(LOGTAG,"设备接入");
                mInfoTextView.setText("设备接入");
            }
        }
    };

获取设备

列举出插入的设备

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
    UsbDevice device = deviceIterator.next();
    //your code
}

读取设备具体的数据(这里比较繁琐可以使用这个库https://github.com/magnusja/libaums

private Byte[] bytes;
private static int TIMEOUT = 0;
private boolean forceClaim = true;
...

UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = mUsbManager.openDevice(device);
connection.claimInterface(intf, forceClaim);
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread(异步处理)

在结束读取时或者设备拔出时记得调用releaseInterface()和close()关闭UsbInterface和UsbConnection,可以通过广播监听设备拔出,前面已经贴了代码。

USB Host大致的流程就是这样,Usb Accessory的接入下次有机会再记录一下,自己写了个简单的demo(https://github.com/stansen/OTGDemo) ,可能和这里贴的代码不一样因为这里的代码是Android官网的,具体读取数据的内容并没有写,如果有需要可以使用这个库https://github.com/magnusja/libaums

这里有个开源的文件浏览器:https://github.com/1hakr/AnExplorer ,里边集成了USB Host功能,可以去看看,它也是使用了libaums这个库的。

参考:
https://developer.android.com/reference/android/hardware/usb/UsbDeviceConnection.html
https://developer.android.com/guide/topics/connectivity/usb/index.html

上图:

DSC_1037.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,082评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,231评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,047评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,977评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,893评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,976评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,605评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,888评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,906评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,732评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,513评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,980评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,027评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,232评论 2 339

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,199评论 25 707
  • 1.1PAD作为USB Device设备 USB Device的功能很丰富,其支持的协议越来越多包括:MTP、AD...
    android之子阅读 18,448评论 0 14
  • Android通过两种模式支持各种USB设备: USB accessory 和USB host。(Android ...
    wangdy12阅读 9,417评论 1 6
  • 网卡配置 这个刀片机有两个网卡,网卡1连内网,网卡0连外网(提供外部服务),网卡的配置方法自然是重写网卡配置文件啰...
    彬哲阅读 966评论 0 0
  • 生活真的太苦了 活着不易,想要活的好就更是困难。 看你想要什么了。 我送你首诗 阳光 雨露 饥渴的你
    beataL阅读 144评论 0 0