Android 屏蔽USB权限弹框

最近在看Android USB主从设备通讯(下载Demo)相关的东西,调试时每次运行都弹下面的框,即使勾选“默认情况下用于该USB设备”,还是会弹出,在调试阶段频繁弹框影响开发效率。


下面分享下怎么屏蔽USB权限框:

1. 做过相关开发的都见过下面代码

        // Check whether we have permission to access the device.
        if (!mUsbManager.hasPermission(device)) {
            Intent intent = new Intent(ACTION_USB_DEVICE_PERMISSION);
            intent.setPackage(getPackageName());
            PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
                    intent, PendingIntent.FLAG_ONE_SHOT);
            mUsbManager.requestPermission(device, pendingIntent);
            return;
        }
意思在没有权限的情况下,请求权限才弹的框。

2. 分析弹框流程

mUsbManager.requestPermission(UsbDevice device...);//以UsbDevice为例,UsbAccessory同理


                a. frameworks/base/core/java/android/hardware/usb/UsbManager.java=>requestPermission

    public void requestPermission(UsbDevice device, PendingIntent pi) {
        try {
            mService.requestDevicePermission(device, mContext.getPackageName(), pi);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

               b. frameworks/base/core/java/android/hardware/usb/UsbDevice.java=>requestDevicePermission

    @Override
    public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
        final int userId = UserHandle.getCallingUserId();
        getSettingsForUser(userId).requestPermission(device, packageName, pi);
    }
getSettingsForUserUsbDevice成员函数

    private UsbSettingsManager getSettingsForUser(int userId) {
        synchronized (mLock) {
            UsbSettingsManager settings = mSettingsByUser.get(userId);
            if (settings == null) {
                settings = new UsbSettingsManager(mContext, new UserHandle(userId));
                mSettingsByUser.put(userId, settings);
            }
            return settings;
        }
    }
               c. frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java=> requestPermission

    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
      Intent intent = new Intent();


        // respond immediately if permission has already been granted
      if (hasPermission(device)) {
            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
            try {
                pi.send(mUserContext, 0, intent);
            } catch (PendingIntent.CanceledException e) {
                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
            }
            return;
        }


        // start UsbPermissionActivity so user can choose an activity
        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
        requestPermissionDialog(intent, packageName, pi);
    }
接着调成员函数requestPermissionDialog

    private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
        final int uid = Binder.getCallingUid();

        // compare uid with packageName to foil apps pretending to be someone else
        try {
            ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
            if (aInfo.uid != uid) {
                throw new IllegalArgumentException("package " + packageName +
                        " does not match caller's uid " + uid);
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new IllegalArgumentException("package " + packageName + " not found");
        }

        long identity = Binder.clearCallingIdentity();
        intent.setClassName("com.android.systemui",
                "com.android.systemui.usb.UsbPermissionActivity");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(Intent.EXTRA_INTENT, pi);
        intent.putExtra("package", packageName);
        intent.putExtra(Intent.EXTRA_UID, uid);
        try {
            mUserContext.startActivityAsUser(intent, mUser);
        } catch (ActivityNotFoundException e) {
            Slog.e(TAG, "unable to start UsbPermissionActivity");
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }
由此看最终显示框的地方在SystemUI的UsbPermissionActivity。


3. 屏蔽弹框

a. 看到有博客说在UsbPermissionActivity的onCreate显示Dialog(setupAlert)时替换点击确定的代码

相当于自动点击确定

    @Override
    public void onCreate(Bundle icicle) {
        ...
        //setupAlert();
        mPermissionGranted = true;
        finish();
    }

    @Override
    public void onCreate(Bundle icicle) {
        ...
        mAlwaysUse.setOnCheckedChangeListener(this);
        mAlwaysUse.setChecked(true);
        ...
        setupAlert();
        onClick(this, AlertDialog.BUTTON_POSITIVE);
    }
上述两种方法虽然能达到自动授权,个人觉得这种屏蔽方法不理想,下面介绍更好方法。


b. 直接让mUsbManager.hasPermission返回true(自动授权),不更彻底?!

下面列下函数调用关系

①. frameworks/base/core/java/android/hardware/usb/UsbManager.java=>hasPermission

    public boolean hasPermission(UsbDevice device) {
        try {
            return mService.hasDevicePermission(device);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
②. frameworks/base/core/java/android/hardware/usb/UsbDevice.java =>hasDevicePermission
    public boolean hasDevicePermission(UsbDevice device) {
        final int userId = UserHandle.getCallingUserId();
        return getSettingsForUser(userId).hasPermission(device);
    }
③. frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java=>hasPermission

    // Android L(5.x)及之后的版本
    public boolean hasPermission(UsbDevice device) {
        synchronized (mLock) {
            int uid = Binder.getCallingUid();
            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                return true;
            }
            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        }
    }

让条件(uid == Process.SYSTEM_UID || mDisablePermissionDialogs)为真即可

第一种方式:在AndroidManifest.xml里加上android:sharedUserId="android.uid.system",但是apk需打系统签名

第二种方式:让mDisablePermissionDialogs为真

        mDisablePermissionDialogs = context.getResources().getBoolean(
                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
frameworks/base/core/res/res/values/config.xml

    
    true
哈哈,看看上面注释,这么改会引起CTS fail,这就看你的项目是否要过CTS了


    // Android KK(4.4)及之前的版本
    public boolean hasPermission(UsbDevice device) {
        synchronized (mLock) {
            int uid = Binder.getCallingUid();
            if (uid == Process.SYSTEM_UID) {
                return true;
            }
            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        }
    }
这里就用上述第一种方式即可。


感觉自己好啰嗦哦(贴了不少代码),对你有帮助就好。


你可能感兴趣的:(Android)