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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章