android源码usb目录,Android USB 框架 —— UsbHostManager

我们知道 USB 是主从模式(Host/Slave)的,有趣的是,Android 手机既可以被当做从设备连接到我们的 PC 被识别成为一个 USB 设备,也可以当做主设备识别外部的 USB 设备。这篇东东主要着眼于后者。

我之前一直有一个疑惑,那就是 Android 上层与 Linux 内核底层究竟是如何通讯的。之前我知道了可以通过 sysfs ,或者是通过 uevent 来打通内核与上层。现在,我学习了 Android USB Host 之后,发现了还有一种新的方法 —— Android JNI 。

首先声明,我对 Android 这一块知道的确实不多,所以下面的描述肯定是会有这样那样的错误,还待日后更加深入学习。

Android JNI

关于 Java JNI 的我写了另外的一篇东东,就不再赘述了。下面着重说明 Android JNI 与 Java JNI 的区别。

Android 中使用了一种不同于传统 Java JNI 的方法来定义其 native 函数。其中很重要的区别就是 Android 使用了一种 Java 和 C 函数的映射表数组,并在其中表述了函数的参数和返回值。这个数组的类型就是 JNINativeMethod ,定义如下:

typedef struct {

const char *name;

const char *signature;

void *fnPtr;

} JNINativeMethod;

其中,第一个变量是 Java 中的函数名,第二个变量是一个描述返回值和参数的字符串,第三个是指向 native 实现的函数指针。

比较难以理解的第二个参数,具体可以参照这篇博客。

发现 USB 设备流程

首先,我先使用自顶向下来说明一下整个流程:

在 UsbHostManager 中创建一个线程来监控(注意,这里用“监控”可是很有讲究的,因为当 Android 设备处于 Host 模式时,当有 USB 设备插拔时,首先相应的是 Linux 内核,然后内核再将设备变动的信息传递给上层,而 Android 根据这些信息在进行相应的动作,所以这里使用了“监控”二字。这跟 Android 设备处于 Slave 模式下的 UsbDeviceManager 是完全不同的)设备的连接状态。该线程执行的是 JNI 层的函数,而 JNI 层则是通过调用 libusb 库的函数,利用内核提供的 inotify 机制来监控 /dev/bus/usb 下文件的变动来判断是否有新的设备的插拔。下面的图示很好地展现了这种自顶向下的路线:

6a84987a8485f2f1e7b7c7170238ebc6.png

(Source:Unboxing Android USB)

接下来,我再以自底向上的方法来详细阐述整个流程:

首先,USB 插入设备之后,内核会在 /dev/bus/usb 这个目录下建立代表该文件的设备文件入口(device file entry)(当然,个中细节还需要花时间深入理解,这里先假设其成立),然后 Android 上层使用 libusb 这个库来监控这个目录下的文件变动,其中用到的技术就是 inotify 。关于 inotify 我也写了一篇东东来简要地说明它,可以先去看看。下面将通过分析 libusb 的源代码(具体位置在 system/core/libusb/usbhost.c)来详细说明:

首先,先初始化 inotify ,获得文件描述符,此后的所有事件都是通过读取该文件描述符中的数据来判断:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16struct 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 这个数据结构:

1

2

3

4

5

6

7

8

9struct usb_host_context {

int fd; // inotify 返回的文件描述符,通过读取其中的数据来判断事件

usb_device_added_cb cb_added; // 当有 USB 设备插入是调用的回调函数,在 JNI 层赋值,后面会提到

usb_device_removed_cb cb_removed; // 同上,当有 USB 设备拔出是的回调函数

void *data; // 调用者给上面两个回调函数的参数

int wds[MAX_USBFS_WD_COUNT]; // /dev/bus/usb 下各个子目录对应的 watch descriptor

int wdd; // /dev 对应的 watch descriptor

int wddbus; // /dev/bus 对应的 watch descriptor

};

然后,就是要开始添加要监控的目录了(即我们这里的 /dev/bus/usb):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15void 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() */

主要的工作是在 usb_host_load() 中完成的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31int 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 */

// 如上面说说,wdd 是用来发现 /dev 下是否有子目录创建或者删除的

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);

if (discovery_done_cb)

done |= discovery_done_cb(client_data);

return done;

} /* usb_host_load() */

其中的 watch_existing_subdirs() 就是添加 /dev/bus/usb 下的文件变动的监控的。函数不难理解,主要说明一下,就是 context->wds 这个数组:wds[0] 代表的是整个 /dev/bus/usb 下子目录的变动,而 wds[] 数组中其他的代表了真正设备的变动,因为内核会在每个新设备插拔之后在 /dev/bus/bus 这个目录下增加设备对应的文件如:/dev/bus/usb/001/001 等。

在完成了文件监控之后,接下来就可以来读数据了,调用了上面的 usb_host_read_event() ,这个函数重复的地方很多,下面我只选取了关键的 USB 设备的部分来说明:

1

2

3

4

5

6

7

8

9

10for (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) {

done = context->cb_added(path, context->data);

} else if (event->mask == IN_DELETE) {

done = context->cb_removed(path, context->data);

}

}

}

如果传来的数据是跟真正 USB 设备相关的(通过 watch descriptor 来判断,而且结合上面对 context->wds[] 数组的说明),那么就通过调用相应的回调函数来通知上层相应的设备变动。

好,底层的这部分说明完了,接下来就要 JNI 层了。JNI 层负责给调用上面提到的函数,指定合适的回调函数,具体位置在:frameworks/base/services/core/jni/com_android_server_UsbHostManager.cpp:

1

2

3

4

5

6

7

8

9

10static 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);

}

在联系我们在 libusb 上提到的 usb_host_init() 和 usb_host_run() ,有没有一种 connected 的感觉呢?

我们前面说过,JNI 层的函数是被 UsbHostManager 调用的,我们再来看看 UsbHostManager 中具体的监控进程,位置在 frameworks/base/services/usb/java/com/android/server/usb/UsbHostManager.java:

1

2

3

4

5

6

7

8

9

10

11

12public 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 = new Runnable() {

public void run(){

monitorUsbHostBus();

}

};

new Thread(null, runnable, "UsbService host thread").start();

}

}

那么如何打通这两者呢?哈,我们上面提到的 Android JNI 就扮演着连接者的角色。看看下面的代码你就明白了:

1

2

3

4static JNINativeMethod method_table[] = {

{ "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },

......

};

至此,Android 发现外部 USB 设备的整个流程我们就说明完了。

EOF

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值