Android AOA 通道 FW 層建立

  

                  基於LC6500 模塊AOA 通道的建立

 

 

1.AOA 介紹

AOA 爲Android Open Accessory的縮寫,簡單點說,就是Android支持的USB 設備形態中的一種。

一個Andorid系統的AOA模式分爲兩種:

 

  1. HOST模式:

   

Android 設備在HOST模式下,將會提供電源給外部設備並與之建立通信。

 

  1. Accessory模式:

   

Android 設備在Accessory模式下,將與一個可識別Android USB 設備的設備進行連接並進行通信,且將由此設備給予Accessory 模式下的Andorid設備以電源。

2.具體模式解析

 

 

 

 

 

\* MERGEFORMAT

 

 

 

 

 

 

 

 

 

2.1 HOST 模式

    處於HOST 模式下的Android device,通過檢測連接上的USB 設備是否處於Accessory模式,如果處於accessory模式,則直接建立通道;如果不處於Accessory模式,則通過control transfer 要求連接的USB 設備先切換爲accessory模式再建立通道。

我們具體分析一下CLIENT端連接HOST端時未處於Accessory模式下時的流程

注: 因爲涉及到監聽USB device 的狀態,我們一下代碼都依賴於libusb的庫。

2.1.1 監聽USB設備的連接/拔出

int main(int argc, char* argv[]) {

    struct usb_host_context* context = usb_host_init();

... ...

    // this will never return so it is safe to pass thiz directly

    usb_host_run(context, usb_device_added, usb_device_removed, NULL, NULL);

    return 0;

}

 

main()的主要作用就是:

  1. 獲取libusbhost 庫環境;
  2. 調用usb_host_run函數註冊監聽USB device設備的添加或拔出。USB devices添加時,會調用usb_device_added的回調;USB devices拔出時,會調用usb_device_removed的回調。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.1.2 判斷CLIENT端是處於accessory模式

static int usb_device_added(const char *devname, void* client_data) {

... ...

 struct usb_device *device = usb_device_open(devname)

... ...

 vendorId = usb_device_get_vendor_id(device);

 productId = usb_device_get_product_id(device);

 if (vendorId == 0x18D1 || vendorId == 0x22B8 || vendorId == 0x04e8) {

   //CLIENT端支持accessory模式

   if (!sDevice && (productId == 0x2D00 || productId == 0x2D01)){

   //CLIENT端處於accessory模式

   } else {

   //CLIENT端不處於accessory模式

   }

 }

... ...

}

usb_device_added函數的主要作用是:

  1. 獲取連接設備的Vid 和Pid 數值;
  2. 先判斷Vid是否爲0x18D1 或者0x11B8 或者 0x04e8,如果爲以上Vid 中的一種,表示此設備支持accessory模式;如果不爲上述Vid中的一種,則表示不支持accessory模式;
  3. 判斷Pid是否爲0x2D00 或0x2D01中的一種,如果是,則表示設備當前處於accessory模式;如果不是,則表示設備當前不處於accessory模式;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.1.3 USB device不爲accessory設備時

  2.1.3.1 HOST端發送control transfer,要求client端切換accessory模式

代碼如下:

... ...

  uint16_t protocol;

  ret = usb_device_control_transfer(device, USB_DIR_IN | USB_TYPE_VENDOR,

                    ACCESSORY_GET_PROTOCOL, 0, 0, &protocol, sizeof(protocol), 0);

 

通過調用usb_device_control_transfer,發送一個51 control 請求(“Get Protocol”)來區分是否設備支持 Android accessory 協議。 一個非0數值會被返回,如果對於這個設備來說 accessory 協議是支持的。這個請求是一個控制的請求,在 endpoint 0 上 有如下特徵:

    requestType : USB_DIR_IN | USB_TYPE_VENDOR

    request : 51

    value : 0

    index : 0

data : protocol version num ()

 

 

 

 

 

 

  2.1.3.2 HOST端發送control transfer,要求client端啓動特定的application

代碼如下:

... ...

            send_string(device, ACCESSORY_STRING_MANUFACTURER, "Google, Inc.");

            send_string(device, ACCESSORY_STRING_MODEL, "AccessoryChat");

            send_string(device, ACCESSORY_STRING_DESCRIPTION, "Accessory Chat");

            send_string(device, ACCESSORY_STRING_VERSION, "1.0");

            send_string(device, ACCESSORY_STRING_URI, "http://www.android.com");

            send_string(device, ACCESSORY_STRING_SERIAL, "1234567890");

 

            ret=usb_device_control_transfer(device,USB_DIR_OUT| USB_TYPE_VENDOR,ACCESSORY_START, 0, 0, 0, 0, 0);

 

Client端會解析此消息,並在client端篩選出符合的application,具體的步驟在Accessory模式下講解。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.1.4 USB device 爲accessory模式

讀數據:

    while (sDevice && ret >= 0) {

        char    buffer[16384];

 

        ret = usb_device_bulk_transfer(sDevice, endpoint, buffer, sizeof(buffer), 1000);

        if (ret < 0 && errno == ETIMEDOUT)

            ret = 0;

        if (ret > 0) {

            fwrite(buffer, 1, ret, stdout);

            printf("\n");

            fflush(stdout);

        }

}

 

 

寫數據:

    while (ret >= 0) {

        char    buffer[16384];

        char *line = fgets(buffer, sizeof(buffer), stdin);

        if (!line || !sDevice)

            break;

        ret = usb_device_bulk_transfer(sDevice, endpoint, line, strlen(line), 1000);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.2 Accessory模式

2.2.1 切換accessory模式

  2.2.1.1 kernel上報Uevent

Kernel層解析有HOST段發送過來的control protocol,並上報開啓accessory的Uevent

具體流程:

  略... ...

  2.2.1.2 Framework層切換accessory模式

1. UsbDeviceManager 監聽kernel上報的Uevent,“ACCESSORY”關鍵字爲“START”時,開時切換accessory模式;

/frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java

... ...

    private final UEventObserver mUEventObserver = new UEventObserver() {

        @Override

        public void onUEvent(UEventObserver.UEvent event) {

            String state = event.get("USB_STATE");

            String accessory = event.get("ACCESSORY");

            if (state != null) {

                mHandler.updateState(state);

            } else if ("START".equals(accessory)) {

                if (DEBUG) Slog.d(TAG, "got accessory start");

                startAccessoryMode();

            }

        }

};

 

 

  1.  startAccessoryMode()

   private void startAccessoryMode() {

   ... ...

   // 判斷當前系統是否支持accessory模式

   ... ...

   // 確定需要下發的USB系統屬性

        String functions = null;

        if (enableAccessory && enableAudio) {

            functions = UsbManager.USB_FUNCTION_ACCESSORY + ","

                    + UsbManager.USB_FUNCTION_AUDIO_SOURCE;

        } else if (enableAccessory) {

            functions = UsbManager.USB_FUNCTION_ACCESSORY;

        } else if (enableAudio) {

            functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE;

        }

     // 調用setCurrentFunctions下發的USB系統屬性

        if (functions != null) {

            mAccessoryModeRequestTime = SystemClock.elapsedRealtime();

            setCurrentFunctions(functions);

        }

    }

 

 

 startAccessoryMode函數的主要作用在於:

 

 1.調用nativeGetAccessoryStrings()

/frameworks/base/services/core/jni/com_android_server_UsbDeviceManager.cpp

static jobjectArray android_server_UsbDeviceManager_getAccessoryStrings(JNIEnv *env,jobject /* thiz */)

{

    ... ...

    set_accessory_string(env, fd,ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0);

    set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1);

    set_accessory_string(env, fd,ACCESSORY_GET_STRING_DESCRIPTION, strArray, 2);

    set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3);

    set_accessory_string(env, fd, ACCESSORY_GET_STRING_URI, strArray, 4);

    set_accessory_string(env, fd, ACCESSORY_GET_STRING_SERIAL, strArray, 5);

    ... ...

}

解析之前在2.1.3.2 中提及的有HOST端發送的特定application應用的消息,其中:

通過MANUFACTURER_STRING 和MODEL_STRING 兩個標誌位來判斷accessory是否enable。

 

  1. 調用nativeGetAudioMode()

/frameworks/base/services/core/jni/com_android_server_UsbDeviceManager.cpp

static jint android_server_UsbDeviceManager_getAudioMode(JNIEnv* /* env */, jobject /* thiz */)

{

    int fd = open(DRIVER_NAME, O_RDWR);

... ...

    int result = ioctl(fd, ACCESSORY_GET_AUDIO_MODE);

    close(fd);

    return result;

}

 

其中: #define DRIVER_NAME "/dev/usb_accessory"

 

nativeGetAudioMode函數的作用就是獲取"/dev/usb_accessory"節點中的audio文件流,如果此節點下有視頻流,這說明HOST端正在向client傳送audio文件流,因此USB 的設備狀態中應該包含“audio_source”屬性,已確保USB 驅動中對audio流的傳送的使能。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.2.2 應用層進行通信

  2.2.2.1 應用被喚起

1.在應用的res/xml/中添加accessory_filter.xml文件:

<resources>

    <usb-accessory manufacturer="Google, Inc." model="AccessoryChat" type="Sample Program" version="1.0" />

</resources>

 

只要在accessory_filter.xml中manufacturer,model等關鍵字和在2.1.3.1 中由HOST端發送的篩選信息相同則被喚起。

 

2.需要在AndroidManifest.xml中註明:

<uses-feature android:name="android.hardware.usb.accessory" />

... ...

<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"

                android:resource="@xml/accessory_filter" />

 

  2.2.2.2 應用監聽USB中accessory模式的開啓

   private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

     public void onReceive(Context context, Intent intent) {

       if (ACTION_USB_PERMISSION.equals(intent.getAction())) {

... ...

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

         if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {

                        if (accessory != null) {

                            openAccessory(accessory);

                        }

                    }

          

 

應用監聽到USB 屬性中包含“accessory”屬性,則表示應用所運行的CLIENT端已和HOST端開啓AOA通道。

        

  2.2.2.3 應用進行AOA通道下的數據傳輸

應用調用最終會調用UsbManager.openAccessory()獲取accessory的FileDescriptor。

 

    private void openAccessory(UsbAccessory accessory) {

      ... ...

        mFileDescriptor = mUsbManager.openAccessory(accessory);

      ... ...

            FileDescriptor fd = mFileDescriptor.getFileDescriptor();

            mInputStream = new FileInputStream(fd);

            mOutputStream = new FileOutputStream(fd);

      ... ...

}

 

UsbManager.openAccessory()最終會調用com_android_server_UsbDeviceManager:android_server_UsbDeviceManager_openAccessory:

 

static jobject android_server_UsbDeviceManager_openAccessory(JNIEnv *env, jobject /* thiz */)

{

    int fd = open(DRIVER_NAME, O_RDWR);

... ...

    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);

... ...

    return env->NewObject(gParcelFileDescriptorOffsets.mClass,

        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);

}

 

其中 #define DRIVER_NAME "/dev/usb_accessory"

 

由此可以看到,應用在AOA 通道下的文件讀寫即是對 "/dev/usb_accessory" 這個節點下fd的讀/寫。

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