望远山,知近路,而后自得其乐!

Android USB之UsbHostManager

UsbHostManager是Android USB处于Host模式时,用来管理外部接入的USB设备,如USB存储设置,USB鼠标/键盘,USB摄像头等,UsbHostManager主要用来处理这些设备的插入,移除等事件的处理。

UsbHostManager的初始化

UsbDeviceManager是在UsbService初始化的时候创建的。

public UsbService(Context context) {
    mContext = context;

    mUserManager = context.getSystemService(UserManager.class);
    mSettingsManager = new UsbSettingsManager(context);
    mAlsaManager = new UsbAlsaManager(context);

    final PackageManager pm = mContext.getPackageManager();
    if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
        mHostManager = new UsbHostManager(context, mAlsaManager, mSettingsManager);
    
    ...

 }

然后在UsbService的systemReady中调用了UsbHostManager的systemReady函数。

public void systemReady() {
    ...
    if (mHostManager != null) {
        mHostManager.systemReady();
    }
    ...
}

UsbHostManager的systemReady函数中会启动一个线程来监听USB Host Bus。

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

UsbHostManager监控USB设备

monitorUsbHostBus是一个本地函数,具体实现在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);
}

其中,usb_host_initusb_host_run都是libusbhost库中的函数,该库位于system/core/libusbhost目录。libusbhost库主要提供与usb设备通信的接口,同时以 的方式来监控/dev/bus/usb/目录并见设备变化通知到上层。

其中usb_host_init函数会初始化一个usb_host_context结构体,同时会初始化inotify

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

usb_host_run函数则会加载现有USB设备,同时监控USB设备文件变化。

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_load函数会检已经存在的usb设备,并上报设备状态给上层。usb_host_read_event函数循环读取inotify的事件上报,将设备状态信息通过回调函数上报给上层, 其中usb_device_added_cb回调函数用来回调通知USB设备插入事件,usb_device_removed_cb回调函数用来处理USB设备移除事件。具体的函数,此处不再展开,要了解详细内容可以阅读源码。

回过头来继续看com_android_server_UsbHostManager.cpp,当有USB设备插入时,usb_device_added函数被回调,该函数又会回调Java层的UsbHostManager类的usbDeviceAdded函数;USB设备拔出时,usb_device_removed函数会被回调,该函数会回调Java层的UsbHostManager类的usbDeviceRemoved函数。

USB设备插入处理

当有USB设备插入时,UsbHostManagerusbDeviceAdded会被回调。该函数用来处理USB设备插入事件,函数具体内容如下:

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;
    }
    UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
    logUsbDevice(parser);

    if (isBlackListed(deviceClass, deviceSubclass)) {
        if (DEBUG) {
            Slog.d(TAG, "device class 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 newDevice = parser.toAndroidUsbDevice();
        if (newDevice == null) {
            Slog.e(TAG, "Couldn't create UsbDevice object.");
            // Tracking
            addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
                    parser.getRawDescriptors());
        } else {
            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());
        }
    }

    if (DEBUG) {
        Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
    }

    return true;
}

下面一步步来进行分析:

  1. 其中, isBlackListed函数判断该设备是否已加入黑名单,如果已经在黑名单则直接返回。
  2. 接下来会为设备创建UsbDescriptorParserlogUsbDevice用来输出设备相关的log信息。
  3. 再次调用isBlackListed来判断是不是已经加入黑名单,这次传入的是deviceClassdeviceSubclass
  4. 在当前设备mapmDevices查找是否已经有这个设备,如果已经有了这个设备,那么就会直接返回。
  5. 通过UsbDescriptorParser将设备转换为UsbDevice,如果转换失败,调用addConnectionRecord将设备插入事件添加到ConnectionRecord记录列表。
  6. 如果UsbDevice不为空,者加入到设备mapmDevices
  7. 通过getUsbDeviceConnectionHandler函数获取UsbDeviceConnectionHandler获取到的是一个ComponentName对象。
  8. getCurrentUserSettings函数用来获取当前用户UsbProfileGroupSettingsManager

    public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
        synchronized (mSettingsLock) {
            mCurrentSettings = settings;
        }
    }
    
    private UsbProfileGroupSettingsManager getCurrentUserSettings() {
        synchronized (mSettingsLock) {
            return mCurrentSettings;
        }
    }

该配置信息是在UsbServiceonSwitchUser函数中调用UsbHostManagersetCurrentUserSettings来设置的,用户配置信息变化时,该配置也会做现有的更新。

private void onSwitchUser(@UserIdInt int newUserId) {
    synchronized (mLock) {
        mCurrentUserId = newUserId;
        UsbProfileGroupSettingsManager settings =
                mSettingsManager.getSettingsForProfileGroup(UserHandle.of(newUserId));
        if (mHostManager != null) {
            mHostManager.setCurrentUserSettings(settings);
        }
        if (mDeviceManager != null) {
            mDeviceManager.setCurrentUser(newUserId, settings);
        }
    }
}
  1. getUsbDeviceConnectionHandler返回null会调用UsbProfileGroupSettingsManagerdeviceAttached函数,否则调用deviceAttachedForFixedHandler函数。
  2. UsbProfileGroupSettingsManagerdeviceAttached函数中会发送广播来通知设备Attached。

    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 */);
    }
对应的Attached广播为:`UsbManager.ACTION_USB_DEVICE_ATTACHED`。广播具体定如下:
public static final String ACTION_USB_DEVICE_ATTACHED =
            "android.hardware.usb.action.USB_DEVICE_ATTACHED";
`resolveActivity`函数则是选择使用哪个Activity来处理该USB设备插入事件,具体的此处不再分析。
  1. mUsbAlsaManagerusbDeviceAdded函数,处理USB音频设备的插入事件。
  2. 调用addConnectionRecord将设备插入事件添加到ConnectionRecord记录列表。

USB设备移除处理

当有USB设备移除时,UsbHostManagerusbDeviceRemoved会被回调。该函数用来处理USB设备移除事件,函数具体内容如下:

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/*device*/);
            mSettingsManager.usbDeviceRemoved(device);
            getCurrentUserSettings().usbDeviceRemoved(device);

            // Tracking
            addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
        } else {
            Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
        }
    }
}

移除设备时,处理相对简单:

  1. 将设备从当前设备map mDevices中移除。
  2. 调用mUsbAlsaManager.usbDeviceRemoved处理USB音频设备移除事件。
  3. 调用mSettingsManager.usbDeviceRemoved处理USB设备移除事件,该函数会以广播的形式来通知USB设备DETACHED事件。

    void usbDeviceRemoved(@NonNull UsbDevice device) {
        synchronized (mSettingsByUser) {
            for (int i = 0; i < mSettingsByUser.size(); i++) {
                // clear temporary permissions for the device
                mSettingsByUser.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);
    
        if (DEBUG) {
            Slog.d(LOG_TAG, "usbDeviceRemoved, sending " + intent);
        }
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    }
  4. 调用UsbProfileGroupSettingsManagerusbDeviceRemoved函数,该函数很简单,只是音频相关的Notification

    void usbDeviceRemoved(@NonNull UsbDevice device) {
        mMtpNotificationManager.hideNotification(device.getDeviceId());
    }
  5. 调用addConnectionRecord将设备插入事件添加到ConnectionRecord记录列表。

文章评论已关闭!