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

    实际上调用的是 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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

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

    权限申请

    参考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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

    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.UsbPermissionActivitystring>
    
    • 1

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

            
            <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
    • 3
    • 4
    • 5
    • 6
    • 7

    响应

    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
    • 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
  • 相关阅读:
    神仙级别Kafka架构笔记,阿里架构师看到都感慨怎么没早看到
    第八章、python中的序列、迭代器、生成器及可迭代对象------迭代器(iterator)与生成器(generator)
    Android 10 如何在通知栏下拉状态栏会暂停第三方应用播放视频
    软件测试打工人必须掌握的这9项技能.....
    ORB-SLAM2从理论到代码实现(十):LoopClosing程序详解
    node编写服务器
    计算机毕业设计(75)php小程序毕设作品之网上销售小程序商城系统
    第三次科技革命(一)
    30岁的程序员,技术一般,去考公还是努力提升技术?
    诈骗对象逐渐年轻化,“00”后为何成黑平台青睐对象?
  • 原文地址:https://blog.csdn.net/mospuito/article/details/126168162