赞
踩
相关资源:
android GrantPermissionsActivity 详解
android grantRuntimePermission 详解
之前几篇博文(Android Runtime Permission 详解、android grantRuntimePermission 详解、
android GrantPermissionsActivity 详解)主要是分析了Runtime permission的控制流程。
对于Runtime permission,在应用需要对应的权限的时候,会给用户一个提示。
CTA 要求对于BT、WLAN、NFC 等也需要给出相应的提示,所以这些permission也需要单独的控制。
这一篇来分析一下Normal permission或者intall time permission 在android M之后单独控制流程。
这里我们用蓝牙来举例说明,蓝牙的permission 如下:
- <permission android:name="android.permission.BLUETOOTH_ADMIN"
- android:description="@string/permdesc_bluetoothAdmin"
- android:label="@string/permlab_bluetoothAdmin"
- android:protectionLevel="normal" />
如果需要打开/关闭蓝牙的控制,必须申请这个权限,但是这个权限level 是normal的,并不是runtime permission,不能通过PMS 中的grantRuntimePermission 或者是invokeRuntimePermission 来管理。对于这种normal permission,我们可以利用AppOps 来管理。
BT enable:
先来看下BT enable/disable是如何实现的?
framework/base/core/java/android/bluetooth/BluetoothAdapter.java:
- public boolean enableBLE() {
- if (!isBleScanAlwaysAvailable()) return false;
-
- try {
- String packageName = ActivityThread.currentPackageName();
- mManagerService.updateBleAppCount(mToken, true, packageName);
- if (isLeEnabled()) {
- if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled");
- return true;
- }
- if (DBG) Log.d(TAG, "enableBLE(): Calling enable");
- return mManagerService.enable(packageName);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
-
- return false;
- }
这里是提供用户enable的开关入口,最终会调用到IBluetoothManager.enable:
class BluetoothManagerService extends IBluetoothManager.Stub {}
- public boolean enable(String packageName) throws RemoteException {
- final int callingUid = Binder.getCallingUid();
- final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-
- if (isBluetoothDisallowed()) {
- if (DBG) {
- Slog.d(TAG,"enable(): not enabling - bluetooth disallowed");
- }
- return false;
- }
-
- if (!callerSystem) {
- if (!checkIfCallerIsForegroundUser()) {
- Slog.w(TAG, "enable(): not allowed for non-active and non system user");
- return false;
- }
-
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH ADMIN permission");
-
- if (!isEnabled() && mPermissionReviewRequired
- && startConsentUiIfNeeded(packageName, callingUid,
- BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
- return false;
- }
- }
- ...
- ...
- }
我们通过AppOps 控制,只需要在这里,enable 真正实施之前加一个dialog 确认即可。
- AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
- String packages = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
- if ((Binder.getCallingUid() >= Process.FIRST_APPLICATION_UID)
- && (packages.indexOf("android.uid.systemui") != 0)
- && (packages.indexOf("android.uid.system") != 0)) {
- int result = mAppOpsManager.noteOp(AppOpsManager.OP_BLUETOOTH_ADMIN,
- Binder.getCallingUid(), packages);
- if (result == AppOpsManager.MODE_IGNORED) {
- return false;
- }
- }
通过AppOps 的noteOp 接口确认当前permission 是否是允许或者禁止状态,返回值分别是AppOpsManager.MODE_ALLOWED 和AppOpsManager.MODE_IGNORED。
AppOpsManager.noteOp:
- public int noteOp(int op, int uid, String packageName) {
- try {
- int mode = mService.noteOperation(op, uid, packageName);
- if (mode == MODE_ERRORED) {
- throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
- }
- return mode;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
当然还有一个接口:
- public int noteOpNoThrow(int op, int uid, String packageName) {
- try {
- return mService.noteOperation(op, uid, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
显而易见,对于返回值MODE_ERRORED 是否进行exception 提醒进行了区分。但是最终调用的都是AppOpsService 中的noteOperation 接口:
- public int noteOperation(int code, int uid, String packageName) {
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- String resolvedPackageName = resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
- }
- return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
- }
noteOperationUnchecked 的source code 这里就不给出了,我们可以看到最终会调用到这里,确认此权限在app 中是否会被允许或禁止。
那按照CTA 的要求,我们可以在这里给出用户dialog 提示,并且让用户选择是否打开。
具体dialog 的source code 涉及公司保密协议,暂不给出,不过欢迎一起交流。
NFC enable 的管控:
根据BT 的经验,对于NFC 可以同样的进行控制:
- private boolean isNfcAllowed() {
- AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
- String packages = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
- if ((Binder.getCallingUid() >= Process.FIRST_APPLICATION_UID)
- && (packages.indexOf("android.uid.systemui") != 0)
- && (packages.indexOf("android.uid.system") != 0)) {
- int result = mAppOpsManager.noteOp(AppOpsManager.OP_NFC,
- Binder.getCallingUid(), packages);
- if (result == AppOpsManager.MODE_IGNORED) {
- return false;
- }
- }
- return true;
- }
最主要是其中OP_BLUETOOTH_ADMIN 和 OP_NFC 的逻辑控制需要在AppOpsManager 中控制好。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。