Android的权限管理越来越完善,但是牵涉的内容也是更多了:从4.4的AppOps到6.0的Runtime Permission,Google还是为之做了不少努力。
AppOps简介:
Android 4.4加入的权限管理:用户在安装应用时,会弹窗列举申请的权限,用户授权才能正常安装,因此只要安装了的应用就会获取所有权限。部分三方OEM厂商会将安装授予的权限改为询问,提高安全性。因为是安装授予的权限,我们暂且把AppOps管理的权限称为“安装权限”。
虽然AppOps在4.4没有对外开放,但仍是后续版本权限管理的基础。
Runtime Permission简介:
Android 6.0启用的权限管理:在应用运行时,需要动态的申请权限,而不仅仅是在Manifest中申明就行了。依据其特性,我们称之为“运行时权限”。6.0中对于sdk >= 23的应用,安装权限都是默认赋予的。
运行时权限与安装权限是相辅相成的,只有同时具备两种权限,应用才能正常使用对应功能。需要指出的是:运行时权限仅针对api >= 23的应用,对于api < 23的应用,仍沿用AppOps的规则。
权限的检查:
在Activity中检查应用是否有权限,ContextWrapper.java:
@Override
public int checkSelfPermission(String permission) {
return mBase.checkSelfPermission(permission);
}
其具体实现在ContextImpl.java中:
@Override
public int checkSelfPermission(String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return checkPermission(permission, Process.myPid(), Process.myUid());
}
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
try {
return ActivityManagerNative.getDefault().checkPermission(
permission, pid, uid);
} catch (RemoteException e) {
return PackageManager.PERMISSION_DENIED;
}
}
走入了ActivityManagerNative.java中,ActivityManagerNative.getDefault()获取到的是一个IActivityManager对象,这实际上是ActivityManagerService的一个远程代理,具体实现在ActivityManagerService.java:
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
return checkComponentPermission(permission, pid, uid, -1, true);
}
int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
如果是MY_PID,直接赋予权限,否则调用ActivityManager.java的checkComponentPermission方法:
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
final int appId = UserHandle.getAppId(uid);
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
if (UserHandle.isIsolated(uid)) {
return PackageManager.PERMISSION_DENIED;
}
// If there is a uid that owns whatever is being accessed, it has
// blanket access to it regardless of the permissions it requires.
if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
if (!exported) {
/*
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
here);
*/
return PackageManager.PERMISSION_DENIED;
}
if (permission == null) {
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;
}
ROOT_UID和SYSTEM_UID也是直接赋予权限的。在上面代码中owningUid是设置为-1的,因此最终会走到AppGlobals.getPackageManager()
.checkUidPermission(permission, uid),通过PackageManager来查询权限,其具体实现在PackageManagerService.java
public int checkUidPermission(String permName, int uid) {
final int userId = UserHandle.getUserId(uid);
if (!sUserManager.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
synchronized (mPackages) {
//依据uid查询,获取一个应用的状态对象,判断该应用是否具有该权限
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;
}
}
//此处处理系统应用的状态,延伸下去发现是从xml文件读取的默认权限。
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;
}
至此运行时权限的查询流程走完了,我们发现运行时权限最终是落脚于PackageManagerService中的,这里才是运行时权限的核心。至于运行时权限的设置、持久化是如何进行,在下文权限的申请中讨论,当然也是在PackageManagerService中。
权限的申请:
动态权限的申请用requestPermissions来进行,从Activity作为起点:
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can reqeust only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
Intent的获取是通过PackageManager来获取的,用的是startActivityForResult方法来启动权限申请对话框,便于返回结果:
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;
}
intent的Action为ACTION_REQUEST_PERMISSIONS,隐式的调用GrantPermissionsActivity来进行授权。UI部分在GrantPermissionsViewHandler中处理,具体授权仍在GrantPermissionActivity中,具体授权代码如下:
@Override
public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
if (isObscuredTouch()) {
showOverlayDialog();
finish();
return;
}
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();
}
}
我们看到grantRuntimePermissions是AppPermissionGroup的方法,这是PackageInstaller中的一个类,在6.0中运行时权限也都是PackageInstaller这个系统应用在管理。从名字我们也可以看出,这个应用也负责应用的安装、卸载。
public boolean grantRuntimePermissions(boolean fixedByTheUser) {
final boolean isSharedUser = mPackageInfo.sharedUserId != null;
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 (mAppSupportsRuntimePermissions) {
// Do not touch permissions fixed by the system.
if (permission.isSystemFixed()) {
return false;
}
// Ensure the permission app op enabled before the permission grant.
if (permission.hasAppOp() && !permission.isAppOpAllowed()) {
permission.setAppOpAllowed(true);
mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
}
// 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);
}
}
} else {
// Legacy apps cannot have a not granted permission but just in case.
// Also if the permissions has no corresponding app op, then it is a
// third-party one and we do not offer toggling of such permissions.
if (!permission.isGranted() || !permission.hasAppOp()) {
continue;
}
if (!permission.isAppOpAllowed()) {
permission.setAppOpAllowed(true);
// It this is a shared user we want to enable the app op for all
// packages in the shared user to match the behavior of this
// shared user having a runtime permission.
if (isSharedUser) {
// Enable the app op.
String[] packageNames = mPackageManager.getPackagesForUid(uid);
for (String packageName : packageNames) {
mAppOps.setUidMode(permission.getAppOp(), uid,
AppOpsManager.MODE_ALLOWED);
}
} else {
// Enable the app op.
mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
}
// Mark that the permission should not be be granted on upgrade
// when the app begins supporting runtime permissions.
if (permission.shouldRevokeOnUpgrade()) {
permission.setRevokeOnUpgrade(false);
mPackageManager.updatePermissionFlags(permission.getName(),
mPackageInfo.packageName,
PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE,
0, mUserHandle);
}
// Legacy apps do not know that they have to retry access to a
// resource due to changes in runtime permissions (app ops in this
// case). Therefore, we restart them on app op change, so they
// can pick up the change.
mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
}
}
}
return true;
}
permissionsState.grantRuntimePermission
与checkUidPermission中的permissionsState.hasPermission相呼应。
mSettings.writeRuntimePermissionsForUserLPr(userId, false)进行持久化,将结果写入runtime-permissions.xml。
未完待续。。。
参考:
http://blog.csdn.net/lewif/article/details/49124757
http://mobile.51cto.com/android-526905.htm