Android系统权限是建立在框架层上的一套权限解析分配和鉴权流程,其主要数据结构和校验流程主要在pms(包管理服务)中实现。
简单理解系统权限机制主要分为权限解析、权限分配、鉴权这三个主要内容。
Sdk版本大于等于23后,新增了动态权限管理,让Android系统权限管理更加灵活和自主。
下面来分析下Android 5.1上面的权限管理和鉴权过程。
权限的数据结构
涉及到系统权限的数据结构如上图所示,分别来分析每个数据结构的内容和作用。
Ø BasePermission
系统权限的基本表示单元是BasePermission,Settings中维护了一个总的权限映射表mPermissions,所有的权限都会添加到mPermissions列表中,其中key是权限的名字,value是具体的BasePermission实例。
[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]
// Mapping from permission names to info about them.
final ArrayMap<String, BasePermission> mPermissions = new ArrayMap<String, BasePermission>();
我们都知道绝大部分权限已经在系统中定义好,如READ_EXTERNAL_STORAGE读写外部存储权限、READ_PHONE_STATE读取手机状态信息权限等,这些权限的定义都在framework-res.apk中,在扫描framework-res.apk过程中解析并添加到mPermissions映射表中。系统权限的定义:
[/frameworks/base/core/res/AndroidManifest.xml]
系统中绝大部分的权限定义都在frameworks/base/services/core/res/AndroidManifest.xml中,最终会打包进framework-res.apk中并经过解析保存到Settings的mPermissions映射表中。
Ø PackageParser.Permission
PackageParser.Permission在上面分析PackageParser解析apk过程中有提及过,解析apk的AndroidManifest.xml文件中的标签后得到的权限表示。
PackageParser.Permission中包含一个对应的PermissionInfo。
Ø PermissionInfo
权限信息的表示,其中包含权限等级的定义(NORMAL, DANGER, SIGNERATURE),另外实现了序列化,用户于进程间通信。每个BasePermission实例中包含一个PermissionInfo的实例。
以上三个类结构是系统权限部分的定义,下面四个类结构和pkg强相关,表示pkg的权限状态。
Ø GrantedPermissions
表示一个pkg已经赋予的权限,类里面定义了一个字符串列表grantedPermissions保存pkg已经被赋予的所有权限。
[/frameworks/base/services/core/java/com/android/server/pm/GrantedPermissions.java]
class GrantedPermissions {
int pkgFlags;
ArraySet<String> grantedPermissions = new ArraySet<String>();
Ø PackageSettingBase
PackageSettingBase继承了GrantedPermissions类并添加更多和 pkg相关的信息,是一个pkg信息的基本表示类。PackageSettingBase保存为了如pkg的codePath, resourcePath, signature等信息,同时PackageSettingBase是GrantedPermissions的子类,因为也包含了pkg被赋予的权限列表。
Ø PackageSetting
PackageSetting继承了PackageSettingBase类,并新增如PackageParser.Package和SharedUserSetting。那么以后只要获得一个pkg的PackageSetting实例,就可以获得对应的SharedUserSetting实例,通过这个SharedUserSetting实例来获得与其sharedUser的其他pkg信息。同时PackageSetting是GrantedPermissions的子类,那么也就意味着只要拿到pkg的PackageSetting实例就可以知道pkg已经被赋予了哪些权限。
Ø SharedUserSetting
SharedUserSetting继承了GrantedPermissions,类里面保存了一个packages的列表,表示这个列表中的所有应用共享该uid。同时共享这个uid所赋予的所有权限。
权限管理
我们知道在pms构造方法中扫描apk后,创建对应的PackageSetting实例并赋予给pkg的mExtras,以后我们就可以通过pkg.mExtras获得相应的PackageSetting实例。一开始扫描过程中,PackageSetting实例中保存的grantedPermissions列表为空,权限的赋予会在pms构造方法的结束部分,通过调用updatePermissionsLPw方法遍历所有已经安装的pkg,一个一个的根据其请求的权限来确定是否赋予。
首先来看看updatePermissionsLPw方法:
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
private void updatePermissionsLPw(String changingPkg,PackageParser.Package pkgInfo, int flags) {
// Make sure there are no dangling permission trees.
// 更新permission trees列表,去除无效的permission tree
// Make sure all dynamic permissions have been assigned to a package,
// and make sure there are no dangling permissions.
// 更新permission列表,去除无效无主的permission定义
if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
changingPkg);
}
}
}
if (pkgInfo != null) {
grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
}
}
updatePermissionsLPw方法中主要做两个事情,检查无效的permissiontree和permission定义,另外一个就是根据传入的flags是否带有UPDATE_PERMISSIONS_ALL来遍历mPackages包列表来更新所有安装包的权限信息。重点分析grantPermissionsLPw方法。
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, String packageOfInterest) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
ArraySet<String> origPermissions = gp.grantedPermissions;
boolean changedPermission = false;
/* 获得解析pkg中请求的权限数量,循环一个一个去决定是否分配给该应用或者sharedUserId */
final int N = pkg.requestedPermissions.size();
for (int i=0; i<N; i++) {
final String name = pkg.requestedPermissions.get(i);
final boolean required = pkg.requestedPermissionsRequired.get(i);
final BasePermission bp = mSettings.mPermissions.get(name);
/* 检查应用申请的这个权限是否存在有效,如果无效,忽略 */
if (bp == null || bp.packageSetting == null) {
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
Slog.w(TAG, "Unknown permission " + name + " in package " + pkg.packageName);
}
continue;
}
final String perm = bp.name;
boolean allowed;
boolean allowedSig = false;
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
if (level == PermissionInfo.PROTECTION_NORMAL || level == PermissionInfo.PROTECTION_DANGEROUS) {
/* 权限等级为NORMAL和DANGER的权限,只要申请就分配 */
allowed = (required || origPermissions.contains(perm)
|| (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
} else if (bp.packageSetting == null) {
/* 无头的权限,直接拒绝 */
allowed = false;
} else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
/* 权限等级要求签名的,检查该pkg签名是否和platform apk签名一致,一致则返回 */
allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
if (allowed) { allowedSig = true; }
} else {
allowed = false;
}
if (allowed) {
if (allowed) {
if (!gp.grantedPermissions.contains(perm)) {
/* 权限允许赋予后,如果pkg已获得权限列表中没有,则添加 */
changedPermission = true;
gp.grantedPermissions.add(perm);
gp.gids = appendInts(gp.gids, bp.gids);
}
}
} else {
/* 权限不允许 */
if (gp.grantedPermissions.remove(perm)) {
/* 如果已获取权限列表中存在去除的该权限,说明apk版本有变化 */
changedPermission = true;
gp.gids = removeInts(gp.gids, bp.gids);
}
}
}
}
grantPermissionsLPw方法中实现了5.1权限赋予的流程。主要流程首先获得pkg的PackageSettings实例,上面我们知道pkg的PackageSettings实例中包含了应用已经获得的权限列表grantedPermissions。第一次开机扫描应用的过程中,这个列表为空。通过遍历包解析后得到的requestPermissions列表的权限的等级来判断是否授权,如果请求的权限授权成功后,就会保存进入grantedPermissions列表中。
grantedPermissions列表会根据应用的更新而更新。
权限管理的基本流程如上。
鉴权过程
一般客户端调用checkPermission方法鉴权的流程如下:
①Context.java--->public int checkPermission(String permission, int pid, int uid)
②ActivityManagerService.java--->public int checkPermission(String permission, int pid, int uid)
③ActivityManagerService.java---> checkComponentPermission
④ActivityManager.java--->checkComponentPermission
Root用户(uid=0)和System用户(uid=1000)直接鉴权通过。
⑤PackageManagerService.java--->checkUidPermission
查看pms的checkUidPermission方法:
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
@Override
public int checkUidPermission(String permName, int uid) {
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 {
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms != null && perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
return PackageManager.PERMISSION_DENIED;
}
发现checkUidPermission方法中调用Settings的getUserIdLPr获得一个Object实例,这个Object实例就是对应uid的pkgSettings实例,里面包含grantedPermissions信息。最后查询grantedPermissions列表中是否包含所要检查的权限即可判断该uid是否具有该权限。
继续分析Settings的getUserIdLPr方法:
[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]
public Object getUserIdLPr(int uid) {
if (uid >= Process.FIRST_APPLICATION_UID) {
final int N = mUserIds.size();
final int index = uid - Process.FIRST_APPLICATION_UID;
return index < N ? mUserIds.get(index) : null;
} else {
return mOtherUserIds.get(uid);
}
}
这个方法首先判断检查的uid是否是应用的uid(10000以上),如果是,通过uid来查询mUserIds表来获得对应uid的pkgSettings实例(mUserIds就是uid和pkgSettings的映射表)。通过进一步分析,mUserIds列表是在手机开机后读取packages.xml缓存生成的。
鉴权流程2:
① 根据PMS解析的申请权限数据,对应用添加gid
② 启动应用进程时,指定所有对应的gid
举例:android.permission.WRITE_EXTERNAL_STORAGE
① 应用在AndroidManifest.xml中申请android.permission.WRITE_EXTERNAL_STORAGE
② 安装后,PMS解析该应用,并为该应用添加sdcard_rw的组ID。
③ 启动该应用进程时,使用上面解析的gid。