翻譯Android USB HOST API

翻譯Android USB HOST APIhtml

 

源代碼地址:http://developer.android.com/guide/topics/connectivity/usb/host.htmljava

 

譯者注:翻譯的好很差不是過重要,重點是在翻譯的過程當中會把每句話都看認真看一遍,或者說是抱着翻譯的思想來完畢一個讀懂的目的。linux

 

USB Host通訊android

當你的可供電Android設備處理USB host模式時,它擔任着爲USB總線供電,枚舉鏈接的USB從設備等等一個主設備應用的工做。app

Android 3.1及之後版本號開始支持USB host模式。異步

 

API概述ide

開始以前,有必要弄明確之後要用到的類。下表中描寫敘述了包括在android.hardware.usb包中的USB host APIsui

 

1. USB host APIs.this

Classspa

Description

UsbManger

贊成你枚舉USB從設備以及和其通訊

UsbDevice

表明一個USB從設備,獲取其信息。

如接口,端點等等。

UsbInterface

表明一個USB從設備的接口,一個設備可以擁有多個接口用於通訊。

UsbEndpoint

表明接口的一個端點。它是該端點一個通訊通道。

一個接口可以擁有一個或者多個端點,一般輸入輸出端點用於和設備進行雙向通訊。

UsbDeviceConnection

表明一個設備的鏈接,經過端點數據傳輸,該類贊成你發送接收數據,通訊方式可以是同步或者異步。

UsbRequest

表明經過UsbDeviceConnection通訊的異步請求。

UsbConstants

定義USB常量相應Linux內核中linux/usb/ch9.h中的宏。

 

多數狀況下。在和USB設備進行通訊的時候,你需要使用這裏所有的類(UsbRequest僅僅用在異步通訊一個請求)。

一般是這種,使用UsbManager來獲取UsbDevice。肯定了設備。需要查找用於通訊接口的UsbInterfaceUsbEndpoint。一旦獲取了恰當的端點。打開UsbDeviceConnectionUSB設備進行通訊。

 

下列表中描寫敘述了使用USB host APIs以前需要加入到應用程序的manifest文件裏的內容:

由於並不是所有的Android帶供電設備確保支持USB host APIs,加入 <uses-feature>對象來代表你的應用使用了android.hardware.usb.host特性。

SDK最低版本號設置爲12或者更高。USB host APIs不支持更老版本號的API

假設你想讓你的應用檢測一個USB設備的話,在主Activity指定 <intent-filter> 和<meta-data> 對象去匹配android.hardware.usb.action.USB_DEVICE_ATTACHED。

<meta-data>對象指向一個外部的XML資源文件,文件裏描寫敘述你想要探測的USB設備的過濾信息。

 

在這個XML資源文件裏,爲你想要過濾的USB設備聲明<usb-device>對象。

下列表中描寫敘述了<usb-device>的屬性。

一般,使用vendor 和 product ID過濾特殊設備。使用類。子類和協議來過濾一組設備。比方大容量存儲設備(優盤)或者數碼相機。你可以全然指定這個屬性值。也可以一個也不指定。沒有屬性值說明會過濾每個一個USB設備。因此說。你的應用程序有這個需求的狀況下可以指定。


  • vendor-id
  • product-id
  • class
  • subclass
  • protocol (device or interface)

 

保存資源文件到res/xml/文件夾下。資源文件名稱(不包括.xml後綴)必須要和<meta-data>中指定的一樣。

XML資源文件格式假設下面演示樣例:

<manifest ...>
    <uses-feature android:name="android.hardware.usb.host" />
    <uses-sdk android:minSdkVersion="12" />
    ...
    <application>
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
            </intent-filter>

            <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
        </activity>
    </application>
</manifest>

在本例中,下面資源文件應該保存在res/xml/device_filter.xml中,指定需要過濾USB設備的屬性:

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

<resources>
    <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" />
</resources>

 

設備工做

 

USB設備鏈接到Android上時,無論你的應用程序是否對此USB設備感興趣Android系統都會檢測到。

你可以和設備創建通訊。你的應用程序需要作例如如下事情:

1.探測USB設備經過意圖過濾器檢測用戶鏈接的設備。或者枚舉已經鏈接的USB設備。

2.假設以前沒有請求過,向用戶請求權限以鏈接USB設備。

3.和USB設備進行通訊。經過接口端點讀寫數據。

 

探測設備

 

應用程序可以經過兩種方式進行USB設備的探測,使用意圖過濾器監聽用戶鏈接設備或者枚舉已經鏈接的USB設備。

前者可以方便的實現本身主動檢測設備。

假設想列出所有的已鏈接設備或者沒有使用意圖過濾器過濾的設備,可以使用枚舉設備來完畢。

 

使用意圖過濾器

 

探測設備可以指定意圖過濾器來過濾android.hardware.usb.action.USB_DEVICE_ATTACHED意圖。此外需要指定一個資源文件描寫敘述USB設備,比方product and vendor ID. 當用戶鏈接上和你探測的設備匹配時,系統會彈出一個對話框詢問用戶是否贊成啓動的你的應用程序。假設贊成,你的應用程序將會本身主動被賦予權限和該設備通訊,直到設備被移除。

 

下面演示樣例顯示怎樣描寫敘述意圖過濾器:

<activity ...>
...
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
    </intent-filter>

    <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/device_filter" />
</activity>

下面演示樣例顯示怎樣描寫敘述資源文件。指定你感興趣的USB設備:

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

<resources>
    <usb-device vendor-id="1234" product-id="5678" />
</resources>

 

在你的Activity中。你要得到UsbDevice,它表明使用意圖探測到的設備。如:

 

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

 

枚舉設備

 

在你的應用程序執行時,假設對因此當前正在鏈接的USB設備感興趣。可以枚舉設備。枚舉設備使用的是getDeviceList()方法獲取所有鏈接設備的哈希表。假設你想獲取一個設備可以依據USB設備的名字爲keyword進行索引。

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...  
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");

必要時,你從哈希表中迭代設備依次處理每個設備:

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
    UsbDevice device = deviceIterator.next()
    //your code
}

 

獲取權限與設備進行通訊

 

與設備通訊以前。你的應用程序必須從用戶那裏獲取權限。

| 注:假設你的應用程序使用意圖過濾器去探測鏈接的USB設備,假設用戶贊成處理意圖,那麼你的應用程序本身主動會獲取權限。

反之,通訊以前你必須向用戶請求權限。

在一些狀況下請求權限是必須的,好比你的應用程序枚舉已經鏈接的USB設備以及與設備通訊。通訊以前你需要檢查權限。不然假設用戶權限拒絕你將會收到一個執行錯誤。

申請權限。首先建立建立一個廣播接收器。

調用requestPermission()時,接收器監聽廣播意圖。調用requestRermission()向給用戶顯示一個對話框請求鏈接該設備的權限。

下列代碼顯示怎樣建立這樣一個廣播接收器:

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) {
                UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(device != null){
                      //call method to set up device communication
                   }
                } 
                else {
                    Log.d(TAG, "permission denied for device " + device);
                }
            }
        }
    }
};

 

加入例如如下代碼到activityonCreate()方法中註冊廣播接收器:

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

 

UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);

 

當用戶回覆對話框,你的廣播接收器會收到的內容爲EXTRA_PERMISSION_GRANTED意圖。它是一個布爾表明答案。通訊以前檢查這個額外值是否爲真。

 

與設備通訊

 

通訊方式可以是同步或者異步。

在這兩種狀況下,你應該建立一個線程運行所有數據的傳輸。不要堵塞界面線程。

創建一個通訊。你需要獲取需要通訊設備的UsbInterfaceUsbEndpoint,以及使用UsbDeviceConnection申請端點。通常地,你的代碼應該是:

  • 檢查UsbDevice對象的屬性,比方 product ID, vendor ID或者設備類別。以識別是否是需要通訊的設備。
  • 假設肯定了是要通訊的設備,檢索出你需要通訊設備的接口。以及該接口的端點。接口可以有一個或者多個端點,一般會有輸入輸出各一的雙向通訊的端點。
  • 假設肯定了端點。在該端點上打開 UsbDeviceConnection。 
  • 使用bulkTransfer()或者controlTransfer()方法將需要傳輸的數據提供到端點上。

    你需要將上述操做實現在其餘線程以避免堵塞當前主界面線程。

    不少其餘關於Android中線程的使用,見Processes and Threads.

 

如下的代碼片斷簡單的實現了同步傳輸數據。

你的代碼應該使用不少其它邏輯上的來查找出正確的通訊接口和端點,相同需要在主界面之外的線程中進行數據的傳輸:

private Byte[] bytes
private static int TIMEOUT = 0;
private boolean forceClaim = true;

...

UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = mUsbManager.openDevice(device); 
connection.claimInterface(intf, forceClaim);
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread

發送異步數據。使用UsbRequest類初始化(initialize)和排隊(queue)等候異步請求。而後使用requestWait()等待結果。

 

不少其它信息見AdbTest樣例,它顯示了怎樣進行塊傳輸的方法異步通訊。以及MissleLauncher sample它顯示了怎樣監聽中斷端點異步。

 

終止通訊

 

當完畢了通訊或者設備被移除了,調用releaseInterface()close()來關閉UsbInterfaceUsbDeviceConection。監聽設備移除事件,建立一個廣播接收器例如如下:

 

BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction(); 

      if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                // call your method that cleans up and closes communication with the device
            }
        }
    }
};

建立廣播接收器是在應用程序內部,而非manifest中。僅在應用程序程序執行時處理設備移除事件。

如此,移除事件僅僅會發送給正在執行的應用而不是所有的應用。

 

感悟:

  • 與原文無關純屬我的感想,USB通訊越向下越複雜。

    Android封裝好一些,其次是libusb。再而後就是Linux內核中進行的USB通訊。依次需要了解的知識多少都不同,比方這個完整的通訊過程當中並沒實用到urb等等。

  • 眼下測試的Android4.2.2上通訊的最大塊爲:16KiB。因此數據要切割爲16KiB大小進行傳輸。
  • 文件裏提到的那個樣例AdbTestMissleLauncher的源代碼位於:development/samples/USB/中。前者功能是讀取usb設備(相同也是Android系統)LOG,後者功能是控制一個火箭發射器玩具。
  • Android這套API並不能檢測所有的USB設備,有些設備檢測不到。比方無線數據的適配器。眼下測試成功的有打印機。
  • 關於對其瞭解的程度,我一直有個毛病就是了解一個東西沒有上限。相似寫文章沒有主線。想到哪裏就深刻到哪裏。我要逐漸改點這個習慣。

    我對這個的練習是將數據發送打印機。完畢打印。當我剛剛實現的時候,我就又開始幻想我是否是可以作一個很是牛叉的USB打印APP了。這個是可以實現的,但並不是眼下的主要任務,假設我堅持作。很是多東西都需要去實現。這也違背了我當前不過了解Android USB host API的初衷。還好,此次我剎住了閘。畢竟我此次是真實的瞭解了,以前只知道在Linux內核中的通訊以及基於Libusb通訊,直接使用java通訊的優勢是解析xml等等在C語言中相對複雜的東西到java中都不顯示覆雜了。

  • l 測試APK和上文提到的那兩個樣例打包上傳到Here,方便那些沒有Android系統源代碼的同志。
相關文章
相關標籤/搜索