我们将针对USB模块API的使用流程,从如下几个点进行初步分析:
- 设备连接及断开广播的触发发送;
- 获取DeviceList;
- 请求权限;
- 打开设备传输数据;
设备连接及断开广播的触发发送
通过注册如下两种广播,可以分别接收USB连接及断开的广播:
- android.hardware.usb.action.USB_DEVICE_ATTACHED
- android.hardware.usb.action.USB_DEVICE_ATTACHED
UsbHostManager#systemReady
服务启动时,调用到 UsbHostManager#systemReady,
-
此时会
启动一个线程
执行jni方法
monitorUsbHostBus
// frameworks/base/services/usb/java/com/android/server/usb/UsbHostManager.java
// com.android.server.usb.UsbHostManager#systemReady
public void systemReady() {
synchronized (mLock) {
// Create a thread to call into native code to wait for USB host events.
// This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
Runnable runnable = this::monitorUsbHostBus;
new Thread(null, runnable, "UsbService host thread").start();
}
}
private native void monitorUsbHostBus();
monitorUsbHostBus
monitorUsbHostBus 的实现如下:
-
先调用 usb_host_init 获得一个 usb_host_context 的结构体对象;
-
然后执行 usb_host_run 方法来初始化对于usb设备的监听(基于inotify机制对文件系统进行监听),当设备添加或者移除时,会触发对应的回调函数(usb_device_added和usb_device_removed)。
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);
}
usb_host_context 结构说明
定义位于:
system/core/libusbhost/usbhost.c
// system/core/libusbhost/usbhost.c
struct usb_host_context {
int fd;
usb_device_added_cb cb_added;
usb_device_removed_cb cb_removed;
void *data;
int wds[MAX_USBFS_WD_COUNT];
int wdd;
int wddbus;
};
其中 usb_device_added_cb 及 usb_device_removed_cb 是两个函数指针,用作回调函数。函数参数为 dev_name,client_data,返回值为int,其定义位于:
// system/core/libusbhost/include/usbhost/usbhost.h
/* Callback for notification when new USB devices are attached.
* Return true to exit from usb_host_run.
*/
typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data);
/* Callback for notification when USB devices are removed.
* Return true to exit from usb_host_run.
*/
typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data);
usb_host_init 方法调用
- 分配 usb_host_context 结构体内存空间;
- 将 usb_host_context 的 fd 指向 inotify_init 返回的文件描述符,以便后续监听文件变化;
// system/core/libusbhost/usbhost.c
struct usb_host_context *usb_host_init()
{
struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context));
if (!context) {
fprintf(stderr, "out of memory in usb_host_context\n");
return NULL;
}
context->fd = inotify_init();
if (context->fd < 0) {
fprintf(stderr, "inotify_init failed\n");
free(context);
return NULL;
}
return context;
}
inotify 机制的大体流程如下:
- 调用 inotify_init 函数创建一个 inotify 句柄。
- 获取要监听的文件或目录路径,并且通过 inotify_add_watch 函数把其添加到 inotify 中进行监听。
- 在一个无限循环中,通过 read 函数读取被监听的文件或目录的变动事件 ;
参考:
usb_host_run 方法调用
-
通过 usb_host_load 来初始化usb host的监听列表,主要包括如下动作:
- 为 usb_host_context 设置添加和移除的两个回调;
- 通过 inotify_add_watch 方法,监听 /dev 目录下的文件创建及删除操作,将返回的监控描述项存储到 context -> wdd
- 然后通过 inotify_add_watch 方法监听 /usb 目录下所有文件创建及删除操作,将返回的监控描述项存储到 context -> wds;
- 从 /usb 目录中查找已经存在的usb设备,找到的话就调用 usb_device_added_cb 方法进行通知回调;
- 所有的 inotify_add_watch 添加的watch都会与 context->fd 关联;
-
usb_host_read_event 会循环不断的执行,通过读取inotify文件描述符,来监听文件变化事件;
- 通过read inotify_init 初始化的文件描述符(context->fd ),来监听文件的变化事件,然后相应的调用 usb_device_added_cb 或 usb_device_removed_cb 回调函数来通知USB的插拔事件
-
完成后会相应的调用 inotify_rm_watch 来移除监听器;
这里需要注意的是, Java层调用jni时,起了一个新的线程,而 usb_host_read_event 是位于一个while循环中的,只要退出标志不为true,则会一直执行,所以能实现usb设备添加及移除的持续监听;
问题:
这里是以 inotify_add_watch 添加 /bus 目录的watch失败作为退出标志,为什么?
// system/core/libusbhost/usbhost.c
void usb_host_run(struct usb_host_context *context,
usb_device_added_cb added_cb,
usb_device_removed_cb removed_cb,
usb_discovery_done_cb discovery_done_cb,
void *client_data)
{
int done;
done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data);
// 注意这里是一个循环,通过done标志来决定是否退出
while (!done) {
done = usb_host_read_event(context);
}
} /* usb_host_run() */
#define DEV_DIR "/dev"
#define DEV_BUS_DIR DEV_DIR "/bus"
#define USB_FS_DIR DEV_BUS_DIR "/usb"
int usb_host_load(struct usb_host_context *context,
usb_device_added_cb added_cb,
usb_device_removed_cb removed_cb,
usb_discovery_done_cb discovery_done_cb,
void *client_data)
{
int done = 0;
int i;
context->cb_added = added_cb;
context->cb_removed = removed_cb;
context->data = client_data;
/* watch for files added and deleted within USB_FS_DIR */
context->wddbus = -1;
for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
context->wds[i] = -1;
/* watch the root for new subdirectories */
context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
/* check for existing devices first, after we have inotify set up */
done = find_existing_devices(added_cb, client_data);
return done;
} /* usb_host_load() */
// 具体细节还需要梳理
int usb_host_read_event(struct usb_host_context *context)
{
struct inotify_event* event;
char event_buf[512];
char path[100];
int i, ret, done = 0;
int offset = 0;
int wd;
ret = read(context->fd, event_buf, sizeof(event_buf));
if (ret >= (int)sizeof(struct inotify_event)) {
while (offset < ret && !done) {
event = (struct inotify_event*)&event_buf[offset];
done = 0;
wd = event->wd;
if (wd == context->wdd) {
if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) {
context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);
if (context->wddbus < 0) {
// 此处设置为1,可退出 usb_host_run
done = 1;
} else {
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
done = find_existing_devices(context->cb_added, context->data);
}
}
} else if (wd == context->wddbus) {
if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) {
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
done = find_existing_devices(context->cb_added, context->data);
} else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) {
for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {
if (context->wds[i] >= 0) {
inotify_rm_watch(context->fd, context->wds[i]);
context->wds[i] = -1;
}
}
}
} else if (wd == context->wds[0]) {
i = atoi(event->name);
snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name);
D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ?
"new" : "gone", path, i);
if (i > 0 && i < MAX_USBFS_WD_COUNT) {
int local_ret = 0;
if (event->mask & IN_CREATE) {
local_ret = inotify_add_watch(context->fd, path,
IN_CREATE | IN_DELETE);
if (local_ret >= 0)
context->wds[i] = local_ret;
done = find_existing_devices_bus(path, context->cb_added,
context->data);
} else if (event->mask & IN_DELETE) {
inotify_rm_watch(context->fd, context->wds[i]);
context->wds[i] = -1;
}
}
} else {
for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) {
if (wd == context->wds[i]) {
snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name);
if (event->mask == IN_CREATE) {
D("new device %s\n", path);
done = context->cb_added(path, context->data);
} else if (event->mask == IN_DELETE) {
D("gone device %s\n", path);
done = context->cb_removed(path, context->data);
}
}
}
}
offset += sizeof(struct inotify_event) + event->len;
}
}
return done;
} /* usb_host_read_event() */
usb_device_added & usb_device_removed 回调
从前面的分析可以知道,我们UsbService 启动时,会启动一个线程,对usb相关的设备目录进行监听,但有设备添加或者移除时,会调用 usb_device_added 及 usb_device_removed 回调来通知对应的USB设备的添加或者移除的事件。
我们先看下 usb_device_removed 的实现:
static int usb_device_removed(const char *devAddress, void* clientData) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject thiz = (jobject)clientData;
jstring deviceAddress = env->NewStringUTF(devAddress);
// 调用 UsbHostManager#usbDeviceRemoved 方法
env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
env->DeleteLocalRef(deviceAddress);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
clientData是什么?
clientData 最初经由 monitorUsbHostBus(JNIEnv* /* env */, jobject thiz) 传递,后续一直在方法调用之间传递,在回调 add 及 remove 事件时,也会传递过来;这个 thiz 实际上就是 monitorUsbHostBus 方法的调用对象,即
UsbHostManager 。
static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
usb_device_removed
移除的逻辑比较简单,实际上就是调用了Java层的 usbDeviceRemoved 方法通知Java层USB设备移除;
看下Java层的实现:
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
private void usbDeviceRemoved(String deviceAddress) {
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
// 这里发送广播
mPermissionManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
ConnectionRecord current = mConnected.get(deviceAddress);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
}
}
}
广播发送 –
ACTION_USB_DEVICE_DETACHED
其他的部分我们先不关注,看下 PermissionManger.usbDeviceRemoved 方法,可以看到
此处发送了 UsbManager.ACTION_USB_DEVICE_DETACHED 的广播
。
// frameworks/base/services/usb/java/com/android/server/usb/UsbPermissionManager.java
/**
* Remove temporary access permission and broadcast that a device was removed.
*
* @param device The device that is removed
*/
void usbDeviceRemoved(@NonNull UsbDevice device) {
synchronized (mPermissionsByUser) {
for (int i = 0; i < mPermissionsByUser.size(); i++) {
// clear temporary permissions for the device
mPermissionsByUser.valueAt(i).removeDevicePermissions(device);
}
}
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
usb_device_added
同 usb_device_removed 一样,usb_device_added 最后也是调用了UsbHostManager的对应的Java方法(UsbDeviceAdded)
// frameworks/base/services/usb/java/com/android/server/usb/UsbHostManager.java
private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
byte[] descriptors) {
synchronized (mLock) {
UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
mPermissionManager, newDeviceBuilder.serialNumber);
UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
serialNumberReader.setDevice(newDevice);
mDevices.put(deviceAddress, newDevice);
ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
if (usbDeviceConnectionHandler == null) {
getCurrentUserSettings().deviceAttached(newDevice);
} else {
getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
usbDeviceConnectionHandler);
}
mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
}
return true;
}
广播发送 – ACTION_USB_DEVICE_ATTACHED
上面的代码中我们省略的若干代码,在执行 getCurrentUserSettings() 的 deviceAttached或deviceAttachedForFixedHandler 的时候,会发送对应的USB设备添加的广播;
// frameworks/base/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
public void deviceAttached(UsbDevice device) {
final Intent intent = createDeviceAttachedIntent(device);
// Send broadcast to running activities with registered intent
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
resolveActivity(intent, device, true /* showMtpNotification */);
}
private static Intent createDeviceAttachedIntent(UsbDevice device) {
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
return intent;
}
总结
- UsbService启动时,启动一个线程,对USB相关设备文件目录进行循环监听;
- 当有设备插拔(即文件添加、移除)时,回调UsbHostManager对应的deviceAdded方法或者deviceRemoved方法进行通知;
- 在UsbHostManager 的deviceAdded或者deviceRemoved回调方法中发送对应的设备ATTACHED或DETACHED 广播;
细节补充
UsbHostManager#systemReady 如何被触发?
待补充
inotify 机制详解(USB中应用)
待补充
usb_device_added 详解
usb_device_added 中会有UsbDevice的构造流程,这里展开一下;
devAddress 格式
设备地址的形式如下:
/bus/(busname)/(deviceName)
clientData
上面已有解释,实际上时UsbHostManager 的jobject对象;是为了能够回调时能调用对应Java实例的对应方法的。下面的thiz就是clientData,即 UsbHostManager
env->CallBooleanMethod(thiz, method_usbDeviceAdded,
deviceAddress, classID, subClassID, descriptorsArray);
流程梳理
-
使用 usb_device_open 打开usb设备文件
- 首先检查usb文件(access);
- 以指定的权限打开usb设备文件,并存储文件描述符及是否可写;
- 从设备文件中read设备描述,设备名称等信息,构造一个usb_device 的结构体对象进行存储;
- 读取设备描述符中的各种信息,如classId,subClassId,将这些信息转换成java可识别的格式,然后调用Java方法 UsbHostManager#usbDeviceAdded
- 关闭usb设备对应的文件描述符,释放对应的结构体内存空间;
static int usb_device_added(const char *devAddress, void* clientData) {
struct usb_device *device = usb_device_open(devAddress);
if (!device) {
ALOGE("usb_device_open failed\n");
return 0;
}
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
int classID = deviceDesc->bDeviceClass;
int subClassID = deviceDesc->bDeviceSubClass;
// get the raw descriptors
int numBytes = usb_device_get_descriptors_length(device);
if (numBytes > 0) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject thiz = (jobject)clientData;
jstring deviceAddress = env->NewStringUTF(devAddress);
jbyteArray descriptorsArray = env->NewByteArray(numBytes);
const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
env->CallBooleanMethod(thiz, method_usbDeviceAdded,
deviceAddress, classID, subClassID, descriptorsArray);
env->DeleteLocalRef(descriptorsArray);
env->DeleteLocalRef(deviceAddress);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
} else {
// TODO return an error code here?
ALOGE("error reading descriptors\n");
}
usb_device_close(device);
return 0;
}
关闭设备:
void usb_device_close(struct usb_device *device)
{
close(device->fd);
free(device);
}