Fork me on GitHub

Android: USB

Android通过两种模式支持各种 USB 外设和 Android USB 附件(实现Android附件协议的硬件):USB附件和USB主机。USB开发需 Android 3.1(API级别12)以上。由于本人工作中主要用到了主机模式,所以本文的侧重点在主机模式开发,该模式需要打开OTG功能。

一、调试

在使用非暴露多个USB接口Android开发板时,Android开发板USB口需要与外设连接,PC和Android开发版就需要通过无线局域网来建立连接了,方法如下:

1、保证Android开发板与PC端连接在同一个局域网内;

2、设定设备tcp连接端口:adb tcpip 5555;

3、建立连接:adb connect 设备IP:5555;

4、断开连接:adb disconnect 设备IP:5555。

二、AndroidManifest文件配置

uses-feature申明这个软件需要USB功能,申明后Google Play会把不满足的设备过滤掉,一般用USB的都是定制开发。

<uses-feature
  android:name="android.hardware.usb.host"
  android:required="true" />

要求最低api 为12。

如果希望应用程序连接指定的USB设备时被通知,需指定和元素对用android.hardware.usb.action.USB_DEVICE_ATTACHED。该元素指向声名识别有关要检测的设备信息的外部XML资源。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"><uses-feature
  android:name="android.hardware.usb.host"
  android:required="true" />
<application
...>
<activity
  android:name=".MainActivity">
<intent-filter>
  <action android:name="android.intent.action.MAIN" />
  <action android:name="android.intent.action.VIEW" />
​
  <category android:name="android.intent.category.LAUNCHER" />
</intent-filter><meta-data
  android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
  android:resource="@xml/device_filter" /><intent-filter>
<!--USB接入的弹框会启动该页面-->
  <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
</activity>
...
</application></manifest>

在XML资源文件中,声名要过滤的元素。以下列表描述了属性。通常,如果要过滤特定设备并使用类、子类和协议,如果要过滤一组USB设备,请使用供应商ID(vendor-id)和产品ID(product-id),在开发中这些过滤ID一般在文档中定义。将资源文件保存在res/xml目录中。资源文件名必须与在AndroidMenifest中对应。例如 device_filter:

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

配置好清单文件后当用户连接与设备过滤器匹配的设备时,系统会向他们显示一个对话框,询问他们是否要启动应用程序。如果用户接受,则应用程序将自动具有访问设备的权限,直到设备断开连接。如果给了默认,那么这个 USB 设备插入后会自动启动这个 Activity。

三、USB 设备的连接和使用

在清单文件中配置好以后我们直接进入 Java 代码环节

1.Android 中的 USB

Android 3.1(API级别12)以上原生提供了 USB 开发的 API,在android.hardware.usb包下提供了开发的相关类。

Class 说明
UsbManager 获得 USB 管理器,与连接的 USB 设备通信。
UsbDevice USB 设备的抽象,每个UsbDevice 都代表一个 USB 设备。
UsbInterface 定义了设备的功能集,一个 UsbDevice 可能包含一个或多个UsbInterface,每个 Interface 都是独立的。
UsbEndpoint UsbEndpoint 是 interface 的通信通道。
UsbDeviceConnection host 与 device 建立的连接,并在 endpoint 传输数据。
UsbRequest USB 请求包。
UsbConstants USB 常量的定义

2.USB 设备的插入

Android 系统中,USB 设备的插入和拔出是以系统广播的形式发送的,我们只要注册监听这个广播就好

public class USBReceiver extends BroadcastReceiver {
public static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
​
@Override
public void onReceive(Context context, Intent intent) {
  String action = intent.getAction();
  if (ACTION_USB_PERMISSION.equals(action)) {
    // 获取权限结果的广播
    synchronized (this) {
      UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
      if (device != null) {
        //call method to set up device communication
        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
          Log.e("USBReceiver", "获取权限成功:" + device.getDeviceName());
        } else {
          Log.e("USBReceiver", "获取权限失败:" + device.getDeviceName());
        }
      }
    }
  }else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
    // 有新的设备插入了,在这里一般会判断这个设备是不是我们想要的,是的话就去请求权限
  }else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
    // 有设备拔出了
  }
}
}

3.获取 UsbManager

usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);

4.获取 USB 设备列表

public List<UsbDevice> getDeviceList() {
  HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
  Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
  List<UsbDevice> usbDevices = new ArrayList<>();
  while (deviceIterator.hasNext()) {
    UsbDevice device = deviceIterator.next();
    usbDevices.add(device);
    Log.e("USBUtil", "getDeviceList: " + device.getDeviceName());
  }
  return usbDevices;
}

5.获取特定的设备

/**
* @param vendorId 厂商ID
* @param productId 产品ID
* @return device
*/
public UsbDevice getUsbDevice(int vendorId, int productId) {
  HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
  Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
  while (deviceIterator.hasNext()) {
    UsbDevice device = deviceIterator.next();
    if (device.getVendorId() == vendorId && device.getProductId() == productId) {
      Log.e("USBUtil", "getDeviceList: " + device.getDeviceName());
      return device;
    }
  }
  Toast.makeText(context, "没有对应的设备", Toast.LENGTH_SHORT).show();
  return null;
}

6.申请 USB 设备使用权限

安卓系统对 USB 设备的使用需要得到相应的权限,这个权限要用户手动授予,或插入设备时应用到你的应用中。在使用 USB 设备前首先我们要确认一下上一节中的device是否已经获得权限,如果没有就要主动申请权限:

/**
* 判断对应 USB 设备是否有权限
*/
public boolean hasPermission(UsbDevice device) {
  return usbManager.hasPermission(device);
}
​
/**
* 请求获取指定 USB 设备的权限
*/
public void requestPermission(UsbDevice device) {
  if (device != null) {
    if (usbManager.hasPermission(device)) {
      Toast.makeText(context, "已经获取到权限", Toast.LENGTH_SHORT).show();
    } else {
      if (mPermissionIntent != null) {
        usbManager.requestPermission(device, mPermissionIntent);
        Toast.makeText(context, "请求USB权限", Toast.LENGTH_SHORT).show();
      } else {
        Toast.makeText(context, "请注册USB广播", Toast.LENGTH_LONG).show();
      }
    }
  }
}

7.通信

与 USB 设备的通信可以是同步的也可以是异步的。无论哪种情况,你都应该创建一个新线程来执行所有数据传输,避免阻塞UI线程。

第一步,打开通信端口

public boolean openPort(UsbDevice device) {
  //获取设备接口,一般只有一个,多个的自己研究去
  usbInterface = device.getInterface(0);

  // 判断是否有权限
  if (hasPermission(device)) {
    // 打开设备,获取 UsbDeviceConnection 对象,连接设备,用于后面的通讯
    usbConnection = usbManager.openDevice(device);

    if (usbConnection == null) {
      return false;
    }
    if (usbConnection.claimInterface(usbInterface, true)) {
      Toast.makeText(Utils.getContext(), "找到 USB 设备接口", Toast.LENGTH_SHORT).show();
    } else {
      usbConnection.close();
      Toast.makeText(Utils.getContext(), "没有找到 USB 设备接口", Toast.LENGTH_SHORT).show();
      return false;
    }
  } else {
    Toast.makeText(Utils.getContext(), "没有 USB 权限", Toast.LENGTH_SHORT).show();
    return false;
  }

  //获取接口上的两个端点,分别对应 OUT 和 IN
  for (int i = 0; i < usbInterface.getEndpointCount(); ++i) {
    UsbEndpoint end = usbInterface.getEndpoint(i);
    if (end.getDirection() == UsbConstants.USB_DIR_IN) {
      usbEndpointIn = end;
    } else {
      usbEndpointOut = end;
    }
  }
  return true;
}

第二步,发送数据

usbConnection.bulkTransfer(usbEndpointOut, bytes, bytes.length, 500);

 参考:https://blog.csdn.net/weixin_41935055/article/details/130817568

posted @ 2023-09-19 19:05  AnimalRabbit  阅读(230)  评论(0编辑  收藏  举报