USB模块分析(四)- 设备列表&权限申请

getDeviceList

其调用入口位于 UsbManager中:

// frameworks/base/core/java/android/hardware/usb/UsbManager.java

private final IUsbManager mService;
@RequiresFeature(PackageManager.FEATURE_USB_HOST)
public HashMap<String,UsbDevice> getDeviceList() {
    HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
    if (mService == null) {
        return result;
    }
    Bundle bundle = new Bundle();
    try {
        mService.getDeviceList(bundle);
        for (String name : bundle.keySet()) {
            result.put(name, (UsbDevice)bundle.get(name));
        }
        return result;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

实际上调用的是 IUsbManager的,也就是通过AIDL调用了对应的 UsbService 远端的实现,我们看下UsbService的实现,这里的实现就是调用UsbHostManager的对应的方法向bundle中塞入数据。(frameworks/base/services/usb/java/com/android/server/usb/UsbService.java)的。

// frameworks/base/services/usb/java/com/android/server/usb/UsbService.java
// com.android.server.usb.UsbService#getDeviceList

/* Returns a list of all currently attached USB devices (host mdoe) */
@Override
public void getDeviceList(Bundle devices) {
    if (mHostManager != null) {
        mHostManager.getDeviceList(devices);
    }
}

UsbHostManager#getDeviceList 实现: 就是从成员变量mDevice中取出设备名称,然后塞到返回对象中,所以我们看下mDevice是何时填充的数据。

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

/* Returns a list of all currently attached USB devices */
public void getDeviceList(Bundle devices) {
    synchronized (mLock) {
        for (String name : mDevices.keySet()) {
            devices.putParcelable(name, mDevices.get(name));
        }
    }
}

mDevices 添加数据

可以看到,mDevices 改变的地方位于 usbDeviceAdded 和 usbDeviceRemoved 两个回调方法中。

调用时机:

之前已经有过分析,在UsbService初始化的时候,会添加对于usb设备目录的监听,当有设备添加或者移除时,会调用 usbDeviceAdded 和 usbDeviceRemoved 两个回调方法。

在这里插入图片描述

接下来看下具体实现

usbDeviceAdded

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

private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
        byte[] descriptors) {
    if (isBlackListed(deviceAddress)) {
        return false;
    }
    if (isBlackListed(deviceClass, deviceSubclass)) {
        return false;
    }
    UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
    if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE
            && !checkUsbInterfacesBlackListed(parser)) {
        return false;
    }
    // Potentially can block as it may read data from the USB device.
    logUsbDevice(parser);
    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.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();
        if (newDeviceBuilder == null) {
            Slog.e(TAG, "Couldn't create UsbDevice object.");
            // Tracking
            addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
                    parser.getRawDescriptors());
        } else {
            UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
                    mPermissionManager, newDeviceBuilder.serialNumber);
            UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
            serialNumberReader.setDevice(newDevice);
            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());
            // Stats collection
            FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
                    newDevice.getVendorId(), newDevice.getProductId(),
                    parser.hasAudioInterface(), parser.hasHIDInterface(),
                    parser.hasStorageInterface(),
                    FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
        }
    }
    if (DEBUG) {
        Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
    }
    return true;
}

usbDeviceRemoved

private void usbDeviceRemoved(String deviceAddress) {
    if (DEBUG) {
        Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end");
    }
    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);
            if (current != null) {
                UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
                        current.mDescriptors);
                    // Stats collection
                FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
                        device.getVendorId(), device.getProductId(), parser.hasAudioInterface(),
                        parser.hasHIDInterface(),  parser.hasStorageInterface(),
                        FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
                        System.currentTimeMillis() - current.mTimestamp);
            }
        } else {
            Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
        }
    }
}

权限申请

参考UsbManager的使用部分,我们知道 权限申请的方式是直接调用 UsbManager.requestPermission,然后传递对应的device及permissionPendingIntent。

https://shimo.im/docs/cXjRDCCTrc9xYCwC#anchor-8M36

UsbManager#requestPermission

我们看下实现代码:

// android.hardware.usb.UsbManager#requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent)
public void requestPermission(UsbDevice device, PendingIntent pi) {
    try {
        mService.requestDevicePermission(device, mContext.getPackageName(), pi);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

UsbService#requestDevicePermission

实际上最终也是调用 UsbService 的 对应方法 - requestDevicePermission

//  com.android.server.usb.UsbService#requestDevicePermission
@Override
public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
    final int uid = Binder.getCallingUid();
    final int pid = Binder.getCallingPid();
    final int userId = UserHandle.getUserId(uid);
    final long token = Binder.clearCallingIdentity();
    try {
        getPermissionsForUser(userId).requestPermission(device, packageName, pi, pid, uid);
    } finally {
        Binder.restoreCallingIdentity(token);
    }
}

UsbUserPermissionManager

这边通过Binder获取到用户的Uid,Pid等信息,然后交给 UsbPermissionManager 处理:

  1. 如果检测已经有权限,则直接启动pendingIntent 指向的intent
  2. 如果没有权限,则启动 权限申请的 Dialog (实际上是个位于System UI中Dialog风格的 Activity )
// frameworks/base/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
// com.android.server.usb.UsbUserPermissionManager#requestPermission(android.hardware.usb.UsbDevice, java.lang.String, android.app.PendingIntent, int, int)

public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
        int uid) {
    Intent intent = new Intent();
    // respond immediately if permission has already been granted
    if (hasPermission(device, packageName, pid, uid)) {
        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
        intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
        try {
            pi.send(mContext, 0, intent);
        } catch (PendingIntent.CanceledException e) {
            if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
        }
        return;
    }
    if (isCameraDevicePresent(device)) {
        if (!isCameraPermissionGranted(packageName, pid, uid)) {
            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
            try {
                pi.send(mContext, 0, intent);
            } catch (PendingIntent.CanceledException e) {
                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
            }
            return;
        }
    }
    requestPermissionDialog(device, null,
            mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid);
}

// com.android.server.usb.UsbUserPermissionManager#requestPermissionDialog(android.hardware.usb.UsbDevice, android.hardware.usb.UsbAccessory, boolean, java.lang.String, int, android.content.Context, android.app.PendingIntent)
void requestPermissionDialog(@Nullable UsbDevice device,
        @Nullable UsbAccessory accessory,
        boolean canBeDefault,
        @NonNull String packageName,
        int uid,
        @NonNull Context userContext,
        @NonNull PendingIntent pi) {
    long identity = Binder.clearCallingIdentity();
    Intent intent = new Intent();
    if (device != null) {
        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    } else {
        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
    }
    intent.putExtra(Intent.EXTRA_INTENT, pi);
    intent.putExtra(Intent.EXTRA_UID, uid);
    intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
    intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
    intent.setComponent(ComponentName.unflattenFromString(userContext.getResources().getString(
            com.android.internal.R.string.config_usbPermissionActivity)));
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    try {
        userContext.startActivityAsUser(intent, mUser);
    } catch (ActivityNotFoundException e) {
        Slog.e(TAG, "unable to start UsbPermissionActivity");
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
}

SystemUI#UsbPermissionActivity

定义声明

前面会启动com.android.internal.R.string.config_usbPermissionActivity 定义的 Activity中,此字符串的定义位于 (./frameworks/base/core/res/res/values/config.xml)

<string name="config_usbPermissionActivity" translatable="false">com.android.systemui/com.android.systemui.usb.UsbPermissionActivity</string>

对应的Activity在 ./frameworks/base/packages/SystemUI/AndroidManifest.xml 中声明,其主题为一个Dialog

        <!-- started from UsbDeviceSettingsManager -->
        <activity android:name=".usb.UsbPermissionActivity"
            android:exported="true"
            android:permission="android.permission.MANAGE_USB"
            android:theme="@style/Theme.SystemUI.Dialog.Alert"
            android:finishOnCloseSystemDialogs="true"
            android:excludeFromRecents="true">

响应

  1. 点击按钮后,会记录是否赋予了权限;
  2. 在销毁时,会设置对应的权限到UsbService,然后启动对应的PendingIntent;
// frameworks/base/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
public void onClick(DialogInterface dialog, int which) {
    if (which == AlertDialog.BUTTON_POSITIVE) {
        mPermissionGranted = true;
    }
    finish();
}

// com.android.systemui.usb.UsbPermissionActivity#onDestroy
@Override
public void onDestroy() {
    IBinder b = ServiceManager.getService(USB_SERVICE);
    IUsbManager service = IUsbManager.Stub.asInterface(b);
    // send response via pending intent
    Intent intent = new Intent();
    try {
        if (mDevice != null) {
            intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
            if (mPermissionGranted) {
                service.grantDevicePermission(mDevice, mUid);
                if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
                    final int userId = UserHandle.getUserId(mUid);
                    service.setDevicePackage(mDevice, mPackageName, userId);
                }
            }
        }
        if (mAccessory != null) {
            intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
            if (mPermissionGranted) {
                service.grantAccessoryPermission(mAccessory, mUid);
                if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
                    final int userId = UserHandle.getUserId(mUid);
                    service.setAccessoryPackage(mAccessory, mPackageName, userId);
                }
            }
        }
        intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted);
        mPendingIntent.send(this, 0, intent);
    } catch (PendingIntent.CanceledException e) {
        Log.w(TAG, "PendingIntent was cancelled");
    } catch (RemoteException e) {
        Log.e(TAG, "IUsbService connection failed", e);
    }
    if (mDisconnectedReceiver != null) {
        unregisterReceiver(mDisconnectedReceiver);
    }
    super.onDestroy();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值