1:简介:
自Android3.1(API Level 12)版本开始,Android系统直接支持USB配件(Accessory Mode)和USB主机(Host Mode)两种模式支持各种USB外围设备和Android USB配件(实现Android配件协议的硬件)。
注意:以上关于Android设备支持USB主机模式和从机模式的支持方式只是软支持,另外还须有硬件支持,并且硬件对两种模式的支持具有决定性。
2:Android USB Host Mode(USB 主机模式)
在Android USB Host Mode模式下,Android设备充当主机,设备包括数码相机、键盘、鼠标和游戏控制器等。针对各类应用和环境设计的USB设备仍可与能够与设备正常通信的Android应用互动。
在USB主机模式下,我们的Android设备必须支持以下条件:
1、Android系统版本3.1(API Level 12)及以上。在概述中我们也提到,自Android3.1(API Level 12)开始才提供Android USB Host Mode的支持。
2、Android设备须支持OTG功能。我们的智能手机和相机等移动设备本身是无法像PC那样直接充当USB Host为总线供电的,而OTG正是为解决此类问题而生,目前主流的Android手机和平板都已添加OTG模块。在主机模式下,我们将Android设备上的USB主机模块又称为USB嵌入式主机(Embedded Host 简称 EH)。EH无法像PC上的USB主机一样,为接入总线的未识别外围设备加载驱动程序,所以EH设备提前在系统中对其目标外围设备列表TPL(Target Peripheral List)进行了定义,在这些外围USB设备中大部分是HID设备(Human Interface Device,如游戏手柄)、BOMS设备(Bulk Only Mass Storage,如读卡器、U盘)和CDC设备(Communication Device Class,USB通信设备类,如打印机、相机),其驱动程序已存在于Android平台的系统中(Linux Kernel),Android设备可以直接与这些设备直接通信。
3、支持AOA协议。AOA协议(Android Open Accessory Protocol,Android开发配件协议)是Google公司推出的用于实现Android设备与外围设备之间进行USB通信的协议,该协议拓展了Android设备USB接口的功能,为基于Android系统的智能设备应用于设备控制和数据采集领域提供了条件。关于AOA协议的固件源码烧写于硬件中,我们有时也把这项称作为Android设备的硬件支持。(该条件仅在接入Android设备外围设备是另一台Android设备时需要,当接入像U盘或打印机时,可不需要AOA协议的支持)
4、系统features.xml文件中提供了关于<uses-featureandroid:name="android.hardware.usb.host"/> 的定义。在使用Android USB Host Mode开发时,我们会在应用程序的AndroidMainifast.xml文件中指定<uses-featureandroid:name="android.hardware.usb.host"/> 用来对不支持Android USB Host Mode的设备进行过滤,Android系统版本3.1及以上默认支持,但是不排除设备商会对原生的Android系统进行裁剪,可能会裁剪掉features.xml文件中关于<uses-featureandroid:name="android.hardware.usb.host"/>的定义,也就是说当应用程序安装时来扫描系统的硬件支持feature时,发现没有关于<uses-featureandroid:name="android.hardware.usb.host"/>的定义,认为该设备不支持Android USB Host Mode,也就会提示你无法安装了。
3、Android USB Accessory Mode(USB 从机模式)
在Android USB Accessory Mode模式下,外部USB硬件充当USB主机,配件包括机器人控制器、扩展坞、音乐设备、自助服务终端、读卡器等。不具备主机功能的Android设备就能够与USB硬件互动。Android USB配件必须设计为与Android设备兼容,并且必须遵守Android配件通信协议。
在USB 从机模式下,我们的Android设备必须支持以下条件:
1、Android系统版本2.3.4(API Level 10)及以上。在概述中我们也提到,自Android3.1(API Level 12)开始才直接提供对Android USB Accessory Mode的支持,同时Google提供的配件开发工具包ADK(Accessory Development Kit)提供了Android设备与Android配件通过USB通信的API,该ADK包能够向后兼容至Android2.3.4系统版本。(Android 3.1及以上的系统之所以能够直接支持也是因为系统直接封装了ADK的API)
2、必须支持AOA协议。在Android USB Accessory Mode中,Android设备必须支持AOA协议,因为当Android设备以从机的方式接入Android配件时,Android配件会通过AOA协议检测并初始化Android设备的USB通信的环境和启动Android设备的USB从机模式。
3、系统features.xml文件中提供了关于<uses-featureandroid:name="android.hardware.usb.accessory"/> 的定义。关于该条件与Android USB Host Mode中的原因一致,这里不再赘述。
4:Android USB Host Mode 通信开发指南:
4-1:USB设备管理器在不同版本系统中的获取方式不同:
Android 2.3.4版本:UsbManager manager=UsbManager.getInstance(this);
Android 3.1版本:UsbManager manager=(UsbManager)getSystemService(Context.USB_SERVICE);
4-2:配置AndroidManifest.xml:
1).添加<uses-feature>元素来声明您的应用使用android.hardware.usb.host功能;
2).将应用的最低SDK设置为API级别 12 或更高级别。USB主机API在更早的API级别中不存在。
3).如果您希望应用接收有关连接的 USB 设备的通知,请为主 Activity 中的 android.hardware.usb.action.USB_DEVICE_ATTACHED Intent 指定 <intent-filter> 和 <meta-data> 元素对。<meta-data> 元素指向外部 XML 资源文件,用于声明有关要检测的设备的标识信息。在 XML 资源文件中,为要过滤的 USB 设备声明 <usb-device> 元素。下表介绍了 <usb-device> 的属性。一般来说,如果您想过滤某个特定设备,请使用供应商 ID 和产品 ID;如果您想过滤一组 USB 设备(例如大容量存储设备或数码相机),请使用类、子类和协议。您可以指定所有这些属性,也可以不指定任何属性。如果不指定任何属性,则会与每个 USB 设备进行匹配,因此只在应用需要时才这样做:vendor-id、product-id、class、subclass、protocol(设备或接口)如下图:
4-3:USB通信实现代码封装到UsbHidHelper类中:
/**
* Created by 631934797 on 2021/3/3
*/
public class UsbHidHelper {
private static final StringTAG = UsbHidHelper.class.getCanonicalName();
private UsbManagermUsbManager;
private UsbDeviceConnectionmUsbDeviceConnection;
private UsbEndpointmUsbEndpointOut;
private UsbEndpointmUsbEndpointIn;
private UsbInterfacemUsbInterface;
private static UsbHidHelpermInstance =null;
private static ContextmContext;
private boolean mToggle =true;
private boolean isConnect =false;
private ExecutorServicemThreadPool;
private byte[]recvBuffer =new byte[1024];
private int mVendorID ;
private int mProductID ;
private int mFindCont =3;
private final StringUSB_PERMISSION ="roy-lee.usb.permission";
private PendingIntentmPrtPermissionIntent; //获取外设权限的意图
/**
* 获取UsbHidHelper对象
* @param context
* @return
*/
public static UsbHidHelper getInstance(Context context) {
if(mInstance ==null) {
mInstance =new UsbHidHelper();
}
mContext = context;
return mInstance;
}
/**
* 初始化 USB设备
*
* @param vendorID
* @param productID
*/
public void initUsb_Hid(int vendorID, int productID){
mVendorID = vendorID;
mProductID = productID;
// init UsbManager
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
// 初始化线程池
mThreadPool = Executors.newFixedThreadPool(5);
// 注册usb广播
registerReceiver();
// 查找USB设备
findUsbDevice();
}
/**
* 动态注册usb广播,拔插动作,注册动作
* */
private void registerReceiver(){
//注册在此service下的receiver的监听的action
IntentFilter intentFilter =new IntentFilter();
intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
intentFilter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
intentFilter.addAction(USB_PERMISSION);
mContext.registerReceiver(usbReceiver, intentFilter);//注册receiver
//通知监听外设权限注册状态
//PendingIntent:连接外设的intent
//ask permission
mPrtPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(USB_PERMISSION), 0);
}
private BroadcastReceiverusbReceiver =new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent ==null) {
return;
}
String action = intent.getAction();
switch (action){
// USB注册动作
case USB_PERMISSION:
synchronized (this) {
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
UsbDevice parcelableExtra = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (parcelableExtra !=null) {
// 连接设备
connectDevice(parcelableExtra);
}else {
Log.e(TAG,"usb device suddenly disappera.");
Log.e(TAG,"...USB外设意外消失...");
}
}else {
Log.e(TAG,"usb permission granted fail.");
Log.e(TAG,"...USB权限注册失败...");
mFindCont--;
if (mFindCont >0){
findUsbDevice();
}
}
}
break;
// USB插入动作
case UsbManager.ACTION_USB_ACCESSORY_ATTACHED:
case UsbManager.ACTION_USB_DEVICE_ATTACHED:
Log.i(TAG,"...USB 插入...");
mFindCont =3;
findUsbDevice();
break;
// USB拔出动作
case UsbManager.ACTION_USB_DEVICE_DETACHED:
Log.e(TAG,"...USB 已被拔出...");
mToggle =true;
isConnect =false;
break;
}
}
};
/**
* 查找设备
*/
private boolean findUsbDevice(){
mThreadPool.execute(new Runnable() {
@Override
public void run() {
// while (mToggle && mFindCont > 0) {
Log.d(TAG, "...查找USB设备...");
HashMap deviceList =mUsbManager.getDeviceList();
Collection values = deviceList.values();
if (!values.isEmpty()) {
for (UsbDevice usbDevice : values) {
// 输出设备信息
Log.e(TAG,"mVendorID : "+mVendorID +" mProductID : " +mProductID);
Log.d(TAG, "设备ID: vid = " + String.format("%x", usbDevice.getVendorId()) +" , pid = " + String.format("%x", usbDevice.getProductId()));
int vendorId = usbDevice.getVendorId();
int productId = usbDevice.getProductId();
if (vendorId ==mVendorID && productId ==mProductID) {
Log.d(TAG, "...枚举SUB设备成功...");
// 获取权限
if (mUsbManager.hasPermission(usbDevice)) {
// 建立连接
connectDevice(usbDevice);
}else {
Log.e(TAG,"...申请USB权限失败...");
mUsbManager.requestPermission(usbDevice, mPrtPermissionIntent);
}
}
}
}else {
// TODO 没有USB设备
Log.e(TAG,"...没有USB设备...");
}
// SystemClock.sleep(10000);
// mFindCont--;
// }
}
});
return isConnect;
}
/**
* 连接设备
* @param usbDevice
*/
private void connectDevice(UsbDevice usbDevice){
Log.e(TAG,"...申请USB权限成功...");
Log.e(TAG,"UsbDevice :" + usbDevice);
// 打开设备
UsbDeviceConnection conn=mUsbManager.openDevice(usbDevice);
if (conn !=null) {
mUsbInterface = usbDevice.getInterface(0);
if (conn.claimInterface(mUsbInterface, true)){
mUsbDeviceConnection = conn;
int endpointCount =mUsbInterface.getEndpointCount();
for (int i =0; i < endpointCount; i++) {
UsbEndpoint usbEndpoint =mUsbInterface.getEndpoint(i);
Log.e(TAG,"Type: "+ usbEndpoint.getType());
Log.e(TAG,"Direction: "+ usbEndpoint.getDirection());
// TODO USB 4种传输模式,根据自己实际通信需求自行更改
if (usbEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_INT) {
if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
mUsbEndpointOut = usbEndpoint;
}else if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
mUsbEndpointIn = usbEndpoint;
}
}
}
if (mUsbEndpointOut !=null &&mUsbEndpointIn !=null) {
// TODO 连接USB设置成功
Log.i(TAG,"connected success");
mToggle =false;
isConnect =true;
onLoopSendData();
}
}
}
}
/**
* 发送HID数据
*
* @param messageContent
* @return
*/
public boolean sendHidData(String messageContent){
return sendHidData(messageContent.getBytes());
}
public boolean sendHidData(final byte[] contentBytes){
final boolean[] states = {false};
mThreadPool.execute(new Runnable() {
@Override
public void run() {
if (mUsbDeviceConnection !=null &&mUsbEndpointOut !=null) {
/**
* 发送数据的地方 , 只接受byte数据类型的数据
*/
int i =mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut, contentBytes, contentBytes.length, 2000);
if (i >0) {
Log.i(TAG,"发 ==> ◇: " + HexDump.byteTo16String(contentBytes) +"\r\n");
states[0] =true;
}else {
Log.e(TAG,"...数据发送失败...");
}
}else {
Log.e(TAG,"...请先连接USB...");
}
}
});
return states[0];
}
/**
* 接收HID数据
*
* @param callback
*/
public void receivedHidNewData(final HidDataCallback callback){
mThreadPool.execute(new Runnable() {
@Override
public void run() {
while (true) {
/**
* 循环接受数据的地方 , 只接受byte数据类型的数据
*/
if (mUsbDeviceConnection !=null &&mUsbEndpointIn !=null &&isConnect) {
int i =mUsbDeviceConnection.bulkTransfer(mUsbEndpointIn, recvBuffer, 64, 1000);
if (i >0) {
byte[] subArray = HexDump.getSubArray(recvBuffer, 0, i);
Log.i(TAG,"收 <== ◆: "+HexDump.byteTo16String(subArray) +"\r\n");
if(callback !=null)
callback.onReceiveHidData(subArray);
}
}
SystemClock.sleep(10);
}
}
});
}
}