Android系统在MarshMallow以前,权限都是在安装的时候授予的,虽然在4.3时,Google就试图在源码里面引入AppOpsManager来达到动态控制权限的目的,但因为不太成熟,在Release版本中都是把这个功能给隐藏掉的。在6.0以后,Google为了简化安装流程且方便用户控制权限,正式引入了runtime-permission,容许用户在运行的时候动态控制权限。对于开发而言就是将targetSdkVersion设置为23,而且在相应的时机动态申请权限,在适配了Android6.0的App运行在Android 6.0+的手机上时,就会调用6.0相关的API,不过在低版本的手机上,仍然是按安装时权限处理。javascript
AppOpsManager是Google在Android4.3引入的动态权限管理方式,不过,Google以为不成熟,因此在每一个发行版的时候,老是会将这个功能给屏蔽掉。该功能跟国内的权限动态管理表现相似,这里用CyanogenMod12里面的实现讲述一下,(国内的ROM源码拿不到,不过从表现来看,实现应该相似)。AppOpsManager实现的动态管理的本质是:将鉴权放在每一个服务内部,好比,若是App要申请定位权限,定位服务LocationManagerService会向AppOpsService查询是否授予了App定位权限,若是须要受权,就弹出一个系统对话框让用户操做,并根据用户的操做将结果持久化在文件中,若是在Setting里设置了响应的权限,也会去更新相应的权限操做持久化文件/data/system/appops.xml,下次再次申请服务的时候,服务会再次鉴定权限。html
App在使用定位服务的时候,通常是经过LocationManager的requestLocationUpdates获取定位,实际上是经过Binder请求LocationManagerService去定位。java
/android/location/LocationManager.javaandroid
private void requestLocationUpdates(LocationRequest request, LocationListener listener,
Looper looper, PendingIntent intent) {
...
try {
mService.requestLocationUpdates(request, transport, intent, packageName);
...复制代码
/com/android/server/LocationManagerService.javagit
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
PendingIntent intent, String packageName) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
checkPackageName(packageName);
<!--关键函数 1 ,查询Manifest文件,是否进行了权限声明 --> int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, request.getProvider()); 。。。 <!--获取调用app的pid跟uid--> 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 { <!--关键函数 2 检查是否动态受权了权限,或者拒绝了权限--> 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查询APP是否在Manifest中进行了声明github
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代码app
@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);
...
<!--关键点 1-->
if (switchOp.mode == AppOpsManager.MODE_IGNORED ||
switchOp.mode == AppOpsManager.MODE_ERRORED) {
op.rejectTime = System.currentTimeMillis();
op.ignoredCount++;
return switchOp.mode;
<!--关键点 2--> } else if(switchOp.mode == AppOpsManager.MODE_ALLOWED) { op.time = System.currentTimeMillis(); op.rejectTime = 0; op.allowedCount++; return AppOpsManager.MODE_ALLOWED; } else { op.noteOpCount++; <!--关键函数 3--> userDialogResult = askOperationLocked(code, uid, packageName, switchOp); } } return userDialogResult.get(); }复制代码
在上面的代码里面,一、2是对已经处理过的场景直接返回已受权,或者已经拒绝,而3就是咱们常见受权入口对话框,这里是统一在AppOpsServie中进行受权处理的。askOperationLocked会显示一个系统对话框,用户选择受权或者拒绝后,AppOpsServie会将选择记录在案,并通知申请服务提供或者拒绝服务。askOperationLocked经过mHandler发送鉴权Message,看一下实现其实就是新建了一个PermissionDialog受权对话框,而且将AppOpsService的引用传了进去,受权后会经过mService.notifyOperation通知受权结果。ide
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PERMISSION_DIALOG: {
HashMap<String, Object> data =
(HashMap<String, Object>) 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;
}
}
};复制代码
在Android4.3到5.1之间,虽然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) {
}
}复制代码
遍历源码也只有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));
}
}复制代码
Android6.0的runtime-permission机制让用户在任什么时候候均可以取消受权,所以,每次在申请系统服务的时候,都要动态查询是否获取了相应的权限,若是没有获取,就须要动态去申请,首先先看一下权限的查询:
support-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.
<!--root及System能获取全部权限-->
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来进行的。
public int checkUidPermission(String permName, int uid) {
final boolean enforcedDefault = isPermissionEnforcedDefault(permName);
synchronized (mPackages) {
<!--PackageManagerService.Setting.mUserIds数组中,根据uid查找uid(也就是package)的权限列表-->
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
<!--mSystemPermissions记录一些系统级的应用的 uid 对应的 permission->
HashSet<String> perms = mSystemPermissions.get(uid);
if (perms != null && perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
}
if (!isPermissionEnforcedLocked(permName, enforcedDefault)) {
return PackageManager.PERMISSION_GRANTED;
}
}
return PackageManager.PERMISSION_DENIED;
}复制代码
@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;
}
// Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms != null) {
if (perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
}
return PackageManager.PERMISSION_DENIED;
}复制代码
能够看到Android6.0以后,对权限的操做是PermissionsState
PermissionsState.java (android-6.0\frameworks\base\services\core\java\com\android\server\pm)
public boolean hasPermission(String name, int userId) {
enforceValidUserId(userId);
if (mPermissions == null) {
return false;
}
PermissionData permissionData = mPermissions.get(name);
return permissionData != null && permissionData.isGranted(userId);
}复制代码
从上面的代码能够很清晰看出,6.0以后,除了声明了权限以外,还必须是受权了的。运行时权限跟install权限有所不一样,对于install权限isGranted一直返回是True,这里先没必要深究PermissionsState是怎么存进内存,先记住,后面会将讲。
申请权限能够经过V4包里面的ActivityCompat,它已经对不一样版本作了兼容
ActivityCompat.java
public static void requestPermissions(final @NonNull Activity activity,
final @NonNull String[] permissions, final int requestCode) {
if (Build.VERSION.SDK_INT >= 23) {
ActivityCompatApi23.requestPermissions(activity, permissions, requestCode);
} else if (activity instanceof OnRequestPermissionsResultCallback) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
final int[] grantResults = new int[permissions.length];
PackageManager packageManager = activity.getPackageManager();
String packageName = activity.getPackageName();
final int permissionCount = permissions.length;
for (int i = 0; i < permissionCount; i++) {
grantResults[i] = packageManager.checkPermission(
permissions[i], packageName);
}
((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
requestCode, permissions, grantResults);
}
});
}
}复制代码
能够看到,若是是6.0如下,直接经过PKMS查询是否在Manifest里面申请了权限,并把查询结果经过onRequestPermissionsResult回调传给Activity或者Fragment。其实这里只要在Manifest中声明了,就会默认是Granted。接着往下看:ActivityCompatApi23最终会调用activity.requestPermissions去请求权限。
Activity
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
}复制代码
Intent实际上是经过PackageManager(ApplicationPackageManager实现类)获取的Intent
public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
if (ArrayUtils.isEmpty(permissions)) {
throw new NullPointerException("permission cannot be null or empty");
}
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
intent.setPackage(getPermissionControllerPackageName());
return intent;
}复制代码
这里首先是隐式的获取受权Activity组件相关信息(GrantPermissionsActivity),其实就是对话框样式的受权Activity,它是PackageInstaller系统应用里面的一个Activity。这里的getPermissionControllerPackageName其实就是获取相应的包名,
ApplicationPackageManager.java (android-6.0\frameworks\base\core\java\android\app)
@Override
public String getPermissionControllerPackageName() {
synchronized (mLock) {
if (mPermissionsControllerPackageName == null) {
try {
mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName();
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}
return mPermissionsControllerPackageName;
}
}复制代码
最终经过PackageManagerService获取包名
PackageManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\pm)
@Override
public String getPermissionControllerPackageName() {
synchronized (mPackages) {
return mRequiredInstallerPackage;
}
}复制代码
mRequiredInstallerPackage这个变量具体赋值是在PMS的构造器中:对于原生Android 6.0,权限管理的APP跟安装器是同一个
mRequiredInstallerPackage = getRequiredInstallerLPr();复制代码
这里会获得PackageInstaller应用的相关信息,PackageInstaller负责应用的安装与卸载,里面还包含了对受权管理的一些逻辑。startActivityForResult启动的就是PackageInstaller中的GrantPermissionsActivity,该Activity主要负责权限的授予工做。
<activity android:name=".permission.ui.GrantPermissionsActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/GrantPermissions">
<intent-filter>
<action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>复制代码
这是一个相似于对话框的悬浮窗样式的Activity
<style name="GrantPermissions" parent="Settings">
<item name="android:windowIsFloating">true</item>
<item name="android:windowElevation">@dimen/action_dialog_z</item>
<item name="android:windowSwipeToDismiss">false</item>
</style>复制代码
以后就是动态更新权限流程:
经过上面的流程,咱们进入了GrantPermissionsActivity,在这个Activity里面,若是一开始没有得到权限,就会弹出权限申请对话框,根据用户的操做去更新PKMS中的权限信息,同时将更新的结构持久化到runtime-permissions.xml中去。
GrantPermissionsActivity
GrantPermissionsActivity实际上是利用GroupState对象与PKMS通讯,远程更新权限的,固然,若是权限都已经授予了,那么就不须要再次弹出权限申请对话框。
public class GrantPermissionsActivity extends OverlayTouchActivity implements GrantPermissionsViewHandler.ResultListener {
private LinkedHashMap<String, GroupState> mRequestGrantPermissionGroups = new LinkedHashMap<>();
....
@Override
public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
GroupState groupState = mRequestGrantPermissionGroups.get(name);
if (groupState.mGroup != null) {
if (granted) {
<!--权限更新时机-->
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
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以上才有
<pkg name="com.snail.labaffinity">
<item name="android.permission.CALL_PHONE" granted="true" flags="0" /> <item name="android.permission.CAMERA" granted="false" flags="1" /> </pkg>复制代码
这些持久化的数据会在手机启动的时候由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);
<!--加载package信息-->复制代码
根据SettingsFile或者BackupSettingsFile读取相应的设置信息 生成PackageSetting对象,里面有权限列表字段protected final PermissionsState mPermissionsState;,以后再运行中,动态权限的操做都是针对这个对象
boolean readLPw(@NonNull List<UserInfo> 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);
...
<!--读取runtime权限信息-->
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 Manifest清单去读取,只会在启动时读取一次。Android6.0以前会吧全部的权限都放置在data/system/packages.xml文件中。Android6.0以后,分为运行时权限跟普通权限,普通权限仍是放在data/system/packages.xml中,运行时权限防止在data/system/users/0/runtime-permissions.xml文件中。根据运行时是否动态申请去更新权限。
Android6.0里,普通权限仍然按照运行时权限的模型,只是granted="true",就是永远是取得受权的。因此能够直接得到权限申请成功的回调。若是查看packages.xml,就会发现:以下信息:
<perms>
<item name="android.permission.INTERNET" granted="true" flags="0" /> <item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" /> </perms>复制代码
关键节点并非查询是否具备该权限,Android6.0以前的 权限查询是不会触发权限申请与受权的,只有在请求系统服务的时候,由系统服务调用AppopsManager去查询是否赋予了该权限,第一次未操做确定是null,未赋予就可能会触发权限申请逻辑,这个点在各个系统服务内部,由AppOpsService服务统一管理,不过对于官方的Release版本,其实只有系统通知APP才有动态权限管理的能力,其余都没有操做能力。
一、Android 安全機制概述 Permission
二、android permission权限与安全机制解析
三、android6.0权限管理原理
四、深刻理解 PackageManagerService
五、Android 4.3 隐藏功能 App Ops 分析
六、Android 权限机制,你真的了解吗?
七、Android原生权限管理:AppOps
八、 Android 5.1 AppOps总结
九、CM12源码