• 安卓USB模块源码分析(三)- 设备连接断开广播


    我们将针对USB模块API的使用流程,从如下几个点进行初步分析:

    1. 设备连接及断开广播的触发发送;
    2. 获取DeviceList;
    3. 请求权限;
    4. 打开设备传输数据;

    设备连接及断开广播的触发发送

    通过注册如下两种广播,可以分别接收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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    monitorUsbHostBus

    monitorUsbHostBus 的实现如下:

    1. 先调用 usb_host_init 获得一个 usb_host_context 的结构体对象;
    2. 然后执行 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    其中 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参考: C中typedef 函数指针的使用 - rainbow70626 - 博客园 (cnblogs.com)

    usb_host_init 方法调用

    1. 分配 usb_host_context 结构体内存空间;
    2. 将 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    inotify 机制的大体流程如下:

    1. 调用 inotify_init 函数创建一个 inotify 句柄。
    2. 获取要监听的文件或目录路径,并且通过 inotify_add_watch 函数把其添加到 inotify 中进行监听。
    3. 在一个无限循环中,通过 read 函数读取被监听的文件或目录的变动事件 ;
      参考:

    usb_host_run 方法调用

    1. 通过 usb_host_load 来初始化usb host的监听列表,主要包括如下动作:
      1. 为 usb_host_context 设置添加和移除的两个回调;
      2. 通过 inotify_add_watch 方法,监听 /dev 目录下的文件创建及删除操作,将返回的监控描述项存储到 context -> wdd
      3. 然后通过 inotify_add_watch 方法监听 /usb 目录下所有文件创建及删除操作,将返回的监控描述项存储到 context -> wds;
      4. 从 /usb 目录中查找已经存在的usb设备,找到的话就调用 usb_device_added_cb 方法进行通知回调;
      5. 所有的 inotify_add_watch 添加的watch都会与 context->fd 关联;
    2. usb_host_read_event 会循环不断的执行,通过读取inotify文件描述符,来监听文件变化事件;
      1. 通过read inotify_init 初始化的文件描述符(context->fd ),来监听文件的变化事件,然后相应的调用 usb_device_added_cb 或 usb_device_removed_cb 回调函数来通知USB的插拔事件
      2. 完成后会相应的调用 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() */
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    clientData是什么?

    clientData 最初经由 monitorUsbHostBus(JNIEnv* /* env */, jobject thiz) 传递,后续一直在方法调用之间传递,在回调 add 及 remove 事件时,也会传递过来;这个 thiz 实际上就是 monitorUsbHostBus 方法的调用对象,即 UsbHostManager 。

    static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
    
    • 1

    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);
            } 
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    广播发送 - 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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    广播发送 - 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    总结

    1. UsbService启动时,启动一个线程,对USB相关设备文件目录进行循环监听;
    2. 当有设备插拔(即文件添加、移除)时,回调UsbHostManager对应的deviceAdded方法或者deviceRemoved方法进行通知;
    3. 在UsbHostManager 的deviceAdded或者deviceRemoved回调方法中发送对应的设备ATTACHED或DETACHED 广播;

    细节补充

    UsbHostManager#systemReady 如何被触发?

    待补充

    inotify 机制详解(USB中应用)

    待补充

    usb_device_added 详解

    usb_device_added 中会有UsbDevice的构造流程,这里展开一下;

    devAddress 格式

    设备地址的形式如下:

    /bus/(busname)/(deviceName)
    
    • 1

    clientData

    上面已有解释,实际上时UsbHostManager 的jobject对象;是为了能够回调时能调用对应Java实例的对应方法的。下面的thiz就是clientData,即 UsbHostManager

    env->CallBooleanMethod(thiz, method_usbDeviceAdded,
                    deviceAddress, classID, subClassID, descriptorsArray);
    
    • 1
    • 2

    流程梳理

    1. 使用 usb_device_open 打开usb设备文件
      1. 首先检查usb文件(access);
      2. 以指定的权限打开usb设备文件,并存储文件描述符及是否可写;
      3. 从设备文件中read设备描述,设备名称等信息,构造一个usb_device 的结构体对象进行存储;
    2. 读取设备描述符中的各种信息,如classId,subClassId,将这些信息转换成java可识别的格式,然后调用Java方法 UsbHostManager#usbDeviceAdded
    3. 关闭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;
    }
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    关闭设备:

    void usb_device_close(struct usb_device *device)
    {
        close(device->fd);
        free(device);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    Mac系统 AndroidStudio Missing essential plugin:org.jetbrains.android报错
    线上问题之Java数字范围引发的异常
    软件测试工程师简历项目经验怎么写?--1000个已成功入职的软件测试工程师简历范文模板(含真实简历)
    【Spring Boot】创建一个 Spring Boot 项目
    解析:WMS仓库管理系统是什么,可以提供什么协助
    矩阵距离——多源BFS
    勒索病毒最新变种.mkp勒索病毒来袭,如何恢复受感染的数据?
    智能配电系统解决方案
    代码随想录算法训练营第六十三天 |84.柱状图中最大的矩形
    Dubbo源码(四) - 服务引用(消费者)
  • 原文地址:https://blog.csdn.net/mospuito/article/details/126168121