我们将针对USB模块API的使用流程,从如下几个点进行初步分析:
通过注册如下两种广播,可以分别接收USB连接及断开的广播:
服务启动时,调用到 UsbHostManager#systemReady,
// 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 的实现如下:
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);
}
定义位于: 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);
// 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 函数读取被监听的文件或目录的变动事件 ;
参考:
问题:
这里是以 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() */
从前面的分析可以知道,我们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 最初经由 monitorUsbHostBus(JNIEnv* /* env */, jobject thiz) 传递,后续一直在方法调用之间传递,在回调 add 及 remove 事件时,也会传递过来;这个 thiz 实际上就是 monitorUsbHostBus 方法的调用对象,即 UsbHostManager 。
static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
移除的逻辑比较简单,实际上就是调用了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);
}
}
}
其他的部分我们先不关注,看下 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_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;
}
上面的代码中我们省略的若干代码,在执行 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;
}
待补充
待补充
usb_device_added 中会有UsbDevice的构造流程,这里展开一下;
设备地址的形式如下:
/bus/(busname)/(deviceName)
上面已有解释,实际上时UsbHostManager 的jobject对象;是为了能够回调时能调用对应Java实例的对应方法的。下面的thiz就是clientData,即 UsbHostManager
env->CallBooleanMethod(thiz, method_usbDeviceAdded,
deviceAddress, classID, subClassID, descriptorsArray);
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);
}