安卓USB開發教程 USB Accessory

SoldierJazz2019發表於2017-06-28

USB Accessory(配件模式)

USB 配件模式允許使用者連線專為 Android 裝置設計的 USB 主機硬體。配件必須遵守 Android Accessory Development Kit 文件中列出的 Android 配件協議。 這使得 Android 裝置無法充當 USB 主機時仍然可以與 USB 硬體互動。 當 Android 裝置處於 USB 配件模式時,所連線的 Android USB 配件充當主機,為 USB 匯流排供電,並列舉所連線的裝置。 Android 3.1(API 級別12)支援 USB 配件模式,該功能也被以附加元件庫的方式回溯到 Android 2.3.4(API 級別10),以支援更廣泛的裝置。前提是廠商必須在系統映象中新增附加元件庫。

選擇正確的 USB Accessory API 函式

儘管 USB accessory API 函式在 Android 3.1 引入到平臺,通過使用 Google APIs 附加庫的方式用在 Android 2.3.4 上。由於這些 APIs 使用外部庫的方式回溯,有兩個包可以匯入來支援 USB 配件模式。根據你要支援的安卓裝置,您可能要使用一個庫而不是另一個:

com.android.future.usb: 為了在 Android 2.3.4 中支援 USB accessory 模式,Google APIs add-on library 包括回溯的 USB accessory  API,它們包含在此名稱空間中。 Android 3.1 還支援在此名稱空間中匯入和呼叫類,以支援使用附加庫編寫的應用程式。 這個附加庫是圍繞 android.hardware.usb accessory API 的簡版 wrapper,不支援USB主機模式。 如果您想支援最廣泛的支援 USB accessory 模式的裝置,請使用附加庫並匯入此軟體包。 重要的是要注意,並非所有的 Android 2.3.4 裝置都需要支援 USB 配件功能。 每個單獨的裝置製造商決定是否支援此功能,這就是為什麼必須在 manifest 檔案中宣告它。
android.hardware.usb: 此名稱空間包含在 Android 3.1 中支援 USB accessory 模式的類。 該軟體包作為 framework API 的一部分,Android 3.1 支援 USB accessory 模式,而不需要附加庫。 如果您只關心具有 USB accessory 模式硬體支援的 Android 3.1 或更新版本,您可以在清單檔案中宣告使用此軟體包。

安裝 Google APIs 附加庫

如果要安裝該附加庫,可以通過使用 SDK Manager 安裝 Google API Android API 10 軟體包。有關安裝附加庫的更多資訊,請參閱 Installing the Google APIs Add-on

API 概述

由於附加庫是 framework API 的 wrapper,因此支援 USB accessory 功能的類很相似。 即使您正在使用附加庫,也可以使用 android.hardware.usb 的參考文件。

Note:但是,您應該注意的附加庫和 framework API 之間存在較小的用法差異。
下表描述支援 USB accessory API 的類:
Class Description
UsbManager 允許列舉以及與已連線 USB 配件通訊
UsbAccessory 表示USB 配件,包含訪問其標識資訊的方法

附加庫和平臺 APIs 的使用差別

在使用 Google APIs 和平臺 APIs 之間有兩處使用區別。
如果正在使用附加庫,必須以下列方式獲取 UsbManager

UsbManager manager = UsbManager.getInstance(this);

如果沒在使用附加庫,必須以下列方式獲取 UsbManager

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

當您使用 intent filter 過濾連線的配件時,UsbAccessory 物件包含在傳遞給應用程式的 intent 內。 如果正在使用附加庫,則必須以下列方式獲取 UsbAccessory 物件:

UsbAccessory accessory = UsbManager.getAccessory(intent);

如果沒在使用附加庫,必須以下列方式獲取 UsbAccessory

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

安卓 Manifest 要求

以下列表描述了在使用 USB accessory API 之前需要新增到應用程式的 manifest 檔案中的內容。manifest and resource file examples 展示瞭如何宣告這些專案:

1. 因為並非所有 Android 裝置被授權支援 USB accessory API,因此包含一個 <uses-feature> 元素,宣告您的應用程式使用 android.hardware.usb.accessory 功能。

2. 如果您正在使用附加庫,請新增指定 com.android.future.usb.accessory 的 <uses-library> 元素。

3. 如果您正在使用附加庫,請將應用程式的最小 SDK 設定為 API Level 10,如果使用的是android.hardware.usb 包,則將其設定為12。

4. 如果您希望應用程式在 USB 配件連線時收到通知,請在主要活動中為 android.hardware.usb.action.USB_ACCESSORY_ATTACHED 意圖指定 <intent-filter><meta-data> 元素對。<meta-data> 元素指向一個外部XML資原始檔,它宣告瞭您想檢測的配件的標識資訊。

在XML資原始檔中,為要過濾的配件宣告 <usb-accessory> 元素。每個 <usb-accessory> 可以具有以下屬性:

  • manufacturer
  • model
  • version
將資原始檔儲存在 res/xml/ 目錄中。資原始檔名(不含 .xml 副檔名)必須與您在 <meta-data> 元素中指定的檔名相同。 XML資原始檔的格式也展示在下面的 example 中

Manifest 和 資原始檔示例

以下示例展示了一個 manifest 樣例及相應的資原始檔:

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

在這個案例中,下面的資原始檔應該儲存在 res/xml/accessory_filter.xml 中,並且指定具有相關 model、manufacturer、version 的配件應該被過濾。配件將這些屬性傳送給 Android 裝置:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
</resources>

使用配件

當使用者將 USB 配件連線到 Android 裝置時,Android 系統可以決定您的應用程式是否對連線的配件感興趣。 如果是這樣,如果需要,您可以設定與附件的通訊。如果是這樣,您可以根據需要建立與裝置的通訊。為此,您的應用程式必須:

1. 通過使用 intent filter 過濾配件連線事件來發現配件或通過列舉已連線的配件找到正確的配件。

2. 請求使用者連線 USB 配件的許可權,如果尚未獲得。

3. 在正確的介面端點上讀寫資料與 USB 配件進行通訊。

發現配件

應用程式可以通過使用 intent filter 在使用者連線配件時收到通知或通過列舉已連線的 USB 配件來發現 USB 配件。如果您希望能夠讓應用程式自動檢測到所需的配件,則使用 intent filter 非常有用。 如果要獲取所有連線的配件列表,或者您的應用程式沒有為 intent 進行過濾,則列舉已連線的 USB 配件的方法非常有用。

使用 intent filter(意圖過濾器)

要使您的應用程式發現一個特定的 USB peijia,可以指定一個 intent filter 來過濾 android.hardware.usb.action.USB_ACCESSORY_ATTACHED intent。 除了此 intent filter,您還需要指定一個資原始檔,該資原始檔指定 USB 配件的屬性,如 manufacturer、model 和 version。 當使用者連線與 accessory filter 匹配的配件時,

以下示例展示如何宣告 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>

以下示例展示如何宣告相應資原始檔,其指定了感興趣的 USB 配件:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>

在你的活動中,你可以像這樣從 intent 中獲取表示連線附件的 UsbAccessory(使用附加庫)

UsbAccessory accessory = UsbManager.getAccessory(intent);

或者像這樣(使用平臺 APIs):

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

列舉配件

當應用程式執行時,可以讓應用程式列舉已經標識自己的配件。

使用 getAccessoryList() 方法獲取所有已連線 USB 配件的陣列:

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

Note:同一時間只可以支援一個已連線配件。

獲取與配件進行通訊的許可權

在與 USB 配件進行通訊之前,應用程式必須獲得使用者的許可。

Note如果應用程式 uses an intent filter 來發現連線時的 USB 配件,則如果使用者允許您的應用程式處理 intent,則它將自動接收許可權。如果沒有,您必須在連線到配件之前在應用程式中明確請求許可權。

在某些情況下,顯式請求許可權可能是必需的,例如當您的應用程式列舉到已連線的 USB 配件,然後要與其進行通訊時。在嘗試與之通訊之前,您必須檢查訪問配件的許可權。如果沒有,使用者拒絕訪問配件的許可權時,您將收到 runtime 錯誤。

要明確獲得許可,首先建立一個廣播接收器。該接收器偵聽當您呼叫 requestPermission() 時獲得廣播的意圖。對 requestPermission() 的呼叫向使用者顯示一個對話方塊,請求連線到配件的許可權。以下示例程式碼展示瞭如何建立廣播接收器

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

要註冊廣播接收器,在活動的 onCreate() 方法中新增如下程式碼:

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() 方法

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

當使用者響應對話方塊時,廣播接收器收到包含額外值 EXTRA_PERMISSION_GRANTED 的 intent,這是表示答案的布林值。 在連線配件之前,請檢查這個額外值是否為 true。

與配件通訊

您可以使用 UsbManager 與配件通訊,以獲取檔案描述符,您可以配置輸入和輸出流來讀取和寫入資料到描述符。 資料流表示配件的輸入和輸出批量端點。 您應該在另一個執行緒中建立裝置和配件之間的通訊,因而不會阻塞主UI執行緒。 以下示例展示如何開啟配件進行通訊:

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

線上程的 run() 方法中,可以使用 FileInputStream 或 FileOutputStream 物件來讀寫配件。 使用 FileOutputStream 物件從配件讀取資料時,請確保使用的緩衝區足夠大以儲存 USB 資料包資料。 Android 配件協議支援高達16384位元組的資料包緩衝區,因此為了簡單起見,您可以選擇始終宣告緩衝區為此大小。

Note:注意:在較低階別,USB 全速配件的資料包為64位元組,USB 高速配件為512位元組。 為了簡單起見,Android 配件協議將兩個速度的資料包捆綁在一起成為一個邏輯資料包。

有關在 Android 中使用執行緒的更多資訊,請參閱 Processes and Threads

終止與配件通訊

當你與配件通訊完成或者配件拔出時,呼叫 close() 方法關閉你開啟的描述符為了監聽拔除事件,如下所示建立廣播接收器:

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

在應用程式中而不是 manifest 中建立廣播接收器允許應用在執行時只處理拔除事件。通過這種方式,廣播事件只會傳送到當前正在執行的應用程式而不是廣播到所有應用。


原文連結:https://developer.android.com/guide/topics/connectivity/usb/accessory.html









相關文章