USB Accessory 模式
USB附件模式允許用戶連接專為Android設備設計的USB主機硬件。配件必須遵守Android配件開發套件文檔中概述的Android附件協議。這使得無法充當USB主機的Android電源設備仍然可以與USB硬件交互。當Android設備處於USB附件模式時,所附的Android USB附件充當主機,為USB總線供電,並列舉連接的設備。 Android 3.1(API級別12)支持USB附件模式,並且該功能也被返回到Android 2.3.4(API級別10),以支持更廣泛的設備
a. 選擇正確的USB附件API
雖然USB附件API已經在Android 3.1平台上引入,但Android 2.3.4中也可以使用Google API附加庫。因為這些API是使用外部庫進行反向輸入的,所以有兩個包可以導入以支持USB附件模式。根據您要支持的Android驅動的設備,您可能需要使用其他設備:
com.android.future.usb:要在Android 2.3.4中支持USB附件模式,Google API附加庫包括后端的USB附件API,它們包含在此命名空間中。 Android 3.1還支持在此命名空間中導入和調用類,以支持使用附加庫編寫的應用程序。這個附加庫是圍繞android.hardware.usb附件API的薄包裝,不支持USB主機模式。如果要支持最廣泛的支持USB附件模式的設備,請使用附加庫並導入此軟件包。重要的是要注意,並不是所有的Android 2.3.4設備都需要支持USB附件功能。每個設備制造商決定是否支持此功能,這就是為什么必須在清單文件中聲明它。
android.hardware.usb:這個命名空間包含在Android 3.1中支持USB附件模式的類。該軟件包作為框架API的一部分,所以Android 3.1支持USB附件模式而不使用附加庫。如果您只關心具有USB附件模式硬件支持的Android 3.1或更新的設備,您可以在清單文件中聲明,請使用此軟件包。
b. API概述
UsbManager 允許您枚舉和連接所連接的USB附件。
UsbAccessory 表示USB附件,包含訪問其識別信息的方法。
如果您正在使用附加庫,則必須以下列方式獲取UsbManager對象:
UsbManager manager = UsbManager.getInstance(this);
1
如果您正在使用附加庫,則必須以下列方式獲取UsbManager對象:
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
1
如果您不使用附加庫,則必須以下列方式獲取UsbAccessory對象:
UsbAccessory accessory = UsbManager.getAccessory(intent);
1
如果您不使用附加庫,則必須以以下方式獲取UsbAccessory對象:
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
1
c. Android Manifest 要求
以下列表描述了在使用USB輔助API之前需要添加到應用程序的清單文件中。清單和資源文件示例顯示如何聲明這些項目:
因為並非所有Android設備都能保證支持USB附件API,還包括一個元素,聲明您的應用程序使用android.hardware.usb.accessory功能。
如果您正在使用附加庫,請添加指定庫的com.android.future.usb.accessory的元素。
如果您正在使用附加庫,請將應用程序的最小SDK設置為API Level 10,如果使用的是android.hardware.usb包,則將其設置為12。
如果希望您的應用程序被通知附加的USB附件,請在主要活動中為android.hardware.usb.action.USB_ACCESSORY_ATTACHED意圖指定和元素對。 元素指向一個外部XML資源文件,它聲明要檢測的附件的信息。
在XML資源文件中,為要過濾的附件聲明元素。每個可以具有以下屬性:
manufacturer
model
version
1
2
3
將資源文件保存在res / xml /目錄中。資源文件名(不含.xml擴展名)必須與您在元素中指定的文件名相同。 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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在這種情況下,應將以下資源文件保存在res / xml / accessory_filter.xml中,並指定具有相應型號,制造商和版本的任何附件應被過濾。配件將這些屬性發送給Android設備:
<?xml version="1.0" encoding="utf-8"?>
< resources>
< usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
< /resources>
1
2
3
4
Working with Accessory
當用戶將USB附件連接到Android設備時,Android系統可以確定您的應用程序是否對連接的附件感興趣。如果是這樣,如果需要,您可以設置與附件的通信。為此,您的應用程序必須:
1、通過使用過濾附件事件的意圖過濾器或枚舉連接的附件並找到適當的附件來發現連接的附件。
2、要求用戶與附件通信,如果尚未獲得。
3、通過在適當的接口端點上讀取和寫入數據來與附件通信。
Discovering an accessory
您的應用程序可以通過使用意圖過濾器來發現附件,以便在用戶連接附件或枚舉已連接的附件時收到通知。如果希望能夠讓應用程序自動檢測到所需的附件,則使用意圖過濾器很有用。如果您想獲得所有連接的配件的列表,或者您的應用程序沒有為意圖過濾,枚舉連接的附件是有用的。
Using an intent filter
要使您的應用程序發現一個特定的USB附件,您可以指定一個intent過濾器來過濾android.hardware.usb.action.USB_ACCESSORY_ATTACHEDintent。除了該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>
1
2
3
4
5
6
7
8
9
以下示例顯示如何聲明指定您感興趣的USB附件的相應資源文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>
1
2
3
4
5
在您的活動中,您可以從這樣的intent(使用附加庫)獲取代表附件的UsbAccessory:
UsbAccessory accessory = UsbManager.getAccessory(intent);
或者像這樣(使用平台API):
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
枚舉 accessories
您的應用程序可以枚舉在應用程序運行時識別自己的配件。
使用getAccessoryList()方法獲取所有連接的USB附件的陣列:
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAcccessoryList();
獲取與附件通信的權限
在與USB配件進行通信之前,您的應用必須得到用戶的許可。
注意:如果您的應用程序使用意圖過濾器在附件中發現附件,則如果用戶允許您的應用程序處理意圖,則它會自動接收權限。如果沒有,您必須在連接到附件之前在應用程序中明確請求許可。
在某些情況下,明確請求許可可能是必需的,例如當您的應用程序枚舉已連接的配件,然后要與其進行通信時。在嘗試與之通信之前,您必須檢查訪問附件的權限。如果沒有,如果用戶拒絕訪問附件的權限,您將收到運行時錯誤。
要明確獲得許可,首先創建廣播接收器。該接收器偵聽當您調用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);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
};
要注冊廣播接收器,請將其放在您的活動中的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 extra的意圖,這是一個表示答案的布爾值。在連接附件之前,請檢查這個額外的值是否為true。
與 Accessory 通訊
您可以通過使用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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在線程的run()方法中,您可以使用FileInputStream或FileOutputStream對象來讀寫附件。使用FileInputStream對象從附件讀取數據時,請確保您使用的緩沖區足夠大以存儲USB數據包數據。 Android附件協議支持高達16384字節的數據包緩沖區,為了簡單起見您可以選擇始終聲明緩沖區:
注意:在較低的級別,USB全速附件的數據包為64字節,USB高速附件為512字節。為了簡單起見,Android附件協議將兩個速度的數據包捆綁在一起成為一個邏輯數據包。
有關在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
}
}
}
1
2
3
4
5
6
7
};
在應用程序中創建廣播接收者,而不是清單,允許您的應用程序在運行時僅處理分離的事件。這樣,分離的事件只會發送到當前運行的應用程序,而不是廣播到所有的應用程序