参考文献:
Android 6.0 usb解析(二)UsbHostManager
参考文献很牛,我是根据参考文献自己再理了一遍。
UsbHostManager和UsbDeviceManager的区别在于,UsbDeviceManager是将手机作为一个设备,比如手机连上电脑,使用adb、mtp等;而UsbHostManager,是将手机作为一个host,比如手机连接usb鼠标、usb摄像头等,就会new出一个UsbDevice出来。
UsbHostManager初始化
UsbHostManager和UsbDeviceManager都是在UsbService中创建的(Z:\zw\frameworks\base\services\usb\java\com\android\server\usb\UsbService.java),如下所示:
public UsbService(Context context) {
mContext = context;
mUserManager = context.getSystemService(UserManager.class);
mSettingsManager = new UsbSettingsManager(context, this);
mPermissionManager = new UsbPermissionManager(context, this);
mAlsaManager = new UsbAlsaManager(context);
final PackageManager pm = mContext.getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
//创建UsbHostManager
mHostManager = new UsbHostManager(context, mAlsaManager, mPermissionManager);
}
if (new File("/sys/class/android_usb").exists()) {
//创建UsbDeviceManager
mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager,
mPermissionManager);
}
if (mHostManager != null || mDeviceManager != null) {
mPortManager = new UsbPortManager(context);
}
BroadcastReceiver receiver = new BroadcastReceiver() {
......
};
...
mContext.registerReceiver(receiver, filter, null, null);
}
上面为UsbService的构造函数,其在构造函数中new了UsbHostManager和UsbDeviceManager。UsbService.java中一个systemReady()方法,该方法如下:
public void systemReady() {
mAlsaManager.systemReady();
if (mDeviceManager != null) {
mDeviceManager.systemReady();
}
if (mHostManager != null) {
mHostManager.systemReady();
}
if (mPortManager != null) {
mPortManager.systemReady();
}
}
该方法会分别调用在UsbService构造函数中new出的mAlsaManager、mDeviceManager、mHostManager、和mPortManager的systemReady()方法。那我们现在来看UsbHostManager,UsbHostManager在UsbHostManager.java中(.\frameworks\base\services\usb\java\com\android\server\usb\UsbHostManager.java),在UsbHostManager中,有一个成员变量如下:
// 包含所有连接的USB设备
private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
和host连接的USB设备会保存在这个HashMap中。
刚才已经分析了UsbService的systemReady()方法会调用UsbHostManager的systemReady(),该方法如下:
public void systemReady() {
synchronized (mLock) {
// 创建一个线程调用本机代码等待USB主机事件。
// 这个线程会在usbDeviceAdded和usbdevicermoved上回调我们。
Runnable runnable = this::monitorUsbHostBus;
new Thread(null, runnable, "UsbService host thread").start();
}
}
可以看到,UsbHostManager的systemReady()方法会创建一个线程,该线程会运行monitorUsbHostBus函数,该函数在什么地方呢?
private native void monitorUsbHostBus();
在UsbHostManager类中有如下声明,可以看出来这个函数是一个Native函数,即该调用是一个JNI调用。
UsbHostManager的Native层
该函数对应在com_android_server_UsbHostManager.cpp中(Z:\zw\frameworks\base\services\core\jni\com_android_server_UsbHostManager.cpp)android_server_UsbHostManager_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;
}
// 这个永远不会返回,所以直接传递这个是安全的
usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}
该函数进来之后通过usb_host_init()函数创建了一个struct usb_host_context对象,usb_host_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;
}
可以看到函数进来分配了一块struct usb_host_context内存之后,使用inotify_init()新建了一个INotify,并且将这个INotify的fd赋值给了usb_host_context的成员变量。什么是INotify呢?
简单说一下,INotify是一个内核用于通知用户空间程序文件系统发生变化的一种机制,例如文件增加、删除等事件都可以立刻让用户得知。类似的机制还有,Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理;udev 动态地维护 /dev 下的设备文件等。后面再写关于INotify的内容。
再回来,usb_host_init创建了一个INotify之后,将这个INotify的fd赋值给了usb_host_context的成员变量,然后在android_server_UsbHostManager_monitorUsbHostBus中就进入了usb_host_run函数,需要注意这里将JNI中的jobject thiz作为指针也传给了usb_host_run。该函数也在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);
while (!done) {
done = usb_host_read_event(context);
}
} /* usb_host_run() */
android_server_UsbHostManager_monitorUsbHostBus调用usb_host_run的时候传入了两个比较关键的函数指针,usb_device_added和usb_device_removed,看名字就知道这两个函数和USB设备的添加和删除有关,两个函数都定义在com_android_server_UsbHostManager.cpp文件中,我们后面再对这两个函数作分析。
usb_host_run()进来之后直接就调用了usb_host_load函数,同时将两个函数指针传了进去(discovery_done_cb为NULL),我们接着看usb_host_load函数,也是在usbhost.c中,如下:
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;
/* 观察在USB_FS_DIR中添加和删除的文件 */
context->wddbus = -1;
for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
context->wds[i] = -1;
/* 查看根目录是否有新的子目录 #define DEV_DIR "/dev" */
context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
if (context->wdd < 0) {
fprintf(stderr, "inotify_add_watch failed\n");
if (discovery_done_cb)
discovery_done_cb(client_data);
return done;
}
//#define MAX_USBFS_WD_COUNT 10
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
/* 在我们设置inotify之后,首先检查现有的设备 */
done = find_existing_devices(added_cb, client_data);
if (discovery_done_cb)
done |= discovery_done_cb(client_data);
return done;
} /* usb_host_load() */
在usb_host_load中,首先将两个函数指针赋值给struct usb_host_context中的成员变量,client_data是在JNI中传入的thiz指针,discovery_done_cb传入的值为NULL。这里又看到了inotify开头的函数inotify_add_watch,这个也是和inotify机制相关的东西,这里再简单的说一下。
在inotify机制中,一个称作watches 的对象管理文件系统的变化事件,每一个 watch 是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的 inotify 事件,每一个位对应一个 inotify 事件。具体的一个Watch 对象通过 watch描述符引用, watch描述符就是inotify_add_watch函数返回的int值,添加一个watch通过目标文件或目录的路径名来添加,如果添加的watch是一个目录,那么该watches将会根据设置的事件掩码来返回在该目录下的所有文件上面发生的事件。
再回到代码中来,在usb_host_load中,首先通过inotify_add_watch,添加了一个目录watch("/dev"),其事件掩码设置为IN_CREATE | IN_DELETE(即观察创建和删除事件),并将这个watch的描述符储存在usb_host_context的成员变量wdd中。后面紧接着就调用watch_existing_subdirs函数,该函数也在usbhost.c中,如下所示:
static void watch_existing_subdirs(struct usb_host_context *context,
int *wds, int wd_count)
{
char path[100];
int i, ret;
//#define USB_FS_DIR "/dev/bus/usb"
wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
if (wds[0] < 0)
return;
/* 查看USB_FS_DIR的现有子目录 #define USB_FS_DIR "/dev/bus/usb" */
for (i = 1; i < wd_count; i++) {
snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i);//
ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);
if (ret >= 0)
wds[i] = ret;
}
}
wds是usb_host_context中的一个int数组,其长度为MAX_USBFS_WD_COUNT。这里又添加了一个目录watch("/dev/usb"),其事件掩码也设置为IN_CREATE | IN_DELETE,然后将watch描述符储存在wds[0]的位置。
紧接着就是一个for循环,该for循环将"/dev/bus/usb"目录下的设备目录(从001开始到MAX_USBFS_WD_COUNT),都添加到inotify中去,作为目录watch进行观察,然后各个watch描述符依次存放在wds数组剩余的位置中。
watch_existing_subdirs函数返回之后,在usb_host_load函数中接着又调用了find_existing_devices函数。(usbhost.c)如下所示:
/* 如果其中一个回调指示我们完成,则返回true */
static int find_existing_devices(usb_device_added_cb added_cb,
void *client_data)
{
char busname[32];
DIR *busdir;
struct dirent *de;
int done = 0;
//#define USB_FS_DIR "/dev/bus/usb"
busdir = opendir(USB_FS_DIR);
if(busdir == 0) return 0;
while ((de = readdir(busdir)) != 0 && !done) {
if(badname(de->d_name)) continue;
snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name);
done = find_existing_devices_bus(busname, added_cb,
client_data);
} //end of busdir while
closedir(busdir);
return done;
}
该函数首先打开"/dev/bus/usb"目录,然后对该目录下的目录调用find_existing_devices_bus函数(usbhost.c),如下:
static int find_existing_devices_bus(char *busname,
usb_device_added_cb added_cb,
void *client_data)
{
char devname[32];
DIR *devdir;
struct dirent *de;
int done = 0;
devdir = opendir(busname);
if(devdir == 0) return 0;
while ((de = readdir(devdir)) && !done) {
if(badname(de->d_name)) continue;
snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name);
done = added_cb(devname, client_data);
} // end of devdir while
closedir(devdir);
return done;
}
该函数会打开之前传入的目录,例如:"/dev/bus/usb/001",然后会遍历该目录下的所有文件,将文件名和之前的目录路径组合到一起就可以得到准确的文件位置,这个文件就代表了一个USB设备,该USB设备是在UsbHostManager运行之前就挂载好了的,所以此时上层还不知道该USB设备的存在,所以需要对该USB设备向上层做补充的通知,即通过added_cb函数指针调用usb_device_added函数。
这里我们还是先不分析usb_device_added函数,只需要知道find_existing_devices函数的作用是,将"/dev/bus/usb"子目录下,在UsbHostManager运行之前就挂载好了的USB设备向上做补充的通知,让上层感知到这些USB设备的存在。
这样usb_host_load函数就返回了,该函数返回之后,usb_host_run会进入一个while循环去执行usb_host_read_event函数(usbhost.c),usb_host_read_event函数非常的长如下:
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) {
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() */
大致就是使用read调用去读取context->fd中的数据,这个fd就是一开始创建的inotify的fd,可以对这个fd做select、poll()等操作。read读到的数据可以转换为struct inotify_event类型的结构体,通过这个结构体中的一些字段来走不同的分支,这里先关注最后一个分支,即是wds数组中,索引0之外的watch描述符发生了变化就会走该分支,根据mask来判断是发生了IN_CREATE事件还是IN_DELETE事件,前者调用usb_device_added函数,后者调用usb_device_removed函数。
添加和删除USB设备时的回调函数
usb_device_added函数
当mask为IN_CREATE会调用该回调函数。
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;
}
参数devAddress为发生变化的文件的路径,clientData为android_server_UsbHostManager_monitorUsbHostBus时设置的jobject thiz指针。该函数通过JNI调用UsbHostManager.java中的usbDeviceAdded方法:
private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
byte[] descriptors) {
if (DEBUG) {
Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
}
if (isBlackListed(deviceAddress)) {
if (DEBUG) {
Slog.d(TAG, "device address is black listed");
}
return false;
}
if (isBlackListed(deviceClass, deviceSubclass)) {
if (DEBUG) {
Slog.d(TAG, "device class is black listed");
}
return false;
}
UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE
&& !checkUsbInterfacesBlackListed(parser)) {
return false;
}
// Potentially can block as it may read data from the USB device.
logUsbDevice(parser);
if (isBlackListed(parser)) {
if (DEBUG) {
Slog.d(TAG, "device UsbDescriptorParser is black listed");
}
return false;
}
synchronized (mLock) {
if (mDevices.get(deviceAddress) != null) {
Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
//TODO If this is the same peripheral as is being connected, replace
// it with the new connection.
return false;
}
UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();
if (newDeviceBuilder == null) {
Slog.e(TAG, "Couldn't create UsbDevice object.");
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
parser.getRawDescriptors());
} else {
UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
mPermissionManager, newDeviceBuilder.serialNumber);
UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
serialNumberReader.setDevice(newDevice);
mDevices.put(deviceAddress, newDevice);
Slog.d(TAG, "Added device " + newDevice);
// It is fine to call this only for the current user as all broadcasts are
// sent to all profiles of the user and the dialogs should only show once.
ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
if (usbDeviceConnectionHandler == null) {
getCurrentUserSettings().deviceAttached(newDevice);
} else {
getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
usbDeviceConnectionHandler);
}
mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
parser.getRawDescriptors());
// Stats collection
FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
newDevice.getVendorId(), newDevice.getProductId(),
parser.hasAudioInterface(), parser.hasHIDInterface(),
parser.hasStorageInterface(),
FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
}
}
if (DEBUG) {
Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
}
return true;
}
这个方法里面会向mDevices(一开始的那个HashMap)中填加UsbDevice。
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);
env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
env->DeleteLocalRef(deviceAddress);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
private void usbDeviceRemoved(String deviceAddress) {
if (DEBUG) {
Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end");
}
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);
if (current != null) {
UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
current.mDescriptors);
// Stats collection
FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
device.getVendorId(), device.getProductId(), parser.hasAudioInterface(),
parser.hasHIDInterface(), parser.hasStorageInterface(),
FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
System.currentTimeMillis() - current.mTimestamp);
}
} else {
Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
}
}
}