Android之监听Usb设备插拔

SystemServer.java在服务启动时会启动UsbService(frameworks/base/services/java/com/android/server/usb/UsbService.java),UsbService会构造UsbHostManager和UsbDeviceManager

frameworks/base/services/jni/com_android_server_UsbHostManager.cpp

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

#define DEV_DIR             "/dev"
#define DEV_BUS_DIR         DEV_DIR "/bus"
#define USB_FS_DIR          DEV_BUS_DIR "/usb"
#define USB_FS_ID_SCANNER   USB_FS_DIR "/%d/%d"
#define USB_FS_ID_FORMAT    USB_FS_DIR "/%03d/%03d"

#define MAX_USBFS_WD_COUNT      10

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

struct usb_device {
    char dev_name[64];
    unsigned char desc[4096];
    int desc_length;
    int fd;
    int writeable;
};

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

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

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;

    D("Created device discovery thread\n");

    /* 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);
    if (context->wdd < 0) {
        fprintf(stderr, "inotify_add_watch failed\n");
        if (discovery_done_cb)
            discovery_done_cb(client_data);
        return done;
    }

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

static void watch_existing_subdirs(struct usb_host_context *context,
                                   int *wds, int wd_count)
{
    char path[100];
    int i, ret;

    wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
    if (wds[0] < 0)
        return;

    /* watch existing subdirectories of USB_FS_DIR */
    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;
    }
}

/* returns true if one of the callbacks indicates we are done */
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;

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

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;
}
static int usb_device_added(const char *devname, void* client_data) {
    struct usb_descriptor_header* desc;
    struct usb_descriptor_iter iter;

    struct usb_device *device = usb_device_open(devname);
    if (!device) {
        ALOGE("usb_device_open failed\n");
        return 0;
    }

    JNIEnv* env = AndroidRuntime::getJNIEnv();
    jobject thiz = (jobject)client_data;
    Vector<int> interfaceValues;
    Vector<int> endpointValues;
    const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);

    uint16_t vendorId = usb_device_get_vendor_id(device);
    uint16_t productId = usb_device_get_product_id(device);
    uint8_t deviceClass = deviceDesc->bDeviceClass;
    uint8_t deviceSubClass = deviceDesc->bDeviceSubClass;
    uint8_t protocol = deviceDesc->bDeviceProtocol;

    usb_descriptor_iter_init(device, &iter);

    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
        if (desc->bDescriptorType == USB_DT_INTERFACE) {
            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;

            // push class, subclass, protocol and number of endpoints into interfaceValues vector
            interfaceValues.add(interface->bInterfaceNumber);
            interfaceValues.add(interface->bInterfaceClass);
            interfaceValues.add(interface->bInterfaceSubClass);
            interfaceValues.add(interface->bInterfaceProtocol);
            interfaceValues.add(interface->bNumEndpoints);
        } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
            struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;

            // push address, attributes, max packet size and interval into endpointValues vector
            endpointValues.add(endpoint->bEndpointAddress);
            endpointValues.add(endpoint->bmAttributes);
            endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize));
            endpointValues.add(endpoint->bInterval);
        }
    }

    usb_device_close(device);

    // handle generic device notification
    int length = interfaceValues.size();
    jintArray interfaceArray = env->NewIntArray(length);
    env->SetIntArrayRegion(interfaceArray, 0, length, interfaceValues.array());

    length = endpointValues.size();
    jintArray endpointArray = env->NewIntArray(length);
    env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array());

    jstring deviceName = env->NewStringUTF(devname);
    env->CallVoidMethod(thiz, method_usbDeviceAdded,
            deviceName, vendorId, productId, deviceClass,
            deviceSubClass, protocol, interfaceArray, endpointArray);

    env->DeleteLocalRef(interfaceArray);
    env->DeleteLocalRef(endpointArray);
    env->DeleteLocalRef(deviceName);
    checkAndClearExceptionFromCallback(env, __FUNCTION__);

    return 0;
}

frameworks/base/services/java/com/android/server/usb/UsbHostManager.java

/* Called from JNI in monitorUsbHostBus() to report new USB devices */
private void usbDeviceAdded(String deviceName, int vendorID, int productID,
        int deviceClass, int deviceSubclass, int deviceProtocol,
        /* array of quintuples containing id, class, subclass, protocol
           and number of endpoints for each interface */
        int[] interfaceValues,
       /* array of quadruples containing address, attributes, max packet size
          and interval for each endpoint */
        int[] endpointValues) {

    if (isBlackListed(deviceName) ||
            isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
        return;
    }

    synchronized (mLock) {
        if (mDevices.get(deviceName) != null) {
            Slog.w(TAG, "device already on mDevices list: " + deviceName);
            return;
        }

        int numInterfaces = interfaceValues.length / 5;
        Parcelable[] interfaces = new UsbInterface[numInterfaces];
        try {
            // repackage interfaceValues as an array of UsbInterface
            int intf, endp, ival = 0, eval = 0;
            for (intf = 0; intf < numInterfaces; intf++) {
                int interfaceId = interfaceValues[ival++];
                int interfaceClass = interfaceValues[ival++];
                int interfaceSubclass = interfaceValues[ival++];
                int interfaceProtocol = interfaceValues[ival++];
                int numEndpoints = interfaceValues[ival++];

                Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
                for (endp = 0; endp < numEndpoints; endp++) {
                    int address = endpointValues[eval++];
                    int attributes = endpointValues[eval++];
                    int maxPacketSize = endpointValues[eval++];
                    int interval = endpointValues[eval++];
                    endpoints[endp] = new UsbEndpoint(address, attributes,
                            maxPacketSize, interval);
                }

                // don't allow if any interfaces are blacklisted
                if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
                    return;
                }
                interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
                        interfaceSubclass, interfaceProtocol, endpoints);
            }
        } catch (Exception e) {
            // beware of index out of bound exceptions, which might happen if
            // a device does not set bNumEndpoints correctly
            Slog.e(TAG, "error parsing USB descriptors", e);
            return;
        }

        UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
                deviceClass, deviceSubclass, deviceProtocol, interfaces);
        mDevices.put(deviceName, device);
        getCurrentSettings().deviceAttached(device);
    }
}

frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java

public void deviceAttached(UsbDevice device) {
    Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    ArrayList<ResolveInfo> matches;
    String defaultPackage;
    synchronized (mLock) {
        matches = getDeviceMatchesLocked(device, intent);
        // Launch our default activity directly, if we have one.
        // Otherwise we will start the UsbResolverActivity to allow the user to choose.
        defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
    }

    // Send broadcast to running activity with registered intent
    mUserContext.sendBroadcast(intent);

    // Start activity with registered intent
    resolveActivity(intent, matches, defaultPackage, device, null);
}