【轉載請注明出處】
首先介紹一個概念:USB Host and Accessory
Android通過兩種模式支持一系列的USB外圍設備和Android USB附件(實現了Android附件協議的硬件設備):USB從設備模式和USB主設備模式,在USB從設備模式下,外圍的USB硬件設備作為USB主設備,從設備模式的例子包括擴展塢,讀卡器等等。這使得Android設備不具備和USB硬件主動交流的能力,Android USB附件必須遵循Android附件交流協議。在USB主設備模式下,Android設備是作為主設備的,例子包括游戲控制器,數碼相機等等。
下面兩幅圖展示了兩種模式的區別。的那個Android設備處於主設備模式的時候,它給USB提供動力,當它是從設備模式的時候,連接的USB硬件設備作為主設備,給USB提供動力。
USB主設備和從設備模式在Android3.1(API 12)或者更新的平台上被直接支持,也可以在Android2.3.4(API 10)上被支持,需要一個add-on庫,設備制造商可以選擇是否在系統鏡像中包含該庫。(備注:對USB的主/從設備的支持完全獨立於硬件,而且不限於API Level。開發者可以使用<uses-feature>元素來過濾支持USB主/從設備模式的設備)。
HSB Host
主設備模式下,你可以主動的和對方設備進行交互。涉及到的類包括:UsbManager,UsbDevice,UsbInterface,UsbEndpoint,UsbDeviceConnection,UsbRequest,UsbConstants.一般情況下你需要用到全部這些類(UsbRequest只在你需要進行異步通信的時候使用),你需要一個UsbManager對象去檢索需要的UsbDevice對象,當你獲得這個設備之后,就需要找到合適UsbInterface和它的UsbEndpoint進行交互,一旦獲得了正確的endpoint,就可以打開一個UsbDeviceConnection與USB設備進行交互了。
Android Manifest Requirements
下面列舉了在使用USB host API之前需要對應用的manifest文件作出的改變:
1)因為不是所有的Android設備都保證支持USB host API,所以應用中需要包含<uses-featrue>元素來聲明,你的應用需要用到android.hardware.usb.host特征;
2)將SDK的最小值設置為12或者更高,USB host API在更早的版本上不被支持;
3)如果你想你的應用在有USB設備連接上去的時候得到通知,需要聲明<intent-filter>和<meta-data>元素對(詳情見后面),<meta-data>指向一個外部的xml資源文件,該文件描述了應用關心的設備的身份信息。在XML資源文件中,使用<usb-device>元素來過濾關心的USB設備,接下去的列表則描述了設備的屬性,包括vendor-id,product-id,class,subclass,protocol。如果沒有列出屬性,則表示對任何設備都感興趣。資源文件放在res/xml/文件夾下面,文件的名字必須和<meta-data>元素中聲明的一樣。例子:

1 <manifest ...> 2 <uses-feature android:name="android.hardware.usb.host" /> 3 <uses-sdk android:minSdkVersion="12" /> 4 ... 5 <application> 6 <activity ...> 7 ... 8 <intent-filter> 9 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> 10 </intent-filter> 11 12 <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" 13 android:resource="@xml/device_filter" /> 14 </activity> 15 </application> 16 </manifest>
接下來,我們在res/xml/文件夾下面新建一個device_filter.xml
文件,內容如下:

1 <?xml version="1.0" encoding="utf-8"?> 2 3 <resources> 4 <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" /> 5 </resources>
和設備交互的步驟如下:
1)發現設備
應用可以通過intent filter發現設備或者主動列舉已經連接的設備來發現設備。如果你想應用自動發現想要的設備,使用Intent-filter是非常有效的。如果你的設備沒有過濾器,而且想獲得連接設備的列表,列舉USB設備就會變得非常有用。
使用Intent filter的manifest文件的改變如上代碼已經列出,我們寫明了一個Intent filter,用於過濾得到android.hardware.usb.action.USB_DEVICE_ATTACHED
intent,然后寫明xml文件,當連接的設備符合你的filter之后,系統會彈出一個對話框,詢問是否打開你的應用,如果用戶接受,你的應用就會自動獲取訪問設備的的權限,直到設備斷開連接。
這個時候我們就在Activity中,使用下面的代碼獲取Intent代表的設備:
1 UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
2)列舉設備
如果你的應用在運行的時候對所有連接的設備都感興趣,它可以列舉在連接在總線上的所有設備,使用getDeviceList()方法就可以得到一個所有連接的設備的hash map列表。hashmap的key是設備的名字。代碼如下:

1 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 2 ... 3 HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); 4 UsbDevice device = deviceList.get("deviceName");
你也可以使用迭代器遍歷所有的設備:

1 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 2 ... 3 HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); 4 Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); 5 while(deviceIterator.hasNext()){ 6 UsbDevice device = deviceIterator.next() 7 //your code 8 }
3)獲取和設備交互的權限
在進行交互之前,應用必須獲得用戶的許可,也就是必須需要用戶同意才可以和設備交互。(備注“如果你的應用使用intent-filter來發現設備,當用戶允許應用處理Intent的時候就自動獲取了權限,如果不是,則需要顯示的向用戶發出請求)
有些時候,顯示的請求是必須的,假如不獲取權限就進行交互,就會收到一個runtime error。為了顯示的獲取權限,首先需要創建一個廣播接收器,接收器接收你調用requestPermission()方法產生的Intent,該方法調用會產生一個對話框詢問連接到該設備的權限,下面是示例代碼:

1 private static final String ACTION_USB_PERMISSION = 2 "com.android.example.USB_PERMISSION"; 3 private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { 4 5 public void onReceive(Context context, Intent intent) { 6 String action = intent.getAction(); 7 if (ACTION_USB_PERMISSION.equals(action)) { 8 synchronized (this) { 9 UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); 10 11 if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { 12 if(device != null){ 13 //call method to set up device communication 14 } 15 } 16 else { 17 Log.d(TAG, "permission denied for device " + device); 18 } 19 } 20 } 21 } 22 };
然后在onCreate()方法里面注冊監聽器:

1 UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); 2 private static final String ACTION_USB_PERMISSION = 3 "com.android.example.USB_PERMISSION"; 4 ... 5 mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); 6 IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); 7 registerReceiver(mUsbReceiver, filter);
只需要下面的步驟就可以彈出對話框:

1 UsbDevice device; 2 ... 3 mUsbManager.requestPermission(device, mPermissionIntent);
當用戶做出選擇之后,就會產生一個廣播,接收到之后就可以做出相應的判斷。
4)和設備交互
和USB設備的交流既可以是同步的也可以是異步的,不管是哪種,都需要創建一個新的線程來執行數據傳輸以避免阻塞UI線程。為了和設備正確交互,我們需要獲取設備正確的UsbInterface和UsbEndpoint,然后在該endpoint使用正確的UsbDeviceConnection發送請求。使用bulkTransfer()或者controlTransfer()方法傳送數據。下面的代碼是一段同步數據傳輸的例子,實際的代碼需要更加嚴密的邏輯:

1 private Byte[] bytes 2 private static int TIMEOUT = 0; 3 private boolean forceClaim = true; 4 5 ... 6 7 UsbInterface intf = device.getInterface(0); 8 UsbEndpoint endpoint = intf.getEndpoint(0); 9 UsbDeviceConnection connection = mUsbManager.openDevice(device); 10 connection.claimInterface(intf, forceClaim); 11 connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread
如果想要異步的發送數據,就使用UsbRequest類來初始化和隊列話異步請求,然后可以使用requestWait()來等待傳輸結果。
5)終止交互
當應用完成了交互或者設備被移除了,就需要通過調用releaseInterface()和close()關閉UsbInterface和UsbDeviceConnection,為了監聽移除時間,需要使用以下監聽器:

1 BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { 2 public void onReceive(Context context, Intent intent) { 3 String action = intent.getAction(); 4 5 if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { 6 UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); 7 if (device != null) { 8 // call your method that cleans up and closes communication with the device 9 } 10 } 11 } 12 };
在應用中創建監聽器,而不要在manifest中,這樣就可以使得只有在程序運行的時候監聽到該事件,就可以讓移除時間不會被發送給所有的應用程序而只是運行中的程序。
HSB Accessory
USB Accessory允許用連接專門為Android設備設計的USB主設備硬件。這些硬件必須遵循相應的協議。這允許不能作為USB主設備的Android設備也能和USB硬件交互,當一個設備處於USB從設備模式的時候,連接上來的硬件就是主設備,硬件就會給USB提供電能,並且列舉連接的設備。
Choosing the Right YSB Accessory APIs
盡管USB Accessory API是在Android 3.1被引入的,它在Android 2.3.4上面也可以通過附加庫使用,因為是通過外部庫,總共有兩個庫可以被導入支持USB從設備模式,使用哪一個庫決定於你支持的設備:
1)com.android.future.usb:為了在Android2.3.4上面支持USB從設備模式, Google APIs add-on library包含了這些API,並且就存在於該名稱空間下面。Android3.1也支持導入和使用該名稱空間中的類。這個庫是android.hardware.usb中接口的一層薄包裹,並且不支持USB主設備模式,如果你想最大范圍的支持USB從屬設備模式,使用附加庫,導入該包!值得注意的是並非所有的Android2.3.4設備都被要求支持USB accessory特征。每一個設備生產商自行決定是否要支持該功能,所有你要在你的manifest文件聲明(是否需要使用該特征);
2)android.hardware.usb:該名稱空間包括了Android3.1下面支持USB從屬設備模式的所有類,它是Framework API的一部分,因而Androuid3.1並不需要附加庫來實現從屬設備模式,如果你只關心Android3.1或者更新的設備,你可以使用該庫。
API Overview
有兩個類涉及到Android從設備模式,一個是UsbManager,另外一個是UsbAccessory。因為附加庫是framework API的一個包裹,所以接口基本一直,你可以看着android.hardware.usb庫的文檔使用附加庫。但是略有不同:
如果你使用附加庫,則可以使用以下代碼獲取UsbManager:

1 UsbManager manager = UsbManager.getInstance(this);
否則使用如下代碼:

1 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
使用Intent filter捕捉設備連接事件的時候,如果你是使用的附加庫,則使用以下語句獲取UsbAccessory:

1 UsbAccessory accessory = UsbManager.getAccessory(intent);
否則使用如下代碼:

1 UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
Android Manifest requirements
下面列舉了在使用USB accessory API之前需要對應用的manifest文件作出的改變:
1)因為不是所有的設備都保證支持USB accessory APIs,所以必須在文件中包含<uses-feature>元素,聲明應用需要使用android.hardware.usb.accessory特征;
2)如果你是在使用附加庫,則添加<uses-library>元素明確需要使用android.hardware.usb包;
3)如果你在使用附加庫,將SDK的最小值設置為10,如果在使用android.hardware.usb包,則設置為12;
4)如果你想你的應用在有USB設備連接上去的時候得到通知,需要在主Activity中聲明<intent-filter>和<meta-data>元素對(詳情見后面),<meta-data>指向一個外部的xml資源文件,該文件描述了應用關心的設備的身份信息。在XML資源文件中,使用<usb-accessory>元素來過濾關心的USB設備,每個<usb-accessory>包含下列屬性,包括manufactures,model,version。如果沒有列出屬性,則表示對任何設備都感興趣。資源文件放在res/xml/文件夾下面,文件的名字必須和<meta-data>元素中聲明的一樣。例子:

1 <manifest ...> 2 <uses-feature android:name="android.hardware.usb.accessory" /> 3 4 <uses-sdk android:minSdkVersion="<version>" /> 5 ... 6 <application> 7 <uses-library android:name="com.android.future.usb.accessory" /> 8 <activity ...> 9 ... 10 <intent-filter> 11 <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> 12 </intent-filter> 13 14 <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" 15 android:resource="@xml/accessory_filter" /> 16 </activity> 17 </application> 18 </manifest>
接下來,我們在res/xml/文件夾下面新建一個accessory_filter.xml
文件,內容如下:

1 <?xml version="1.0" encoding="utf-8"?> 2 3 <resources> 4 <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/> 5 </resources>
和設備交互的步驟如下:
1)發現設備
這里大致和主設備模式一樣,代碼前面都有,intent-filter獲得的時候,要注意區分是不是使用的附加庫。
2)列舉設備
當你的程序正在運行的時候,可以列舉確認了它們作為附件的設備,代碼如下:

1 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 2 UsbAccessory[] accessoryList = manager.getAcccessoryList();
備注:目前,Android設備一次只能作為一個硬件設備的附件,API如此設計是為了未來的發展。
3)獲取和設備交互的權限
這里基本上和從設別一直,只是用戶確認以后,是使用:

1 UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
語句獲取設備的句柄的。其余一致。
4)與設備交互
應用可以使用UsbManager來獲得附件的一個文件描述符,然后使用建立一個輸入輸出流來向文件描述符讀寫數據。這些流代表着附件的輸入輸出endpoints,你同樣應該為此新建一個線程。例子:

1 UsbAccessory mAccessory; 2 ParcelFileDescriptor mFileDescriptor; 3 FileInputStream mInputStream; 4 FileOutputStream mOutputStream; 5 6 ... 7 8 private void openAccessory() { 9 Log.d(TAG, "openAccessory: " + accessory); 10 mFileDescriptor = mUsbManager.openAccessory(mAccessory); 11 if (mFileDescriptor != null) { 12 FileDescriptor fd = mFileDescriptor.getFileDescriptor(); 13 mInputStream = new FileInputStream(fd); 14 mOutputStream = new FileOutputStream(fd); 15 Thread thread = new Thread(null, this, "AccessoryThread"); 16 thread.start(); 17 } 18 }
在線程的run()方法中你可以使用FileInputStream和FileOutputStream對象來讀寫附件,當你從設備上使用FileInputStream對象讀取數據的時候,要確保你的緩沖區足夠大,可以存儲USB包數據,Android附件協議支持包的大小上限是16384bytes,所以你可以選擇簡單的就聲明成這個大小。
5)終止交互
當應用完成了交互或者設備被移除了,就需要通過調用rclose()關閉文件描述符,為了監聽移除時間,需要使用以下監聽器:

1 BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { 2 public void onReceive(Context context, Intent intent) { 3 String action = intent.getAction(); 4 5 if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { 6 UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); 7 if (accessory != null) { 8 // call your method that cleans up and closes communication with the accessory 9 } 10 } 11 } 12 };
在應用中創建監聽器,而不要在manifest中,這樣就可以使得只有在程序運行的時候監聽到該事件,就可以讓移除時間不會被發送給所有的應用程序而只是運行中的程序。
以上就是有關Android USB的相關知識了,具體的開發需要參考SDK中的例子,以及相應的開發文檔。