天天看點

USB Accessory-Android 6.0開發者文檔一、USB accessory和USB host二、USB accessory三、選擇正确的usb accessory API四、API簡介五、Android manifest設定六、使用accessory

以下内容翻譯自USB Accessory

  • 一、USB accessory和USB host
  • 二、USB accessory
  • 三、選擇正确的usb accessory API
  • 四、API簡介
    • 4.1 附加library和架構API之間的差别
  • 五、Android manifest設定
  • 六、使用accessory
    • 6.1 發現配件
      • 6.1.1 使用intent filter
      • 6.1.2 枚舉配件
    • 6.2 擷取通信權限
    • 6.3 與配件通信
    • 6.4 終止與配件的通信

一、USB accessory和USB host

Android系統對各種USB裝置和Android USB配件(這些配件遵守Android accessory protocol)的支援基于兩種模式:USB accessory和USB host。在USB accessory模式下,外部USB裝置作為usb host,這些外部裝置有:機器人控制器、擴充塢、音頻裝置、讀卡器等。這個模式允許沒有host能力的Android裝置跟USB裝置(此裝置必須支援Android accessory communication protocol)互動。在USB host模式下,Android裝置作為host。支援的外部裝置有數位相機、鍵盤、滑鼠、遊戲主機等。

圖1顯示了兩種模式的不同。Android裝置在host模式時,作為USB host,并驅動通信總線。Android裝置在accessory模式時,外部USB裝置作為host并驅動資料總線。

USB Accessory-Android 6.0開發者文檔一、USB accessory和USB host二、USB accessory三、選擇正确的usb accessory API四、API簡介五、Android manifest設定六、使用accessory

Android從Android 3.1(API 12)開始支援這兩種模式。USB accessory模式可以通過附加的library相容到Android 2.3.4(API 10),以支援更多的裝置。裝置廠商可以自主選擇是否将此library添加到系統中。

注意:對兩種模式的支援,最終取決于裝置硬體,而不是平台level。你可以用

<uses-feature>

标簽過濾掉不支援這兩種模式的裝置

二、USB accessory

USB accessory模式允許使用者連接配接到針對Android特别設計過的USB host裝置。這些裝置必須支援Android accessory development kit文檔中描述的Android accessory protocol協定。這個協定使不能作為USB host的Android裝置可以和USB裝置通信。當Android裝置處于USB accessory模式時,連接配接的USB裝置作為host,為usb總線提供能量,并且可以枚舉已連接配接裝置。Android從Android 3.1(API 12)開始支援usb accessory模式,USB accessory模式可以通過附加的library相容到Android 2.3.4(API 10),以支援更多的裝置。

三、選擇正确的usb accessory API

雖然USB accessory API從Android 3.1開始支援,但是你也可以通過Google API附加的library将它相容到Android 2.3.4。由于這些API是通過外部library加入的,是以需要導入兩個包。根據你想要支援的Android裝置,你可以選擇下面兩個包中的一個:

  • com.android.future.usb:這個包中的内容是為了将USB accessory相容到Android 2.3.4在Google API附加的library。Android 3.1也可以導入這個包并調用其中的類。這個library是對android.hardware.usb中的accessory API的輕量包裝,且不支援USB host模式。如果你想要相容更多的裝置,你可以将這個library導入到項目中。需要注意的是,不是所有的Android 2.3.4裝置都支援USB accessory特性。裝置廠商可以自主選擇是否支援此特性,是以你最好在manifest檔案中聲明你需要此特性。
  • android.hardware.usb:這個包中的内容是Android 3.1加入的支援USB accessory的類。這個包是架構API的一部分,是以Android 3.1不需要導入額外的library就可以支援USB accessory。如果你隻需要支援Android 3.1及更高的裝置,你可以使用這個包,并在manifest檔案中聲明特性。

四、API簡介

由于附加的library是對架構API的封裝,是以這些類都很相似。你甚至可以直接使用android.hardware.usb的文檔使用附加library。

注意:在附加library和架構API之間還是存在一些細微差别的

下表描述了USB accessory API相關的類:

Class 描述
UsbManager 允許你枚舉已連接配接裝置并通信
UsbAccessory 表示一個usb accessory,包含通路識别資訊的方法

4.1 附加library和架構API之間的差别

附加library和架構API之間有兩個差别。

如果你使用附加library,擷取UsbManger對象方式如下:

UsbManager manager = UsbManager.getInstance(this);
           

如果你使用架構API,擷取UsbManager對象方式如下:

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
           

當你用intent filter過濾已連接配接的accessory時,可以從傳遞到你的應用的intent中擷取UsbAccessory對象。而如果你使用的是附加的library的話,你就需要用以下方式擷取UsbAccessory對象了:

UsbAccessory accessory = UsbManager.getAccessory(intent);
           

如果你使用架構API,則代碼如下:

UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
           

五、Android manifest設定

下面描述的是在使用usb accessory API前需要在manifest檔案中添加的東西:

  • 由于不是所有裝置都支援USB accessory API,是以請用标簽聲明你的應用會使用android.hardware.usb.accessory特性。
  • 如果你使用的是附加的library,請添加

    <uses-librara>

    标簽指定com.android.future.usb.accessory。
  • 如果你使用的是附加的library,請将SDK最小值設為API 10,如果使用的是架構API,請設為12。
  • 如果你希望你的應用可以收到附加usb accessory的資訊,可以在activity中指定

    <intent-filter>

    <meta-data>

    以接收指定intent。其中action為android.hardware.usb.action.USB_ACCESSORY_ATTACHED,

    <meta-data>

    指定一個包含你想要知道的識别資訊的外部XML檔案。

    XML檔案中需要聲明你想要過濾的accessory的資訊(用

    <usb-accessory>

    标簽)。每個

    <usb-accessory>

    标簽都包含以下屬性:
    • manufacturer
    • model
    • version
    将這個resource檔案儲存在res/xml/目錄。resource檔案名稱必須與

    <meta-data>

    中(沒有.xml字尾)所寫一緻。

下面是一個manifest檔案和對應resource檔案示例:

<manifest ...>
    <uses-feature android:name="android.hardware.usb.accessory" />

    <uses-sdk android:minSdkVersion="<version>" />
    ...
    <application>
      <uses-library android:name="com.android.future.usb.accessory" />
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
            </intent-filter>
            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/accessory_filter" />
        </activity>
    </application>
</manifest>
           

resource檔案儲存為res/xml/accessory_filter.xml,并指定model、manufacturer、version:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
</resources>
           

六、使用accessory

當使用者将USB配件連接配接到Android裝置時,Android系統會判斷你的應用是否對此連接配接的配件感興趣。如果是的話,你就可以跟此配件通信了。要聲明你的應用對何種附件感興趣,你可以:

  1. 通過intent filter擷取配件相關事件進而發現已連接配接配件,或直接枚舉已連接配接配件
  2. 向使用者申請跟配件通信的權限
  3. 在适當的接口上跟配件用讀寫操作通信

6.1 發現配件

當使用者連接配接某個配件時,你的應用可以通過intent filter擷取到它。你也可以直接枚舉已連接配接的配件。如果你希望應用可以自動發現指定配件,你可以使用intent filter。如果你希望擷取已連接配接裝置清單或不想使用intent filter,可以使用枚舉的方法。

6.1.1 使用intent filter

你可以使用添加了android.hardware.usb.action.USB_ACCESSORY_ATTACHED的intent filter指定你想要發現的USB配件。在這個intent filter中,你需要指定一個resource檔案,在這個檔案中包含你想要的USB配件的一些屬性,如廠商、型号、版本。當使用者将符合你的需求的配件連接配接到Android裝置時,系統會通過intent通知你。

下面的例子顯示了如何聲明intent filter:

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

下面的例子顯示了如何聲明你感興趣的配件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>
           

在你的activity中,你可以用以下代碼從intent中擷取UsbAccessory:

UsbAccessory accessory = UsbManager.getAccessory(intent);
           

如果你使用的是架構API,則是使用以下代碼:

UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
           

6.1.2 枚舉配件

你可以在應用運作時枚舉到已注冊的配件。

使用getAccessoryList()方法可以擷取表示所有已連接配接usb配件的數組:

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAcccessoryList();
           

注意:目前版本,每次隻能擷取一個已連接配接配件,在将來,會支援多配件擷取

6.2 擷取通信權限

在與USB配件通信前,你的應用必須向使用者申請權限。

注意:如果你的應用使用intent filter方式發現配件,那麼在使用者允許你的應用處理相關intent時已經自動給予你的應用相關權限。如果使用的是其他方式,那麼必須在連接配接配件前明确的申請權限。

顯式申請權限在很多情況下都是必要的,比如你的應用要枚舉已連接配接裝置時。你必須在通路配件前檢查相關權限,否則可能會收到運作時異常(如果使用者拒絕給予你的應用權限)。

要顯式的擷取權限,首先需要建立一個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) {
                UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(accessory != null){
                        //call method to set up accessory communication
                    }
                }
                else {
                    Log.d(TAG, "permission denied for accessory " + accessory);
                }
            }
        }
    }
};
           

在你的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, , new Intent(ACTION_USB_PERMISSION), );
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
           

調用requestPermission()方法向使用者申請權限:

UsbAccessory accessory;
...
mUsbManager.requestPermission(accessory, mPermissionIntent);
           

申請權限的對話框顯示以後,使用者的響應會通過廣播發送給你的broadcast receiver,廣播的intent包含名為“EXTRA_PERMISSION_GRANTED”的extra,其是一個boolean,表示使用者是否給予權限。

6.3 與配件通信

你可以通過UsbManager擷取file descriptor,然後通過輸入輸出流跟此descriptor通信。你應該在子線程跟配件通信,不要阻塞主線程。下面為打開配件并通信的示例:

UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;
...
private void openAccessory() {
    Log.d(TAG, "openAccessory: " + accessory);
    mFileDescriptor = mUsbManager.openAccessory(mAccessory);
    if (mFileDescriptor != null) {
        FileDescriptor fd = mFileDescriptor.getFileDescriptor();
        mInputStream = new FileInputStream(fd);
        mOutputStream = new FileOutputStream(fd);
        Thread thread = new Thread(null, this, "AccessoryThread");
        thread.start();
    }
}
           

在“thread”的run()方法中,你可以用FileInputStream和FileOutputStream進行讀寫。當你用FileInputStream從配件中讀資料時,請確定你使用的buffer足夠存儲USB資料包。Android accessory protocol支援的buffer大小最大為16384位元組,是以你可以簡單的将你的buffer設定為這個大小。

注意:在底層,USB full-speed配件的資料包為64位元組,USB high-speed配件的資料包為512位元組。Android accessory protocol會将兩者統一成大小合乎邏輯的包。

6.4 終止與配件的通信

當你的通信結束或者配件與Android裝置分離時,你可以用close()方法關閉file descriptor。要監聽裝置斷開事件,可以用如下代碼建立broadcast receiver:

BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction(); 
        if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
            UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
            if (accessory != null) {
                // call your method that cleans up and closes communication with the accessory
            }
        }
    }
};