Android系統編程入門系列之硬件交互——通信硬件USB

硬件交互的首篇對設備硬件的分類中,互聯通信系列硬件主要用來與其他設備進行數據交互。從本文開始,將重點介紹該系列相關硬件。

互聯通信系列硬件

根據硬件的可通信距離,由近及遠分為USB、NFC、藍牙、WLAN,SIM卡槽,這些硬件之間的功能原理及關係可以查找其他資料詳細學習。總之,他們為當前設備與其他設備的交互搭建了橋樑,只要雙方設備均遵循該系列硬件的協議,就可以在硬件層互相通信,而設備上的Android操作系統便會將硬件層的數據轉換為應用層數據,進而與應用程序交互。這樣也就實現了兩個不同設備上的應用程序間的交互方案。理論上這個方案是可行的,那實際各硬件的使用方式分別是怎麼樣的呢?

USB接口

在應用程序中與USB硬件的交互,系統提供了兩種方式,包括將該應用程序所在設備的USB接口作為主機模式,和該應用程序所在設備的USB接口作為配件模式。在主機模式下,該應用程序所在設備通過USB接口為其他接入的USB設備供電,通常連接沒有自帶電源的設備(比如U盤)時啟用此種模式;反之在配件模式下,是該應用程序所在設備接收通過USB接口接入的其他USB設備的電源提供,通常在連接有電源的設備(比如筆記本電腦)時啟用此模式。理論上這兩種模式只是針對USB硬件的供電方不同而區分,均可以在USB接口連接的兩個設備之間的數據傳輸。

主機模式

權限聲明

在應用程序的清單文件中,需要聲明<uses-feature />標籤,並設置其屬性android:name值為"android.hardware.usb.host"。該標籤設置並不是通過應用程序向系統申請權限,而是聲明應用程序需要使用USB主機模式。

使用流程
獲取USB主機設備

主機模式下,其中一種常用情況,應用程序可以監聽插入USB接口的設備,此時可以在需要監聽的界面Activity對應的清單文件註冊信息中增加指定的接收意圖,意圖值為android.hardware.usb.action.USB_DEVICE_ATTACHED。這樣在USB設備接入後,系統會發送上述意圖值的廣播,從而啟動當前界面Activity

在啟動後的界面Activity中,可以調用getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE)系列方法,獲取android.hardware.usb.UsbDevice USB設備類的對象。

主機模式下另外一種情況,是直接獲取已連接的USB設備。在能獲取上下文環境Context對象的地方,調用該對象的getSystemService(String name),並指定參數值 nameContext.USB_SERVICE="usb",獲得android.hardware.usb.UsbManagerUSB管理類的對象。調用該對象的getDeviceList()方法,返回List<UsbDevice>,同樣能獲取到當前設備連接的所有USB硬件對象的列表數據。

在直接獲取已連接USB設備的情況下,應用程序需要主動向用戶申請設備通信權限。通過USBManager設備管理類對象的requestPermission(UsbAccessory accessory, PendingIntent pi)方法,在用戶同意授權後,才能繼續後續設備通信操作。

USB設備通信

在拿到UsbDevice類型的USB設備類對象,和UsbManager管理類對象之後,調用USB管理類對象的openDevice(UsbDevice device)方法,得到android.hardware.usb.UsbDeviceConnection USB設備連接對象,其參數 device 便是要通信的USB設備對象。

建立UsbDeviceConnection設備連接對象後,通信過程中還需要分別藉助android.hardware.usb.UsbInterface USB接口類和android.hardware.usb.UsbEndpoint USB端點類。

在獲取UsbDevice類型的設備類對象後,調用其getInterface(int index)方法獲取UsbInterfaceUSB接口類對象,其中參數 index 是當前USB設備可獲取的所有接口數量中的索引值,而獲取UsbDevice的所有接口數量,可根據另外一個方法getInterfaceCount()查看。

在獲取UsbInterface類型的接口對象後,調用其getEndpoint(int index)方法獲取UsbEndpointUSB端點類對象,其中參數 index 是當前USB接口中可獲取的所有斷點數量中的索引值,而獲取UsbInterface的所有斷點數量,可根據另外一個方法getEndpointCount()查看。

通過梅開二度獲取USB接口對象和USB端點對象之後,首先要佔用USB設備資源。繼續調用UsbDeviceConnection連接對象的claimInterface(UsbInterface intf, boolean force)方法,其中參數 intf 便是上文獲取UsbInterface接口類對象,參數 force 標明是否強制佔用。返回boolean類型的結果表示佔用是否成功。

在佔用USB設備資源之後,就可以接收USB設備的通信數據了。調用UsbDeviceConnection連接對象的bulkTransfer (UsbEndpoint endpoint, byte[] buffer, int offset, int length, int timeout)方法。參數 endpoint 便是上文獲取UsbEndpoint端點類對象;參數 buffer 用以存儲通信中的二進制數組;參數 offset 可選項,默認值為0,用以標記存放數組 buffer 的起始位置;參數 length 用以標記存放數組的長度;參數 timeout 作為通信連接的最大時長。返回通信過程中實際傳輸的數據長度。

通信結束後,只需要關閉連接並釋放佔用的設備資源。調用UsbDeviceConnection連接對象的close()方法可以關閉通信連接。而調用該對象的releaseInterface(UsbInterface intf)方法可以釋放佔用的UsbInterface設備接口類型的參數 intf 對象。

配件模式

權限聲明

在應用程序的清單文件中,需要聲明<uses-feature />標籤,並設置其屬性android:name值為"android.hardware.usb.accessory"。該標籤設置並不是通過應用程序向系統申請權限,而是聲明應用程序需要使用USB配件模式。

使用流程
獲取USB配件

配件模式下,其中一種常用情況,應用程序可以監聽插入USB接口的設備,此時可以在需要監聽的界面Activity對應的清單文件註冊信息中增加指定的接收意圖,意圖值為android.hardware.usb.action.USB_ACCESSORY_ATTACHED。這樣在USB設備接入後,系統會發送上述意圖值的廣播,從而啟動當前界面Activity

在啟動後的界面Activity中,可以調用getIntent().getParcelableExtra(UsbManager.EXTRA_ACCESSORY)系列方法,獲取android.hardware.usb.UsbAccessory USB配件類的對象。

同樣在配件模式下另外一種情況,是直接獲取已連接的USB配件。在能獲取上下文環境Context對象的地方,調用該對象的getSystemService(String name),並指定參數值 nameContext.USB_SERVICE="usb",獲得android.hardware.usb.UsbManagerUSB管理類的對象。調用該對象的getAccessoryList()方法,返回List<UsbAccessory>,同樣能獲取到當前設備連接的所有USB配件對象的列表數據。

與主機模式類似,在直接獲取已連接USB配件的情況下,應用程序需要主動向用戶申請設備通信權限。通過USBManager設備管理類對象的requestPermission(UsbAccessory accessory, PendingIntent pi)方法,在用戶同意授權後,才能繼續後續設備通信操作。

USB設備通信

在拿到UsbAccessory類型的USB配件類對象,和UsbManager管理類對象之後,調用USB管理類對象的openAccessory(UsbAccessory accessory)方法,得到android.os.ParcelFileDescriptor 數據流化的文件描述符類的對象,其參數 accessory 便是要通信的USB配件對象。

在獲取ParcelFileDescriptor數據流化的文件描述符類的對象後,調用其getFileDescriptor()方法得到java.io.FileDescriptor普通的文件描述符類型的對象,之後通過該對象創建基本的文件輸入流java.io.FileInputStream對象以讀取USB配件中的數據,或者創建基本的文件輸出流java.io.FileOutputStream對象以將數據寫入USB配件中。

通信結束後,只需要關閉數據流化的文件描述符的佔用即可。通過調用ParcelFileDescriptor對象的close()方法以實現該操作。

主機模式與配件模式的區別

代碼軟件層

主機模式下,主要使用UsbDevice類,可以獲取所連接USB設備的詳細信息。
配件模式下,只能使用UsbAccessory類,只能獲取連接USB設備的基本信息。

硬件層

主機模式下,由應用程序所在的設備向主線供電,並向連接的USB設備供電。
配件模式下,由連接的USB設備作為主機向主線供電,並嚮應用程序所在的設備供電。