Android权限管理原理(含6.0-4.3)

Android 4.3-5.1 AppOpsManager动态权限管理(官方不成熟的权限管理)

AppOpsManager 是Google在Android4.3-Android5.0引入的动态权限管理方式,但是又与Google觉得不成熟,所以在每个发行版的时候,总是会将这个功能给屏蔽掉。国内一些早期版本的权限动态管理的表现类似,这里用CyanogenMod12里面的实现讲述一下,国内的ROM源码拿不到,不过从表现来看,实现应该类似。

在一开始,其实Google将权限的动态管理放在每个服务内部,类似于登记的策略,比如,如果一个App要申请Cammera权限,android6.0之前是不要显示的动态申请的,只需要在Manifest中声明即可,在需要使用Camera的时候,如果是第一,App一定没有在Camera服务中登记过,这时候,Camera服务就会调用AppOpsManager访问AppOpsService,发起权限申请请求,并弹出统一的权限申请对话框,并做好登记,如果获得了权限,就开启所请求的服务,如果被拒绝,就拒绝向APP提供服务。这个时机是在AppOpsManager鉴定的时候,类似于对没有获得权限的APP进行服务拦截,如果在Setting里设置了响应的权限,也会去更新相应的权限操作持久化文件/data/system/appops.xml,下次再次申请服务的时候,服务会再次鉴定权限。

举个栗子(定位服务LocationManagerService)

CM12 源码

在调用LocationManager的requestLocationUpdates函数获取定位信息时候,其实是通过Binder请求LocationManagerService去定位。

LocationManager

private void requestLocationUpdates(LocationRequest request, LocationListener listener,
        Looper looper, PendingIntent intent) {

    String packageName = mContext.getPackageName();

    // wrap the listener class
    ListenerTransport transport = wrapListener(listener, looper);

    try {
        mService.requestLocationUpdates(request, transport, intent, packageName);
   } catch (RemoteException e) {
       Log.e(TAG, "RemoteException", e);
   }
}

看一下LocationManagerService中关键代码

LocationManagerService

@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
        PendingIntent intent, String packageName) {
    if (request == null) request = DEFAULT_LOCATION_REQUEST;
    checkPackageName(packageName);
    
    int allowedResolutionLevel = getCallerAllowedResolutionLevel();
    checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
            request.getProvider());
    。。。
    
    final int pid = Binder.getCallingPid();
    final int uid = Binder.getCallingUid();
    // providers may use public location API's, need to clear identity
    long identity = Binder.clearCallingIdentity();
    try {
    
        checkLocationAccess(uid, packageName, allowedResolutionLevel);

        synchronized (mLock) {
            Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
                    packageName, workSource, hideFromAppOps);
            if (receiver != null) {
                    requestLocationUpdatesLocked(sanitizedRequest, receiver, pid,
                                                 uid, packageName);
            }
        }
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
}

getCallerAllowedResolutionLevel主要通过调用getAllowedResolutionLevel,通过mContext.checkPermission查询APP是否在Manifest中声明了权限

private int getCallerAllowedResolutionLevel() {
    return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
}

     private int getAllowedResolutionLevel(int pid, int uid) {
     if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
             pid, uid) == PackageManager.PERMISSION_GRANTED) {
         return RESOLUTION_LEVEL_FINE;
     } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
             pid, uid) == PackageManager.PERMISSION_GRANTED) {
         return RESOLUTION_LEVEL_COARSE;
     } else {
         return RESOLUTION_LEVEL_NONE;
     }
 }

checkLocationAccess这里才是动态鉴权的入口,在checkLocationAccess函数中,会调用mAppOps.checkOp去鉴权,mAppOps就是AppOpsManager实例,

boolean checkLocationAccess(int uid, String packageName, int allowedResolutionLevel) {
    int op = resolutionLevelToOp(allowedResolutionLevel);
    if (op >= 0) {
        int mode = mAppOps.checkOp(op, uid, packageName);
        if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_ASK ) {
            return false;
        }
    }
    return true;
}

进而通过Binder向AppOpsService服务发送鉴权请求

 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) {
    }
    return MODE_IGNORED;
}

AppOpsService负责动态权限的鉴定跟更新,去看noteOperation代码

@Override
public int noteOperation(int code, int uid, String packageName) {
    final Result userDialogResult;
    verifyIncomingUid(uid);
    verifyIncomingOp(code);
    synchronized (this) {
        Ops ops = getOpsLocked(uid, packageName, true);
        ...
          
        if (switchOp.mode == AppOpsManager.MODE_IGNORED ||
            switchOp.mode == AppOpsManager.MODE_ERRORED) {

            op.rejectTime = System.currentTimeMillis();
            op.ignoredCount++;
            return switchOp.mode;
           
        } else if(switchOp.mode == AppOpsManager.MODE_ALLOWED) {

            op.time = System.currentTimeMillis();
            op.rejectTime = 0;
            op.allowedCount++;
            return AppOpsManager.MODE_ALLOWED;
        } else {
            op.noteOpCount++;
            
            userDialogResult = askOperationLocked(code, uid, packageName,
                switchOp);
        }
    }
    return userDialogResult.get();
}

在上面的代码里面,1、2是对已经处理过的场景直接返回已授权,或者已经拒绝,而3 就是我们常见授权入口对话框,这里是统一在AppOpsServie中进行授权处理的。askOperationLocked会显示一个系统对话框,用户选择授权或者拒绝后,AppOpsServie会将选择记录在案,并通知申请服务提供或者拒绝服务。askOperationLocked通过mHandler发送鉴权Message,看一下实现其实就是新建了一个PermissionDialog授权对话框,并且将AppOpsService的引用传了进去,授权后会通过mService.notifyOperation通知授权结果。

  mHandler = new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case SHOW_PERMISSION_DIALOG: {
                    HashMap data =
                        (HashMap) msg.obj;
                    synchronized (this) {
                        Op op = (Op) data.get("op");
                        Result res = (Result) data.get("result");
                        op.dialogResult.register(res);
                        if(op.dialogResult.mDialog == null) {
                            Integer code = (Integer) data.get("code");
                            Integer uid  = (Integer) data.get("uid");
                            String packageName =
                                (String) data.get("packageName");
                            Dialog d = new PermissionDialog(mContext,
                                AppOpsService.this, code, uid,
                                packageName);
                            op.dialogResult.mDialog = (PermissionDialog)d;
                            d.show();
                        }
                    }
                }break;
                }
            }
        };

Android权限管理原理(含6.0-4.3)_第1张图片

Android发行版源码对于动态权限管理的支持(几乎为零)

虽然App可以获得AppOpsManager的实例,但是真正操作动态权限的接口setMode函数是被隐藏,如下

/** @hide */
public void setMode(int code, int uid, String packageName, int mode) {
    try {
        mService.setMode(code, uid, packageName, mode);
    } catch (RemoteException e) {
    }
}

在Android4.3-5.0之间,setMode也是不对外开放的,看源码也只有NotificationManagerService这个系统应用使用了setMode,也就是说发行版,只有通知是通过系统的通知管理进行动态管理的。

public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
    checkCallerIsSystem();

    Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);

    mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
            enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);

    // Now, cancel any outstanding notifications that are part of a just-disabled app
    if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
        cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid));
    }
}

对于6.0以下的手机权限如何检测

对于Android6.0以下的手机,不需要关心targetVersion。先说个自己验证的结果:基本没法检测,同时也不需要检测,就算检测出来也没有多大意义,因为,触发时机是在真正的调用服务时候。对于4.3到6.0之前的国产ROM,虽然采用AppopsManagerService,但是并未按照Google的模型对所有权限进行适配,在这个模型下,也就适配了两个权限,(悬浮窗还不一定有)

  • 通知权限 public static final int OP_POST_NOTIFICATION = 11;
  • 悬浮窗权限 public static final int OP_SYSTEM_ALERT_WINDOW = 24;

Google发行版的APPOpsService,基本是把整个鉴权逻辑给屏蔽了,通过CM的源码,课对这部分代码窥探一斑,如果整个权限都采用4.3权限管理模型,在拒绝一项权限的时候,这个操作会被持久化到appops.xml中去,但是具体看下去,其实并不是如此,这种机制只对以上两个权限生效:

    
    
    
    
    
    

    
    
    
     
    
    
    
    
    

国产rom中,假如你拒绝授权位置权限,按照AppOpsService模型,该操作应该被持久化到appops.xml中去,但是,结果并非如此,也就是说,对于其他权限,国产ROM应该是自己糊弄了一套持久管理,持久化Android系统API无法访问的地方,仅仅为自身ROM可见。appops.xml真正被系统使用时从Android6.0开始,其实Android6.0是有两套权限管理的,这其实很混乱,不知道Google怎么想的,不过6.0似乎也有漏洞:权限的授予跟回收权限好像并不配对

那么这就带来了一个问题,在Android4.3到Android6.0之间的版本,并没有同一个API来检测是否获取了某种权限,因为你动态更新的权限并未持久化到appops.xml中去。对于Android6.0之前的ROM,虽然不能检测,但完全可以直接用服务,不会崩溃,因为如果真需要鉴权,它的鉴权时机其实是在服务使用的时候。AppopsManager在6.0之前,只能用来检测通知,可能还有悬浮窗。

Android 6.0权限管理原理

Android6.0的动态权限管理让用户在任何时候都可以取消授权,因此,每次在使用系统服务的时候,都要动态查询是否获取了相应的权限,如果没有获取,就需要动态去申请。

Android6.0权限查询

V4兼容包里面提供了一个工具类PermissionChecker,可以用来检查权限获取情况。

PermissionChecker

public static int checkPermission(@NonNull Context context, @NonNull String permission,
        int pid, int uid, String packageName) {
    if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
        return PERMISSION_DENIED;
    }

    String op = AppOpsManagerCompat.permissionToOp(permission);
    if (op == null) {
        return PERMISSION_GRANTED;
    }

    if (packageName == null) {
        String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
        if (packageNames == null || packageNames.length <= 0) {
            return PERMISSION_DENIED;
        }
        packageName = packageNames[0];
    }

    if (AppOpsManagerCompat.noteProxyOp(context, op, packageName)
            != AppOpsManagerCompat.MODE_ALLOWED) {
        return PERMISSION_DENIED_APP_OP;
    }

    return PERMISSION_GRANTED;
}

这里我们只关心context.checkPermission,从上面对于4.3-5.1的APPOpsManager的分析,我们知道AppOpsManagerCompat本身的一些操作对于权限管理并没有实际意义,只是用来做一些标记,最多就是对于统治权限有些用。接下来看checkPermission:

ContextImple.java

/** @hide */
@Override
public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }
    try {
        return ActivityManagerNative.getDefault().checkPermissionWithToken(
                permission, pid, uid, callerToken);
    } catch (RemoteException e) {
        return PackageManager.PERMISSION_DENIED;
    }
}

接着往下看

ActivityManagerNative.java

public int checkPermission(String permission, int pid, int uid)
        throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeString(permission);
    data.writeInt(pid);
    data.writeInt(uid);
    mRemote.transact(CHECK_PERMISSION_TRANSACTION, data, reply, 0);
    reply.readException();
    int res = reply.readInt();
    data.recycle();
    reply.recycle();
    return res;
}

ActivityManagerService

public int checkPermission(String permission, int pid, int uid) {
    if (permission == null) {
        return PackageManager.PERMISSION_DENIED;
    }
    return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
}

进而调用ActivityManager.checkComponentPermission,调用AppGlobals.getPackageManager().checkUidPermission(permission, uid);

ActivityManager.java

/** @hide */
public static int checkComponentPermission(String permission, int uid,
        int owningUid, boolean exported) {
    // Root, system server get to do everything.

    
    if (uid == 0 || uid == Process.SYSTEM_UID) {
        return PackageManager.PERMISSION_GRANTED;
    }
        。。。
    
    try {
        return AppGlobals.getPackageManager()
                .checkUidPermission(permission, uid);
    } catch (RemoteException e) {
        // Should never happen, but if it does... deny!
        Slog.e(TAG, "PackageManager is dead?!?", e);
    }
    return PackageManager.PERMISSION_DENIED;
}

最终调用PackageManagerService.java去查看是否有权限。到这里,我们只需要知道权限的查询其实是通过PKMS来进行的。心里先有个底,权限的更新,持久化,恢复都是通过PKMS来进行的。

PKMS不同版本的权限查询

Android5.0的checkUidPermission

 public int checkUidPermission(String permName, int uid) {
        final boolean enforcedDefault = isPermissionEnforcedDefault(permName);
        synchronized (mPackages) {
        
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                GrantedPermissions gp = (GrantedPermissions)obj;
                if (gp.grantedPermissions.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
            
                groupState.mGroup.grantRuntimePermissions(doNotAskAgain);
                groupState.mState = GroupState.STATE_ALLOWED;
            } else {
                groupState.mGroup.revokeRuntimePermissions(doNotAskAgain);
                groupState.mState = GroupState.STATE_DENIED;
            }
            updateGrantResults(groupState.mGroup);
        }
        if (!showNextPermissionGroupGrantRequest()) {
            setResultAndFinish();
        }
    }

具体更新流程:

public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
    final int uid = mPackageInfo.applicationInfo.uid;

    // We toggle permissions only to apps that support runtime
    // permissions, otherwise we toggle the app op corresponding
    // to the permission if the permission is granted to the app.
    for (Permission permission : mPermissions.values()) {
        if (filterPermissions != null
                && !ArrayUtils.contains(filterPermissions, permission.getName())) {
            continue;
        }
            ...
            

            // Grant the permission if needed.
            if (!permission.isGranted()) {
                permission.setGranted(true);
                mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
                        permission.getName(), mUserHandle);
            }
            // Update the permission flags.
            if (!fixedByTheUser) {
                // Now the apps can ask for the permission as the user
                // no longer has it fixed in a denied state.
                if (permission.isUserFixed() || permission.isUserSet()) {
                    permission.setUserFixed(false);
                    permission.setUserSet(true);
                    mPackageManager.updatePermissionFlags(permission.getName(),
                            mPackageInfo.packageName,
                            PackageManager.FLAG_PERMISSION_USER_FIXED
                                    | PackageManager.FLAG_PERMISSION_USER_SET,
                            0, mUserHandle);

可以看到最终还是调用PackageManager去更新App的运行时权限,最终走进PackageManagerService服务,

PackageManagerService

 @Override
    public void grantRuntimePermission(String packageName, String name, final int userId) {
        if (!sUserManager.exists(userId)) {
            Log.e(TAG, "No such user:" + userId);
            return;
        }

        ...一些检查

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
                "grantRuntimePermission");

        enforceCrossUserPermission(Binder.getCallingUid(), userId,
                true /* requireFullPermission */, true /* checkShell */,
                "grantRuntimePermission");
                。。。。。
                ...
            uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
            sb = (SettingBase) pkg.mExtras;
            if (sb == null) {
                throw new IllegalArgumentException("Unknown package: " + packageName);
            }

            final PermissionsState permissionsState = sb.getPermissionsState();

              ...
              ...授权

            final int result = permissionsState.grantRuntimePermission(bp, userId);
            switch (result) {
                case PermissionsState.PERMISSION_OPERATION_FAILURE: {
                    return;
                }

                case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
                    final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
                        }
                    });
                }
                break;
            }

            mOnPermissionChangeListeners.onPermissionsChanged(uid);


              

            // Not critical if that is lost - app has to request again.
            mSettings.writeRuntimePermissionsForUserLPr(userId, false);
        }
 private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(PackageParser.Package pkg,
        BasePermission bp) {
    int index = pkg.requestedPermissions.indexOf(bp.name);
    if (index == -1) {
        throw new SecurityException("Package " + pkg.packageName
                + " has not requested permission " + bp.name);
    }
    if (!bp.isRuntime() && !bp.isDevelopment()) {
        throw new SecurityException("Permission " + bp.name
                + " is not a changeable permission type");
    }
}

首先要更新内存中的权限授予情况

PermissionsState.java

 private int grantPermission(BasePermission permission, int userId) {
    if (hasPermission(permission.name, userId)) {
        return PERMISSION_OPERATION_FAILURE;
    }

    final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
    final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;

    PermissionData permissionData = ensurePermissionData(permission);

    if (!permissionData.grant(userId)) {
        return PERMISSION_OPERATION_FAILURE;
    }

    if (hasGids) {
        final int[] newGids = computeGids(userId);
        if (oldGids.length != newGids.length) {
            return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
        }
    }

    return PERMISSION_OPERATION_SUCCESS;
}
    private PermissionData ensurePermissionData(BasePermission permission) {
    if (mPermissions == null) {
        mPermissions = new ArrayMap<>();
    }
    PermissionData permissionData = mPermissions.get(permission.name);
    if (permissionData == null) {
        permissionData = new PermissionData(permission);
        mPermissions.put(permission.name, permissionData);
    }
    return permissionData;
}

下一步,要将更新的权限持久化到文件中去 mSettings.writeRuntimePermissionsForUserLPr

RuntimePermission持久化

Settings.java

 public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
    if (sync) {
        mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
    } else {
        mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
    }
}

Settings.getPackageLPw这个方法,这是在安装应用扫描的时候scanPackageDirtyLI方法调用的,里面可以看到Settings类中的mUserIds、mPackages里面存的value还有PackageManagerService中的mPackages.pkg. mExtras都是同一个玩意奏是个PackageSetting。

  private File getUserRuntimePermissionsFile(int userId) {
    // TODO: Implement a cleaner solution when adding tests.
    // This instead of Environment.getUserSystemDirectory(userId) to support testing.
    File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
    return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME);
}

在目录data/system/0/runtime-permissions.xml存放需要运行时申请的权限,Android6.0以上才有

  
    
    
  

RuntimePermission 恢复(其实这里也包含普通权限)

这些持久化的数据会在手机启动的时候由PMS读取,开机启动,PKMS扫描Apk,并更新package信息,检查/data/system/packages.xml是否存在,这个文件是在解析apk时由writeLP()创建的,里面记录了系统的permissions,以及每个apk的name,codePath,flags,ts,version,uesrid等信息,这些信息主要通过apk的AndroidManifest.xml解析获取,解析完apk后将更新信息写入这个文件并保存到flash,下次开机直接从里面读取相关信息添加到内存相关列表中,当有apk升级,安装或删除时会更新这个文件,packages.xml放的只包括installpermission,runtimepermissiono由runtime-permissions.xml存放。

public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    ....
    mSettings = new Settings(mPackages);

          //汇总并更新和Permission相关的信息

  updatePermissionsLPw(null, null, true,

                           regrantPermissions,regrantPermissions);

   //将信息写到package.xml、package.list及package-stopped.xml文件中
   mSettings.writeLPr();

    ....
    mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));

Settings(File dataDir, Object lock) {

    mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);


根据SettingsFile或者BackupSettingsFile读取相应的设置信息 生成PackageSetting对象,里面有权限列表字段protected final PermissionsState mPermissionsState;,之后再运行中,动态权限的操作都是针对这个对象

boolean readLPw(@NonNull List users) {
    FileInputStream str = null;
    if (mBackupSettingsFilename.exists()) {
        try {
            str = new FileInputStream(mBackupSettingsFilename);
            mReadMessages.append("Reading from backup settings file\n");
     ...
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

            String tagName = parser.getName();
            if (tagName.equals("package")) {

       !--读取package信息,包括install权限信息(对于Android6.0package.xml)-->

    readPackageLPw(parser); 
        ...

      

    for (UserInfo user : users) {
        mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
    }
}


private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
    String name = null;
     ...
    (tagName.equals(TAG_PERMISSIONS)) {
            readInstallPermissionsLPr(parser,
                        packageSetting.getPermissionsState());

之后就可以checkpermission了

   @Override
    public int checkUidPermission(String permName, int uid) {
        final int userId = UserHandle.getUserId(uid);

        if (!sUserManager.exists(userId)) {
            return PackageManager.PERMISSION_DENIED;
        }

        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                final SettingBase ps = (SettingBase) obj;
                final PermissionsState permissionsState = ps.getPermissionsState();
                if (permissionsState.hasPermission(permName, userId)) {
                    return PackageManager.PERMISSION_GRANTED;
                }

Android权限管理原理(含6.0-4.3)_第4张图片

原来的权限存放位置在哪?不会都从Android Manifest清单去读取,只会在启动时读取一次。Android6.0之前会吧所有的权限都放置在data/system/packages.xml文件中。Android6.0之后,分为运行时权限跟普通权限,普通权限还是放在data/system/packages.xml中,运行时权限防止在data/system/users/0/runtime-permissions.xml文件中。根据运行时是否动态申请去更新权限。

Android权限管理的关键节点,在哪里?

关键节点并不是查询是否具有该权限,Android6.0之前的 权限查询是不会触发权限申请与授权的,只有在请求系统服务的时候,由系统服务调用AppopsManager去查询是否赋予了该权限,第一次未操作肯定是null,未赋予就可能会触发权限申请逻辑,这个点在各个系统服务内部,由AppOpsService服务统一管理,不过对于官方的Release版本,其实只有系统通知APP才有动态权限管理的能力,其他都没有操作能力。

参考文档

1、Android 安全機制概述 Permission
2、android permission权限与安全机制解析
3、android6.0权限管理原理
4、深入理解 PackageManagerService
5、Android 4.3 隐藏功能 App Ops 分析
6、Android 权限机制,你真的了解吗?
7、Android原生权限管理:AppOps
8、 Android 5.1 AppOps总结
9、[CM12源码]

你可能感兴趣的:(Android框架)