【Android】聯通性 -- USB從屬模式

本文譯自:http://developer.android.com/guide/topics/connectivity/usb/accessory.html

         USB的從屬模式允許用戶連接到專門給Android設備設計的USB主機硬件。這些附件必須遵循在Android從屬模式開發工具文檔中描述的Android從屬模式協議。這個協議允許Android設備即使不作爲USB主機依然能夠跟USB硬件進行交互。當Android設備處於USB從屬模式時,被連接的Android USB附件會作爲主機,提供用於供電的USB總線,並列舉被連接的設備。Android3.1(API Level 12)開始支持USB從屬模式,並且這個功能也能夠被移植到Android2.3.4(API Level 10)中,以便能夠支持更廣泛的設備。

一,選擇正確的USB從屬模式API

        儘管USB從屬模式API是在Android3.1中被引入的,但是它們也可以作爲附加類庫在Android2.3.4中使用。因爲這些API是使用一個外部類庫來移植的,所以要導入兩個包才能支持USB從屬模式。根據你想要支持的Android設備,你要使用以下其中一種類庫:

      1. com.android.future.usb(兼容性更強):要在Android2.3.4中支持USB從屬模式,就要使用包含USB從屬模式的Google API的附加類庫,這些類庫被包含在這個命名空間中。

             Android3.1也支持對這個命名空間的類的引入和調用,以便支持用這個附加類來編寫的應用程序。這個附加類庫是圍繞android.hardware.usb從屬模式API的一個簡單封裝,並且不支持USB主機模式。如果你希望設備能夠最大範圍的支持USB從屬模式,就要使用這個附加類庫,並要導入這個包。重點要注意的是,不是所有的Android2.3.4設備都需要支持USB從屬功能。每個獨立的設備製造商會決定是否支持這種能力,這就是爲什麼要在你的清單文件中聲明的原因 。

      2. android.hardware.usb(支持Android 3.1及更新設備):這個命名空間包含了Android3.1中所支持的USB從屬模式的類。這個包被包含在框架API中,因此Android3.1不使用附加類庫來支持USB從屬模式。如果你只關注Android3.1或更新的支持USB從屬模式的硬件設備,那麼就可以在你的清單文件中聲明使用這個包。

二,安裝Google API附加類庫

        如果你要安裝這個附加類庫,使用SDK管理器,通過安裝Google APIs Android API 10來完成。關於安裝附加類庫的更多信息,請看“安裝Google APIs附加類庫”。

API概要

      因爲該附加的類庫是一個針對框架API的封裝,所以支持USB從屬模式的類都是類似的。即使你正在使用這個附加類庫,你也能夠使用android.hardware.usb的參考文檔。

     注意:但是,在附加類庫和框架API之間有一點使用上的差異,你應該注意。

下表介紹了支持USB從屬模式API的類:

介紹

UsbManager

允許你列舉被連接的USB附件,並跟它們通信。

UsbAccessory

代表一個USB附件,幷包含了訪問該附件標識信息的方法。

Add-on 類庫和平臺API之間在使用上的差異

          在使用Google APIs (Google API)add-on類庫  和  平臺API(Android API)之間有兩種使用上的差異。

          如果使用add-on類庫,必須用下列方式獲取UsbManager對象:

                           UsbManager manager=UsbManager.getInstance(this);

          否則,必須用下列方式獲取UsbManager對象:

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

          在使用Intent過濾器過濾一個被連接的附件時,在傳遞給你的應用程序的Intent對象內部包含了UsbAccessory對象。如果你正在使用add-on類庫,就必須使用以下方式來獲取UsbAccessory對象:

                         UsbAccessory accessory=UsbManager.getAccessory(intent);

         否則,必須使用以下方式來獲取UsbAccessory對象:

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

三,Android清單要求

下面列出了在使用USB從屬模式API工作之前,需要添加到你的應用程序清單文件中的內容:

               1. 因爲不是所有的Android設備都保證支持USB從屬模式API,所以要在<uses-feature>元素中聲明使用android.hardware.usb.accessory功能;

               2.  如果你是使用add-on類庫,還要添加<uses-library>元素來指定要使用的com.android.future.usb.accessory類庫;

               3. 如果你是使用add-on類庫,則要設置該應用程序的SDK的最小版本號是API Level 10,如果使用的是android.hardware.usb包,則最小版本號是API Level 12.

               4. 如果你希望獲得連接USB附件的通知,就要在你的主Activity中給<intent-filter>和<meta-data>元素指定android.hardware.usb.action.USB_ACCESSORY_ATTACHED類型的Intent。<meta-data>元素要指向一個外部的XML資源文件,該文件中聲明瞭有關你想要檢測的附件的標識信息。

在這個XML資源文件中,用<usb-accessory>元素來聲明你想要過濾的附件。每個<usb-accessory>元素能夠有以下屬性:

           manufacturer

           model

           version

該資源文件保存在res/xml目錄中。資源文件名稱(不含.xml擴展名)必須跟<meta-data>元素中指定的名稱相同。下例顯示該XML資源文件的格式:

清單和資源文件的示例

下例顯示了清單和它對應的資源文件:

<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文件中,並且指定了要過濾的附件所對應的模式、製造商和版本號。USB附件會把這些屬性發送給Android設備:

<?xml version="1.0" encoding="utf-8"?>
 
<resources>
    <usb-accessorymodel="DemoKit"     manufacturer="Google"       version="1.0"/>   //發送命令的時候一定要對應

</resources>

四,跟附件一起工作

       當用戶把USB附件連接到Android設備時,Android系統能夠判斷你的應用程序是否對接入的附件感興趣。如果感興趣,你能夠跟期望的附件建立通信。以下是你的應用程序要做的事情:

             1.  通過使用過濾附件設備接入事件的Intent過濾器或列舉已經接入的附件設備來發現對應的附件設備;

             2.  如果不是已知的附件設備,就要詢問用戶是否允許跟該附件設備通信;

             3.  通過讀寫對應接口端點上的數據來跟附件設備通信。

     發現附件設備

              你的應用程序既可以通過用戶接入附件設備時的Intent通知,也可以通過列舉已經接入的附件設備來發現你想要的附件設備。

                    如果你想要你的應用能夠自動檢測到期望的附件設備,那麼使用Intent過濾器是有用的。

                    如果你想要獲取所有已連接的附件設備列表,或者你的應用程序並不過濾特定的Intent,那麼使用列舉的方法是有用的。

     使用Intent過濾器

             你可以指定一個android.hardware.usb.action.USB_ACCESSORY_ATTACHED類型的Intent過濾器,以便你的應用程序能夠發現特殊的USB附件設備。跟這個Intent過濾器一起,你還需要指定一個包含USB附件設備屬性的資源文件,這些屬性包括:製造商、模式、版本。

下例顯示瞭如何聲明一個Intent過濾器:

<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對象.

          使用add-on類庫的情況:

                      UsbAccessory accessory=UsbManager.getAccessory(intent);

          使用平臺API的情況:

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

列舉附件設備

             在應用程序運行時,你能夠讓你的應用程序列舉出已經識別出的所有附件設備。

             使用getAccessoryList()方法來獲取所有已連接的USB附件設備:

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

                     UsbAccessory[] accessoryList = manager.getAcccessoryList();

             注意:當前,一次只支持連接一個附件設備,但是在未來,該API被設計成要支持多個附件設備。

獲得跟附件通信的權限

            在跟USB附件設備進行通信之前,你的應用程序必須要從用戶那裏獲得許可。

注意:如果你的應用程序使用Intent過濾器來發現那些被接入的附件設備,而且用戶允許你的應用程序處理該Intent對象,那麼會自動的接收許可。否則,在連接附件設備之前,你必須明確的申請接入許可。

           明確的申請接入許可,在有些情況下是必須的,如在你的應用程序列舉出了已經接入的附件設備,並要跟其中之一進行通信時。在試圖跟其通信之前,你必須檢查訪問該附件設備的權限。否則,如果用戶拒絕了你訪問該附件設備的請求,你會收到一個運行時錯誤。

           要明確的獲得許可,首先就要創建一個廣播接收器。這個接收器會監聽在調用requestPermission()方法時獲得的用於廣播的Intent對象。調用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);

               }

           }

       }

   }

};

在你的Activity的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。

跟一個附件設備通信

         通過使用UsbManagerd對象獲取一個文件描述符來跟附件設備通信,你能夠使用這個文件描述符建立讀寫數據的輸入和輸出流。該流代表了附件設備的輸入和數據塊端點。你應該在另外一個線程中建立設備和附件之間的通信,以便不至於阻塞主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對象來讀寫附件設備。在使用FileInputStream對象從附件設備中讀取數據時,要確保有足夠大的緩存來存儲USB包數據。Android從屬模式協議支持的最大包緩存是16384個字節,因此你可以簡單的始終把你的緩存聲明成這個尺寸。

注意:在底層,對於全速USB附件設備的64字節包和高速USB附件設備的512字節包,Android從屬模式協議會簡單的把這個兩個數據打包成一個邏輯包。

終止跟附件設備的通信

在完成跟附件設備的通信或附件設備被拔出時,你要調用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

           }

       }

   }

};

在應用程序中創建的廣播接收器,而且不在清單文件中,這樣的廣播接收器允許你的應用程序只在運行時處理分離事件。這種方式,分離事件只發送給當前正在運行的應用程序,並不會廣播給所有的應用程序。

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