USB Device的功能很豐富,其支持的協(xié)議越來(lái)越多包括:MTP、ADB、rndis、mass storage、accessory、audio_source、CDROOM等。
在代碼中涉及到的目錄主要有:
1.frameworks/base/services/java/com/android/server/usb/? -----usbService.java用來(lái)管理usb協(xié)議,其通過(guò)property系統(tǒng)與init.xxx.usb.rc通訊。其中UsbDeviceManager.java以及HostManager.java分別管理device和host的設(shè)備。
2.init.xxx.usb.rc這里定義了所有usb device協(xié)議的組合。當(dāng)usb device的協(xié)議發(fā)生變化的時(shí)候,會(huì)設(shè)置sys.usb.config這個(gè)屬性,init.xxx.usb.rc中定義的某種組合會(huì)被觸發(fā),通過(guò)sys節(jié)點(diǎn)來(lái)通知kernel切換USB總線協(xié)議。
我們常用到的有device協(xié)議有ADB、MTP、PTP、MassStorage這幾個(gè),這些都是可以在Setting中開關(guān)或者是切換的。在切換協(xié)議的時(shí)候是調(diào)用UsbDeviceManager中的setCurrentFunctions(String functions, boolean makeDefault)最終設(shè)置sys.usb.config這個(gè)屬性,從而觸發(fā)init.xx.usb.rc去通知kernel切換usb協(xié)議。UsbDeviceManager.java中同時(shí)也監(jiān)聽(tīng)usb事件的uevent,并通過(guò)updateUsbState()發(fā)出UsbManager.ACTION_USB_STATE這個(gè)廣播來(lái)通知MtpReceiver和MountService。其中MtpReceiver負(fù)責(zé)根據(jù)所選擇的usb協(xié)議,啟動(dòng)或者關(guān)閉MtpService。
在accessory模式下,PAD是作為Device設(shè)備的,通常需要一個(gè)支持Accessory的Host設(shè)備(ADK2012等)配合才能工作,可以參考如下谷歌文檔:
http://developer.android.com/guide/topics/connectivity/usb/index.html
http://developer.android.com/guide/topics/connectivity/usb/accessory.html
http://developer.android.com/guide/topics/connectivity/usb/host.html

Accessory模式下Host端代碼可以參考cts/apps/cts-usb-accessory/cts-usb-accessory.c。這里面模擬了一個(gè)Host端的設(shè)備。其思路是調(diào)用system/core/libusbhost/usbhost.c中的usb_host_run()函數(shù),這個(gè)函數(shù)的主要作用就是去監(jiān)控/dev/bus/usb/這個(gè)目錄。
調(diào)用如下接口去查詢/dev/bus/usb其中的設(shè)備是否支持accessory協(xié)議
usb_device_control_transfer(device, USB_DIR_IN | USB_TYPE_VENDOR,
ACCESSORY_GET_PROTOCOL, 0, 0, &protocol, sizeof(protocol), 0);
如果支持就調(diào)用如下接口嘗試將其切換到accessory模式。
usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,ACCESSORY_START, 0, 0, 0, 0, 0);
Accessory模式下Device端的代碼分析:
drivers/usb/gadget/f_accessory.c中收到ACCESSORY_START這個(gè)ioctl后(其實(shí)是由usb中斷傳遞上來(lái)的)就會(huì)發(fā)送ACCESSORY=START的uevent。
static void acc_work(struct work_struct *data)
{
? char *envp[2] = { "ACCESSORY=START", NULL };
? kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
frameworks/base/services/java/com/android/server/usb/UsbDeviceManager.java中,接收到uevent后調(diào)用startAccessoryMode();--->setCurrentFunctions(xxx)-->設(shè)置sys.usb.config這個(gè)屬性后,就觸發(fā)init.xxx.usb.rc去通知kernel切換到accessory模式。
幾個(gè)重要代碼點(diǎn):
1.UsbDeviceManager監(jiān)聽(tīng)DEVPATH=/devices/virtual/android_usb/android0"這個(gè)路徑的UEVENT,
在收到狀態(tài)改變的時(shí)候會(huì)發(fā)出UsbManager.ACTION_USB_STATE這個(gè)broadcast。其中包含connect,configuration狀態(tài)以及當(dāng)前的usb配置的function。
2.在MountService收到ACTION_USB_STATE這個(gè)廣播的時(shí)候,notifyShareAvailabilityChange()會(huì)調(diào)用所有注冊(cè)的listener的bl.mListener.onUsbMassStorageConnectionChanged(avail);
同時(shí)在這里還要處理usb拔出的事件,這里必須把已經(jīng)shared的盤重新Mount回系統(tǒng)中。
3.StorageManager向Mountservice注冊(cè)了listener,其他應(yīng)用又向StorageManager注冊(cè)listener
主要有如下地方:
UsbStorageActivity.java ---UMS開關(guān)界面UI切換
StorageNotification.java----實(shí)現(xiàn)狀態(tài)欄通知(在onUsbMassStorageConnectionChange()中實(shí)現(xiàn),這個(gè)函數(shù)中可以實(shí)現(xiàn)自動(dòng)彈出usbStorageActivity,關(guān)鍵字POP_UMS_ACTIVITY_ON_CONNECT)
TabletStatusBar.java---------向StorageManager注冊(cè)listener,用來(lái)顯示UMS狀態(tài)欄通知
MtpService.java--------------Mtp狀態(tài)變化
幾種不能共存的配置:
1.多用戶和UMS不能共存
----谷歌默認(rèn)的方式是采用fuse將/data/media模擬成用戶盤,這種模式下支持多用戶,但是不能支持UMS。如果要支持UMS那么就不能使用fuse,需要?jiǎng)澇鯱SER分區(qū),通過(guò)Vold來(lái)管理。
目前Android4.4的SDK中通過(guò)BoradConfig.mk中的BUILD_WITH_UMS這個(gè)宏來(lái)在二者中切換。
BUILD_WITH_UMS = true即支持UMS不支持多用戶
BUILD_WITH_UMS = false即支持多用戶但是不支持UMS
2.CDROOM和UMS不能共存
----CDROOM和UMS在kernel中的實(shí)現(xiàn)是類似的,都往/sys/class/android_usb/f_mass_storage/lun/file中寫入內(nèi)容來(lái)與kernel通訊。
目前Android4.4的SDK中通過(guò)BoradConfig.mk中的BUILD_WITH_CDROM來(lái)控制是否打開CDROOM,BUILD_WITH_CDROM_PATH來(lái)設(shè)置iso的路徑。注意BUILD_WITH_UMS和BUILD_WITH_CDROM兩者應(yīng)該是互斥的,不能同時(shí)設(shè)置成true。
當(dāng)usb口作為host使用時(shí),可以連接u盤,鼠標(biāo)/鍵盤,usb音響等設(shè)備,針對(duì)不同的設(shè)備由不同的子系統(tǒng)來(lái)處理。
連接鼠標(biāo)/鍵盤/手柄等輸入設(shè)備時(shí),這些外設(shè)被當(dāng)成是輸入設(shè)備,歸輸入子系統(tǒng)管理。設(shè)備節(jié)點(diǎn)在/dev/input下,輸入事件由InputReader調(diào)用EventHub來(lái)讀取,具體請(qǐng)看EventHub的分析。
外接usb音響等音頻設(shè)備,這些外設(shè)被識(shí)別成音頻設(shè)備,設(shè)備節(jié)點(diǎn)在/dev/snd/下,歸音頻系統(tǒng)管理。
連接usb存儲(chǔ)設(shè)備(u盤,硬盤等)時(shí),設(shè)備節(jié)點(diǎn)在/dev/bus/usb下,由UsbHostManager.java來(lái)管理,簡(jiǎn)單分析如下:
1)frameworks/base/services/java/com/android/server/usb/UsbService.java中的systemReady()調(diào)用mHostManager.systemReady()。
2)frameworks/base/services/java/com/android/server/usb/UsbHostManager.java的systemReady中啟動(dòng)一個(gè)線程來(lái)運(yùn)行monitorUsbHostBus();
frameworks/base/services/jni/com_android_server_UsbHostManager.cpp
static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv *env, jobject thiz)
{
? struct usb_host_context* context =usb_host_init();
?if (!context) {
? ALOGE("usb_host_init failed");
? return;
}
// this will never return so it is safe to pass thiz directly
usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}
其中分別調(diào)用到了system/core/libusbhost/usbhost.c中的usb_host_init(...)和usb_host_run(...)
在usb_host_init()中,最主要的是初始化context->fd = inotify_init();,這個(gè)會(huì)在后面用來(lái)監(jiān)聽(tīng)/dev/bus/usb目錄的創(chuàng)建和刪除在usb_host_run中,主要是添加監(jiān)控的目錄ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);如果發(fā)現(xiàn)目錄有create或者是delete操作,通知回調(diào)函數(shù).
3)在usb_device_added()中,主要是獲取usb設(shè)備的屬性,然后調(diào)用UsbHostManager.java中的usbDeviceAdded(),并將這些usb屬性傳遞上去
env->CallVoidMethod(thiz, method_usbDeviceAdded,deviceName, vendorId, productId, deviceClass,
deviceSubClass, protocol, interfaceArray, endpointArray);
4)在UsbHostManager.java中的usbDeviceAdded()中,主要是創(chuàng)建UsbDevice,如下:
UsbDevice device = new UsbDevice(deviceName, vendorID, productID,deviceClass, deviceSubclass, deviceProtocol, interfaces);
mDevices.put(deviceName, device);
mSettingsManager.deviceAttached(device);
5)frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java中的deviceAttached()函數(shù),主要是檢查系統(tǒng)中是否有安裝能處理UsbManager.ACTION_USB_DEVICE_ATTACHED這個(gè)廣播的activity,并轉(zhuǎn)到該activity.
public void deviceAttached(UsbDevice device) {
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ArrayList matches;
String defaultPackage;
synchronized (mLock) {
matches = getDeviceMatchesLocked(device, intent);
// Launch our default activity directly, if we have one.
// Otherwise we will start the UsbResolverActivity to allow the user to choose.
defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
}
resolveActivity(intent, matches, defaultPackage, device, null);
}
1.2.4libusbhost
libusbhost主要提供與usb設(shè)備通信的接口
struct usb_device *usb_device_open(const char *dev_name) ---打開一個(gè)usb設(shè)備,在/dev/bus/usb/下
void usb_device_close(struct usb_device *device)
void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter)
struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter) --獲取descriptor
int usb_device_claim_interface(struct usb_device *device, unsigned int interface) ----claim一個(gè)interface用于通訊
int usb_device_release_interface(struct usb_device *device, unsigned int interface)
int usb_device_bulk_transfer(struct usb_device *device, --------傳輸數(shù)據(jù)
int endpoint,
void* buffer,
int length,
unsigned int timeout)
int usb_device_control_transfer(struct usb_device *device, ----------控制指令
int requestType,
int request,
int value,
int index,
void* buffer,
int length,
unsigned int timeout)
在java代碼中可以通過(guò)一下文件中提供的接口來(lái)訪問(wèn)usb設(shè)備。
frameworks/base/core/java/android/hardware/usb/UsbManager.java
frameworks/base/core/java/android/hardware/usb/UsbDeviceConnection.java