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

  • Post author:
  • Post category:其他


我们将针对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();



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



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

参考:

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


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





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



总结

  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)



clientData

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

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



流程梳理

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

关闭设备:

void usb_device_close(struct usb_device *device)
{
    close(device->fd);
    free(device);
}



版权声明:本文为mospuito原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。