USB HOST-Android 6.0開發者文檔

以下內容翻譯自USB Host

一、usb host

當你的Android設備處於usb host模式時,它作爲usb host,驅動總線,可以枚舉已連接的usb設備。usb host模式從Android 3.1開始支持。

二、API簡介

下表描述了android.hardware.usb包中的usb host相關API:

描述
UsbManager 包含枚舉、通信接口
UsbDevice 表示一個已連接設備,包含訪問已註冊信息、接口、endpoint的方法
UsbInterface 表示usb設備的接口,定義了此設備支持的功能集合。一個設備可以有多個接口
UsbEndpoint 表示接口endpoint,即接口的通信通道。一個接口可以有多個endpoint,通常有輸入、輸出兩個endpoint以支持雙向通信
UsbDeviceConnection 表示一個連接,可以在endpoint上面傳輸數據。這個類中包含同步/異步讀寫數據的方法
UsbRequest 表示通過UsbDeviceConnection進行的異步請求
UsbConstants 一些usb相關常量

大多數情況下,當你與usb設備通信時,以上的類都會被用到(UsbRequest只在進行異步通信時需要)。通常情況下,建立連接的順序爲:通過UsbManager搜索目標UsbDevice,然後尋找適當的UsbInterface和對應的UsbEndpoint,找到endpoint以後,就可以打開一個UsbDeviceConnection進行通信了。

三、Android manifest設置

以下爲在使用usb host相關API前需要在manifest文件中添加的內容:

  • 由於不是所有的Android設備都支持usb host API,所以需要用<uses-feature>指定需要android.hardware.usb.host
  • 設置最小SDK爲API 12。usb host API不支持更早的系統版本
  • 如果你希望你的應用可以在usb設備接入時收到通知,可以爲activity指定<intent-filter><meta-data>。其中filter的action爲android.hardware.usb.action.USB_DEVICE_ATTACHED<meta-data>指定一個外部XML文件,此文件指定你關注的設備信息。
    在這個XML文件中,需要添加<usb-device>標籤。下面描述的是<usb-device>支持的屬性。通常情況下,你可以使用vendor-id和product-id指定你想要的某個設備,使用class、subclass、protocol指定你想要的一組設備(如存儲設備或數碼相機)。你可以指定下列屬性中的一個或多個。不指定任何屬性將會匹配到所有usb設備。

    • vendor-id
    • product-id
    • class
    • subclass
    • protocol

    將此XML文件保存在res/xml路徑。XML文件的名稱必須與<meta-data>中指定的(沒有.xml後綴)一致

3.1 示例

下面是manifest文件的示例:

<manifest ...>
    <uses-feature android:name="android.hardware.usb.host" />
    <uses-sdk android:minSdkVersion="12" />
    ...
    <application>
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
            </intent-filter>
            <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
        </activity>
    </application>
</manifest>

在這個例子中,下面的xml文件將保存爲res/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>

四、與設備交互

當有usb設備連接到Android設備時,Android系統會判斷你的應用是否對此usb設備感興趣。如果是的話,你可以跟此usb設備通信。要實現此功能,你需要:

  1. 當usb設備接入時通過intent filter發現它,或者直接枚舉已連接的usb設備
  2. 向用戶申請權限
  3. 使用適當的endpoint接口與usb設備通信

4.1 發現設備

你可以通過兩種方式發現usb設備,一是用intent filter在usb接入時由系統通知,一是用枚舉接口直接獲取已連接設備。如果你希望應用可以自動獲取目標設備,可以使用intent filter的方式。如果你希望獲取所有已連接設備列表,可以使用枚舉接口。

4.1.1 使用intent filter

你可以在intent filter中指定action爲android.hardware.usb.action.USB_DEVICE_ATTACHED來發現某個USB設備。在這個intent filter中,你還需要指定一個resource文件,此文件可以說明你要的usb設備的屬性,如product id和vendor id。當用戶將一個符合你要求的設備連接到Android設備時,系統會顯示一個對話框,由用戶決定是否啓動你的應用處理此連接。如果用戶選擇了是,那麼你的應用會自動獲取訪問設備的權限,直到設備連接斷開。

下面是intent filter的示例:

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

下面是指定你想要的usb設備的resource文件的示例:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="1234" product-id="5678" />
</resources>

在你的activity中,你可以通過以下代碼獲取UsbDevice:

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

4.1.2 枚舉設備

如果你的應用是對當前連接的所有usb設備感興趣,你可以使用枚舉接口。使用getDeviceList()方法可以獲取當前連接的usb設備的哈希表。這個哈希表的鍵值是usb設備的名稱。

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...  
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");

你也可以用iterator的方式訪問設備:

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
}

4.2 獲取訪問設備的權限

在與usb設備通信前,你的應用需要獲取相關權限。

注意:如果你的應用使用intent filter方式發現usb設備,那麼在用戶允許你的應用處理該intent時你的應用會自動獲取相關權限。如果用戶不允許,那麼你在與usb設備建立連接前必須顯式的申請權限

在某些情況下顯式的申請權限是必要的,比如你的應用在枚舉所有已連接設備時。你必須在與usb設備通信前檢查是否有訪問該設備的權限。如果不檢查的話,可能會拋出運行時異常。

要顯式的申請權限,你首先需要創建一個broadcast receiver,這個receiver會監聽調用requestPermission()方法時由系統發出的廣播。調用requestPermission()方法會彈出一個對話框要求用戶授權。下面的代碼爲broadcast receiver示例:

private static final String ACTION_USB_PERMISSION =
    "com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    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 (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(device != null){
                      //call method to set up device communication
                   }
                } 
                else {
                    Log.d(TAG, "permission denied for device " + device);
                }
            }
        }
    }
};

然後在activity的onCreate()方法中註冊broadcast receiver:

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);

最後調用requestPermission()方法顯示一個對話框以向用戶申請權限:

UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);

當用戶完成操作後,你的broadcast receiver會收到包含EXTRA_PERMISSION_GRANTED的intent,這個extra爲一個boolean,表示權限獲取結果。

4.3 與設備通信

與usb設備的通信有兩種方式,同步或異步。不論你用哪個方式,你都應該新開一個線程進行通信操作,以防阻塞UI線程。要與設備通信,你需要獲取適當的UsbInterface和UsbEndpoint,然後用UsbDeviceConnection向endpoint發送請求。通常,你的代碼應該:

  • 檢查UsbDevice的屬性,如product ID,vendor ID,設備class
  • 當你確定設備是你想要的後,需要找到你要對此UsbEndpoint通信的UsbInterface。每個interface可以對應多個endpoint,通常情況下需要輸入輸出兩個endpoint以支持雙向通信
  • 當你找到正確的endpoint後,在此endpoint上打開UsbDeviceConnection
  • 用bulkTransfer()或controlTransfer()方法提交你想要傳輸的數據。這些操作應該另起線程去做,不要阻塞UI線程

下面的代碼簡單介紹了同步數據傳輸的方法。你的代碼應該更復雜,應該添加尋找interface和endpoint的邏輯:

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

如果需要異步發送數據,可以使用UsbRequest類初始化異步請求並把它加入到執行隊列中,然後用requestWait()方法等待結果。

更多的信息請看AdbTest sample(此爲異步通信例子),或MissileLauncher sample(此爲異步監聽endpoint中斷的例子)。

4.4 終止通信

當你與設備通信結束後,可以使用releaseInterface()或close()方法關閉UsbInterface或UsbDeviceConnection。你可以用以下的broadcast receiver監聽設備分離事件:

BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction(); 
      if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                // call your method that cleans up and closes communication with the device
            }
        }
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章