2:PackageManagerService
Android既然基于linux,那我们能不能将c/c++代码交叉编译成可执行文件然后放到目标机器上跑呢?当然可以,不过前提你得有执行权限,事实上,android有一部分后台服务是纯linux程序(不需要davik虚拟机资源),比如service manager和media server等。
Android应用没有权限启动linux程序,同样的也无法主动从zygotefork出一个子进程来执行自身代码,那一个app安装后,如何拿到这个app的入口信息?代码文件(dex、so)以及相关资源释放目录的权限如何设置?APP运行时被准许的权限有哪些?这些都是PMS在扫描完一个app后需要确定的。
所以,扫描一个APK, 需要做的事情有:
1) 获取APP暴露的所有组件及相关数据
2) 获取APP声明和准许的权限数据
3) 生成app id,然后基于其生成的user id来作为app本地目录的访问权限控制
4) 释放代码文件,包含dex和so文件
5) 将1和2数据缓存到PMS中,供系统运行时使用。
2.1 PMS初始化流程
系统启动后,systemserver会调用如下代码初始化PMS
public static final PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); ServiceManager.addService("package", m); return m; } |
除了创建PackageManagerService并将其添加到service manager外,什么也没做,接着看PMS
构造函数:
1) 创建Settings对象,并通过addSharedUserLPw添加android预定义的shared user id
2) 初始化systemConfig,然后调用getPermissions获取系统预先配置的permission以及对应group数据,用来初始化settings.permissions列表
3) 通过调用systemConfig.getSharedLibraries,获取系统预先配置的共享库文件(.jar)并初始化mSharedLibraries
4) 通过mSettings.readLPw读取本地缓存文件(/data/system/package.xml)来初始化Settings,
那些数据需要缓存,我这边总结了下,主要有settings内部的permission和permission tree列表和package内部的grant permission列表,最后修改时间,rename过的包等等,总之,那些在运行期生成或会改变的数据,就需要缓存。
5) 遍历mSharedLibraries,对需要的library做代码优化
6) 调用scanDirLI扫描指定目录下的所有apk文件,主要是系统app目录/system/app和用户app目录/data/app
7) 调用updateAllSharedLibrariesLPw更新所有package的usesLibraryFiles数据,为什么要做更新?因为package在manifest里头使用use-library描述要引用的library,在系统mSharedLibraries里可能是未定义的,所以,需要做数据同步
8) 调用updatePermissionsLPw更新系统permission trees和permission 列表
2.2 初始化SystemConfig
PMS在构造时,需要初始化SystemConfig:
SystemConfig systemConfig = SystemConfig.getInstance(); |
接着看构造函数:
SystemConfig() { // Read configuration from system readPermissions(Environment.buildPath( Environment.getRootDirectory(), "etc", "sysconfig"), false); // Read configuration from the old permissions dir readPermissions(Environment.buildPath( Environment.getRootDirectory(), "etc", "permissions"), false); } |
代码主要调用readPermissions遍历/etc/sysconfig和/etc/permissions两个目录下是所有.xml文件,读取系统预置的配置信息,主要有:
1) permission 权限以及对应的group id
2) share libraries 系统预置了哪些共享库可供app加载使用
3) available features 系统支持哪些feature,比如是否支持前置摄像头等
还有,readPermissions第二个参数,如果为false,那么.xml文件内permission对应的gid会被添加到系统默认global gids并分配给启动的app,也就是说,app无须声明这个permission,默认就会在这个Supplementary GID对应分组中。
xml文件内对应的配置数据:
<permission name="android.permission.READ_EXTERNAL_STORAGE" > <group gid="sdcard_r" /> </permission>
<library name="android.test.runner" file="/system/framework/android.test.runner.jar" />
<feature name="android.hardware.camera.front" /> |
2.3 APP权限管理
Linux权限管理是基于UID和GID,可以对目标文件设置用户以及分组访问权限(Read,Write,Execute),Linux进程只有属于对应的用户或分组才能有权限访问该文件。
Android延续了Linux的权限管理方式,每个APK在经过PMS安装成功后,都会分配一个主UID和主GID,还有Supplementary GIDS则是根据system config的global gids + app所有 grant permission对应的gid组合而成。
除了linux的权限管理方式之外,Android还添加了permission机制来对android系统服务做访问控制,app如果要使用某项系统功能,必须要在manifest里声明该功能对应的permission并且该permission被准许后,app才能使用该系统功能。
2.3.1 生成UID和GID
APP所属的GID默认跟UID一致,Android UID的管理方式主要有两种:
1) Share User Id,也就是说,多个签名相同的app共享同一个user id,这样多个app就具有相同的权限,可以相互访问资源
2) Normal User Id,每个app被会分配一个独立的user id,这说明每个app都运行在独立的user下,无法直接共享进程内部数据,从而保护app内部数据的安全
单纯从user id分配来说,又可以分为系统预定义和动态生成,系统预定义的user id主要有Process.SYSTEM_UID(1000, 对应系统权限)和RADIO_UID(1001,对应通信相关应用权限)等等;动态生成的user id,在10000-19999之间依次分配,相关代码如下:
// Returns -1 if we could not find an available UserId to assign private int newUserIdLPw(Object obj) { // Let's be stupidly inefficient for now... final int N = mUserIds.size(); for (int i = mFirstAvailableUid; i < N; i++) { if (mUserIds.get(i) == null) { mUserIds.set(i, obj); return Process.FIRST_APPLICATION_UID + i; } }
// None left? if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) { return -1; }
mUserIds.add(obj); return Process.FIRST_APPLICATION_UID + N; } |
Process.FIRST_APPLICATION_UID值就是10000,mUserIds列表保存所有已经申请的package,package对应的user id就是其在mUserIds的索引加上10000;
这个函数先遍历mUserIds,看是否存在已经释放的user id,如果有,则将其重新分配给新申请的package,否则就将申请的packahe追加到在mUserIds。
接下去说说ShareUser Id,它对应的数据结构为:
/** * Settings data for a particular shared user ID we know about. */ final class SharedUserSetting extends GrantedPermissions { final String name; int userId; // flags that are associated with this uid, regardless of any package flags int uidFlags; final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>(); final PackageSignatures signatures = new PackageSignatures();
/**……..*/ } |
成员函数相关代码这里就不拷贝了,大家可以直接去看源码
从成员变量的定义可以看出,SharedUserSetting主要保存:
1) user id
2) packages 当前share user id的所有package信息
3) signatures share user id的app对应的签名数据
PMS将所有的SharedUserSetting数据保存到Settings. mSharedUsers,然后通过调用Settings. addSharedUserLPw添加新的SharedUserSetting:
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) { SharedUserSetting s = mSharedUsers.get(name); if (s != null) { if (s.userId == uid) { return s; } PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate shared user, keeping first: " + name); return null; } s = new SharedUserSetting(name, pkgFlags); s.userId = uid; if (addUserIdLPw(uid, s, name)) { mSharedUsers.put(name, s); return s; } return null; } |
上面介绍PMS初始化流程时介绍过,PMS初始化时会添加系统预定义的ShareUserSetting:
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); |
有一点要说明下,这里通过addSharedUserLPw添加ShareUserSetting,只设置了name,user id,flags;PackageSetting在scan package调用mSettings.getPackageLPw获取package settings时会被更新设置;
signatues数据,则会在scan package结束调用mSettings.insertPackageSettingLPw(pkgSetting, pkg)时被更新。
2.3.2 Supplementary GIDS
主UID和GID决定了app的访问权限,上面也提过,android还支持一套基于android framework的permission机制,这套机制确保app在调用某项android系统服务接口的时候,必须在manifest声明对应permission,然后permission被准许后app方可访问;
这里还有个问题,假如有些permission对应的系统服务接口,需要访问linux底层设备,那app对底层设备的访问权限如何获取?所以permission在被配置时,必须指定对应设备的访问权限信息,也就是SupplementaryGid,这样app启动时才会在对应的补充分组,从而拥有对linux设备的访问权。
比如app要访问外部存储,必须在manifest声明:
android.permission.READ_EXTERNAL_STORAGE
其对应的group id配置信息如下:
<permission name="android.permission.READ_EXTERNAL_STORAGE" > <group gid="sdcard_r" /> </permission> |
系统预置的permission配置如何数据获取,可查看 2.2 章节
2.3.3 权限数据管理
2.3.3.1权限定义
PMS会将系统支持的所有permission数据保存到Settings. mPermissions MAP中,对应的数据类型如下:
// Mapping from permission names to info about them. final ArrayMap<String, BasePermission> mPermissions = new ArrayMap<String, BasePermission>(); |
Key是permission名字,value是对应的permission数据,数据类型为BasePermission,先看下这个类核心字段的定义:
final class BasePermission { final static int TYPE_NORMAL = 0; final static int TYPE_BUILTIN = 1; final static int TYPE_DYNAMIC = 2; //权限名 final String name; //定义权限的包名,如果type为TYPE_BUILTIN,则为android String sourcePackage; //定义权限的包数据 PackageSettingBase packageSetting; //权限类型 final int type; //权限保护等级 int protectionLevel; //权限数据 PackageParser.Permission perm; PermissionInfo pendingInfo; //权限对应的user id,这个应该是当等级为signature或者permission 类型为tree时有效 int uid; //权限对应的Supplementary Gids int[] gids; } |
先看type和protectionLevel两个字段
type表示权限定义的类型,主要有三个:
1) TYPE_NORMAL,指的是正常app在manifest使用permission字段定义的权限
2) TYPE_BUILTIN,系统内置权限,PMS初始化时从SystemConfig读取
3) TYPE_DYNAMIC,动态添加的权限,一个app能动态添加权限的前提是,需要在manifest里定义permission tree,也就是权限树的根结点域名,然后app才能使用addPermission来动态添加权限。
protectionLevel表示权限的保护等级,默认分四级:
1) PROTECTION_NORMAL
普通权限,低风险
2) PROTECTION_DANGEROUS
有风险的权限
3) PROTECTION_SIGNATURE
申请使用权限的app签名必须跟权限所属app签名一致
4) PROTECTION_SIGNATURE_OR_SYSTEM
申请使用的必须是系统或者签名一致app
接着看perm字段,对应的类为PackageParser.Permission,这个类对应的是manifest里头的permission和permission tree,这两个权限的区别,可以看上头type类型的介绍
PackageParser.Permission类定义:
public final static class Permission extends Component<IntentInfo> { public final PermissionInfo info; public boolean tree; public PermissionGroup group; } |
tree字段用于判断是否是permission tree,group包含权限分组相关描述数据,info则是权限的描述数据
PermissionInfo类定义:
public class PermissionInfo extends PackageItemInfo implements Parcelable { ….. } |
主要包含permission name和package name等.
接着看Settings.mPermissions这个系统权限Map数据是怎么生成的
在PMS构造时,会调用SystemConfig.getPermissions()获取系统builtin权限数据
// Propagate permission configuration in to package manager. ArrayMap<String, SystemConfig.PermissionEntry> permConfig = systemConfig.getPermissions(); for (int i=0; i<permConfig.size(); i++) { SystemConfig.PermissionEntry perm = permConfig.valueAt(i); BasePermission bp = mSettings.mPermissions.get(perm.name); if (bp == null) { bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN); mSettings.mPermissions.put(perm.name, bp); } if (perm.gids != null) { bp.gids = appendInts(bp.gids, perm.gids); } } |
遍历systemConfig定义的permission时,接着判断mSettings.mPermissions是否已经存在该权限的定义,如果不存在,新建一个BasePermission,权限source package为android,type为builtin,接着更新权限对应的supplementary gids;
接着看扫描apk时权限数据的初始化:
PackageParser.parsePermission
private Permission parsePermission(Package owner, Resources res, XmlPullParser parser, AttributeSet attrs, String[] outError) throws XmlPullParserException, IOException { Permission perm = new Permission(owner);
TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestPermission);
if (!parsePackageItemInfo(owner, perm.info, outError, "<permission>", sa, com.android.internal.R.styleable.AndroidManifestPermission_name, com.android.internal.R.styleable.AndroidManifestPermission_label, com.android.internal.R.styleable.AndroidManifestPermission_icon, com.android.internal.R.styleable.AndroidManifestPermission_logo, com.android.internal.R.styleable.AndroidManifestPermission_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; }
/……/
owner.permissions.add(perm); return perm; } |
PackageParser.parsePermissionTree:
private Permission parsePermissionTree(Package owner, Resources res, XmlPullParser parser, AttributeSet attrs, String[] outError) throws XmlPullParserException, IOException { Permission perm = new Permission(owner);
TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestPermissionTree);
if (!parsePackageItemInfo(owner, perm.info, outError, "<permission-tree>", sa, com.android.internal.R.styleable.AndroidManifestPermissionTree_name, com.android.internal.R.styleable.AndroidManifestPermissionTree_label, com.android.internal.R.styleable.AndroidManifestPermissionTree_icon, com.android.internal.R.styleable.AndroidManifestPermissionTree_logo, com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; }
sa.recycle();
int index = perm.info.name.indexOf('.'); if (index > 0) { index = perm.info.name.indexOf('.', index+1); } if (index < 0) { outError[0] = "<permission-tree> name has less than three segments: " + perm.info.name; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; }
perm.info.descriptionRes = 0; perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL; perm.tree = true;
owner.permissions.add(perm);
return perm; } |
可以看出,permission和permission tree都保存到Package.permissions列表中,通过字段perm.tree是true还是false来区分是否是permissiontree
在app扫描结束后,permission数据已经被全部拿到,但是现在数据都还只是保存在Package内部,所以还需要将permission数据添加到PMS permission Map,代码在
PMS.scanPackageDirtyLI,这个函数太长了,所以这里只截取对permission列表处理相关代码段
N = pkg.permissions.size(); r = null; for (i=0; i<N; i++) { PackageParser.Permission p = pkg.permissions.get(i); ArrayMap<String, BasePermission> permissionMap = p.tree ? mSettings.mPermissionTrees : mSettings.mPermissions; p.group = mPermissionGroups.get(p.info.group); if (p.info.group == null || p.group != null) { BasePermission bp = permissionMap.get(p.info.name);
// Allow system apps to redefine non-system permissions if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) { final boolean currentOwnerIsSystem = (bp.perm != null && isSystemApp(bp.perm.owner)); if (isSystemApp(p.owner)) { if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) { // It's a built-in permission and no owner, take ownership now bp.packageSetting = pkgSetting; bp.perm = p; bp.uid = pkg.applicationInfo.uid; bp.sourcePackage = p.info.packageName; } else if (!currentOwnerIsSystem) { String msg = "New decl " + p.owner + " of permission " + p.info.name + " is system; overriding " + bp.sourcePackage; reportSettingsProblem(Log.WARN, msg); bp = null; } } }
if (bp == null) { bp = new BasePermission(p.info.name, p.info.packageName, BasePermission.TYPE_NORMAL); permissionMap.put(p.info.name, bp); }
if (bp.perm == null) { if (bp.sourcePackage == null || bp.sourcePackage.equals(p.info.packageName)) { BasePermission tree = findPermissionTreeLP(p.info.name); if (tree == null || tree.sourcePackage.equals(p.info.packageName)) { bp.packageSetting = pkgSetting; bp.perm = p; bp.uid = pkg.applicationInfo.uid; bp.sourcePackage = p.info.packageName; if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(p.info.name); } } else { Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: base tree " + tree.name + " is from package " + tree.sourcePackage); } } else { Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: original from " + bp.sourcePackage); } } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append("DUP:"); r.append(p.info.name); } if (bp.perm == p) { bp.protectionLevel = p.info.protectionLevel; } } else { Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: no group " + p.group); }
|
从代码可以看出,虽然permission和permission true在package内部是保存到一个list的,但是到了Settings,它们被分开了,分别对应mSettings.mPermissions和mSettings.mPermissionTrees,代码逻辑也比较简单,一开始先判断分组是否配置准确:
p.info.group == null || p.group != null
要么不设置权限分组,如果设置了,那就必须在mPermissionGroups能拿到对应数据,这两者都OK,接着根据权限名称从permissionMap拿到对应BasePermission对象,对第一次扫描apk来说,bp肯定为null,接着创建一个normal的BasePermission并添加到permissionMap,然后判断权限数据对象perm 是否为null,如果为null,才可对其进行更新,更新之前,还需要做两次判断:
1) sourcePackage未定义或者包名相同
2) 判断是否存在匹配的已定义permission tree根节点域名,如果不存在就没问题,反之如果存在,则必须保证permissiontree所属package跟permission是一致的,这也说明,针对app定义在manifest里的permission,可不定义permission tree根节点域名
上面两个条件都满足后,就可以更新bp. packageSetting和bp. sourcePackage等数据了,最后更新bp.protectionLevel权限等级.
在所有apk数据扫描结束后, 最后调用PMS.updatePermissionsLPw刷新PMS permission MAP:
private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, int flags) { // Make sure there are no dangling permission trees. Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); if (bp.packageSetting == null) { // We may not yet have parsed the package, so just see if // we still know about its settings. bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); } if (bp.packageSetting == null) { Slog.w(TAG, "Removing dangling permission tree: " + bp.name + " from package " + bp.sourcePackage); it.remove(); } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { Slog.i(TAG, "Removing old permission tree: " + bp.name + " from package " + bp.sourcePackage); flags |= UPDATE_PERMISSIONS_ALL; it.remove(); } } }
// Make sure all dynamic permissions have been assigned to a package, // and make sure there are no dangling permissions. it = mSettings.mPermissions.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); if (bp.type == BasePermission.TYPE_DYNAMIC) { if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" + bp.name + " pkg=" + bp.sourcePackage + " info=" + bp.pendingInfo); if (bp.packageSetting == null && bp.pendingInfo != null) { final BasePermission tree = findPermissionTreeLP(bp.name); if (tree != null && tree.perm != null) { bp.packageSetting = tree.packageSetting; bp.perm = new PackageParser.Permission(tree.perm.owner, new PermissionInfo(bp.pendingInfo)); bp.perm.info.packageName = tree.perm.info.packageName; bp.perm.info.name = bp.name; bp.uid = tree.uid; } } } if (bp.packageSetting == null) { // We may not yet have parsed the package, so just see if // we still know about its settings. bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); } if (bp.packageSetting == null) { Slog.w(TAG, "Removing dangling permission: " + bp.name + " from package " + bp.sourcePackage); it.remove(); } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { Slog.i(TAG, "Removing old permission: " + bp.name + " from package " + bp.sourcePackage); flags |= UPDATE_PERMISSIONS_ALL; it.remove(); } } }
// Now update the permissions for all packages, in particular // replace the granted permissions of the system packages. 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); } } |
刷新的目的有两个,一个是依次看看mSettings.mPermissionTrees和mSettings.mPermissions是否存在未设置owner package的,如果有,再根据sourcePackage name尝试从mPackages列表中获取对应的package,如果能拿到,更新之,否则说明这个BasePermission数据已经失效,将其删除.
最后尝试刷新所有app的grantpermission数据.
2.3.3.2权限使用
对大部分APP来说,如果要使用某权限,必须要在manifest进行声明, 比如:
<uses-permissionandroid:name="android.permission.INTERNET" />
然后PackageParser. parseBaseApk时,会调用parseUsesPermission对声明解析使用的权限数据:
private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser, AttributeSet attrs, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesPermission);
// Note: don't allow this value to be a reference to a resource // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesPermission_name); /* boolean required = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true); */ boolean required = true; // Optional <uses-permission> not supported
int maxSdkVersion = 0; TypedValue val = sa.peekValue( com.android.internal.R.styleable.AndroidManifestUsesPermission_maxSdkVersion); if (val != null) { if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) { maxSdkVersion = val.data; } }
sa.recycle();
if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) { if (name != null) { int index = pkg.requestedPermissions.indexOf(name); if (index == -1) { pkg.requestedPermissions.add(name.intern()); pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE); } else { if (pkg.requestedPermissionsRequired.get(index) != required) { outError[0] = "conflicting <uses-permission> entries"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } } } }
XmlUtils.skipCurrentTag(parser); return true; } |
app申请的所有权限都保存于pkg.requestedPermissions,请求使用的权限则会被保存到pkg.requestedPermissionsRequired中,required默认为true,说明所有申请的权限在默认情况下,都会被添加到请求使用列表。
权限请求了,接下去还去要对其进行准许(grant)操作,针对准许过后的权限,PMS定义了一个类GrantPermissions用于保存这些数据:
class GrantedPermissions { int pkgFlags; ArraySet<String> grantedPermissions = new ArraySet<String>(); int[] gids; } |
grantedPermissions保存app所有准许过的权限,gids之前说过,它保存有app使用准许权限所需要的Supplementary Gids。
PMS根据app user id的不同,准许过的权限保存位置也不同:
1) normal user id, 对应PackageSetting
2) share user id,对应PackageSetting. sharedUser
在扫描apk结束后,会调用:
void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, int flags) |
来更新系统权限数据,第二个参数pkgInfo用于指定要re-grant的包,第三个参数则是用于指定是否要re-grant所有app包。对于PMS初始化结束后,由于扫描了所有的包,所以需要re-grant所有的app包数据:
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL | (regrantPermissions ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL) : 0)); |
接着看函数代码:
private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, int flags) { // Make sure there are no dangling permission trees. Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); if (bp.packageSetting == null) { // We may not yet have parsed the package, so just see if // we still know about its settings. bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); } if (bp.packageSetting == null) { Slog.w(TAG, "Removing dangling permission tree: " + bp.name + " from package " + bp.sourcePackage); it.remove(); } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { Slog.i(TAG, "Removing old permission tree: " + bp.name + " from package " + bp.sourcePackage); flags |= UPDATE_PERMISSIONS_ALL; it.remove(); } } }
// Make sure all dynamic permissions have been assigned to a package, // and make sure there are no dangling permissions. it = mSettings.mPermissions.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); if (bp.type == BasePermission.TYPE_DYNAMIC) { if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" + bp.name + " pkg=" + bp.sourcePackage + " info=" + bp.pendingInfo); if (bp.packageSetting == null && bp.pendingInfo != null) { final BasePermission tree = findPermissionTreeLP(bp.name); if (tree != null && tree.perm != null) { bp.packageSetting = tree.packageSetting; bp.perm = new PackageParser.Permission(tree.perm.owner, new PermissionInfo(bp.pendingInfo)); bp.perm.info.packageName = tree.perm.info.packageName; bp.perm.info.name = bp.name; bp.uid = tree.uid; } } } if (bp.packageSetting == null) { // We may not yet have parsed the package, so just see if // we still know about its settings. bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); } if (bp.packageSetting == null) { Slog.w(TAG, "Removing dangling permission: " + bp.name + " from package " + bp.sourcePackage); it.remove(); } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { Slog.i(TAG, "Removing old permission: " + bp.name + " from package " + bp.sourcePackage); flags |= UPDATE_PERMISSIONS_ALL; it.remove(); } } }
// Now update the permissions for all packages, in particular // replace the granted permissions of the system packages. 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); } } |
这个函数主要做如下事情:
1) 同步mPermissionTrees和mPermissions数据,清除没有owner package的权限
2) 如果flags有设置UPDATE_PERMISSIONS_ALL,则依次对所有的package调用grantPermissionsLPw
3) 如果pkgInfo不为空,则需要对这个指定的package调用grantPermissionsLPw
grantPermissionsLPw就是针对package来进行具体的权限准许操作了:
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, String packageOfInterest) { final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { return; } final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; ArraySet<String> origPermissions = gp.grantedPermissions; boolean changedPermission = false;
if (replace) { ps.permissionsFixed = false; if (gp == ps) { origPermissions = new ArraySet<String>(gp.grantedPermissions); gp.grantedPermissions.clear(); gp.gids = mGlobalGids; } }
if (gp.gids == null) { gp.gids = mGlobalGids; }
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 (DEBUG_INSTALL) { if (gp != ps) { Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); } }
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; if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) { // Keep track of app op permissions. ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name); if (pkgs == null) { pkgs = new ArraySet<>(); mAppOpPermissionPackages.put(bp.name, pkgs); } pkgs.add(pkg.packageName); } final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; if (level == PermissionInfo.PROTECTION_NORMAL || level == PermissionInfo.PROTECTION_DANGEROUS) { // We grant a normal or dangerous permission if any of the following // are true: // 1) The permission is required // 2) The permission is optional, but was granted in the past // 3) The permission is optional, but was requested by an // app in /system (not /data) // // Otherwise, reject the permission. allowed = (required || origPermissions.contains(perm) || (isSystemApp(ps) && !isUpdatedSystemApp(ps))); } else if (bp.packageSetting == null) { // This permission is invalid; skip it. allowed = false; } else if (level == PermissionInfo.PROTECTION_SIGNATURE) { allowed = grantSignaturePermission(perm, pkg, bp, origPermissions); if (allowed) { allowedSig = true; } } else { allowed = false; } if (DEBUG_INSTALL) { if (gp != ps) { Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); } } if (allowed) { if (!isSystemApp(ps) && ps.permissionsFixed) { // If this is an existing, non-system package, then // we can't add any new permissions to it. if (!allowedSig && !gp.grantedPermissions.contains(perm)) { // Except... if this is a permission that was added // to the platform (note: need to only do this when // updating the platform). allowed = isNewPlatformPermissionForPackage(perm, pkg); } } if (allowed) { if (!gp.grantedPermissions.contains(perm)) { changedPermission = true; gp.grantedPermissions.add(perm); gp.gids = appendInts(gp.gids, bp.gids); } else if (!ps.haveGids) { gp.gids = appendInts(gp.gids, bp.gids); } } else { if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) { Slog.w(TAG, "Not granting permission " + perm + " to package " + pkg.packageName + " because it was previously installed without"); } } } else { if (gp.grantedPermissions.remove(perm)) { changedPermission = true; gp.gids = removeInts(gp.gids, bp.gids); Slog.i(TAG, "Un-granting permission " + perm + " from package " + pkg.packageName + " (protectionLevel=" + bp.protectionLevel + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + ")"); } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) { // Don't print warning for app op permissions, since it is fine for them // not to be granted, there is a UI for the user to decide. if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) { Slog.w(TAG, "Not granting permission " + perm + " to package " + pkg.packageName + " (protectionLevel=" + bp.protectionLevel + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + ")"); } } } }
if ((changedPermission || replace) && !ps.permissionsFixed && !isSystemApp(ps) || isUpdatedSystemApp(ps)){ // This is the first that we have heard about this package, so the // permissions we have now selected are fixed until explicitly // changed. ps.permissionsFixed = true; } ps.haveGids = true; } |
先拿到package对应的PackageSetting对象实例,然后根据是否是share user id来拿到对应的GrantPermission对象,接着遍历package 申请的所有权限,如果权限保护等级是normal或dangerous的,只要权限被请求准许或者app是系统应用,那么准许通过;对于保护等级是signature的,则需要调用grantSignaturePermission来核对签名是否一致,如果一致,准许通过,最后调用gp.grantedPermissions.add(perm);保存准许通过的权限,同时调用
gp.gids= appendInts(gp.gids, bp.gids);保存BasePermission对应的supplementary gids.
至此,app对应的静态权限数据已经全部生成。
2.3.3.3动态权限
上面说过,权限分三种,BUILTIN(系统预置),NORMAL(manifest定义)和DYNAMIC(动态添加),如果一个app要动态添加和准许权限,需要具备:
1) 在manifest定义permission tree
2) 具有"android.permission.GRANT_REVOKE_PERMISSIONS"权限,也就是准许和撤销权限的权限。
这个权限定义在framework-res.apk对应的manifest中:
<permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS" android:label="@string/permlab_grantRevokePermissions" android:description="@string/permdesc_grantRevokePermissions" android:protectionLevel="signature" /> |
其保护等级是signature的,也就是说,用这个权限的app,必须要有系统权限,对于普通app来说,你可以定义permission tree并且动态添加permission,但是你没有权限去准许和撤销这个权限,添加和准许权限对应的函数为PMS.addPermission和PMS.grantPermission。
先看addPermission,其最终将会调用addPermissionLocked:
boolean addPermissionLocked(PermissionInfo info, boolean async) { if (info.labelRes == 0 && info.nonLocalizedLabel == null) { throw new SecurityException("Label must be specified in permission"); } BasePermission tree = checkPermissionTreeLP(info.name); BasePermission bp = mSettings.mPermissions.get(info.name); boolean added = bp == null; boolean changed = true; int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); if (added) { enforcePermissionCapLocked(info, tree); bp = new BasePermission(info.name, tree.sourcePackage, BasePermission.TYPE_DYNAMIC); } else if (bp.type != BasePermission.TYPE_DYNAMIC) { throw new SecurityException( "Not allowed to modify non-dynamic permission " + info.name); } else { if (bp.protectionLevel == fixedLevel && bp.perm.owner.equals(tree.perm.owner) && bp.uid == tree.uid && comparePermissionInfos(bp.perm.info, info)) { changed = false; } } bp.protectionLevel = fixedLevel; info = new PermissionInfo(info); info.protectionLevel = fixedLevel; bp.perm = new PackageParser.Permission(tree.perm.owner, info); bp.perm.info.packageName = tree.perm.info.packageName; bp.uid = tree.uid; if (added) { mSettings.mPermissions.put(info.name, bp); } if (changed) { if (!async) { mSettings.writeLPr(); } else { scheduleWriteSettingsLocked(); } } return added; } |
这个函数首先调用checkPermissionTreeLP并传入权限名来判断调用app是否声明了对应的权限树根节点:
private BasePermission checkPermissionTreeLP(String permName) { if (permName != null) { BasePermission bp = findPermissionTreeLP(permName); if (bp != null) { if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) { return bp; } throw new SecurityException("Calling uid " + Binder.getCallingUid() + " is not allowed to add to permission tree " + bp.name + " owned by uid " + bp.uid); } } throw new SecurityException("No permission tree found for " + permName); } |
先通过findPermissionTreeLP从mSettings.mPermissionTrees找到对应的权限树描述对象,如果没找到,抛出异常,如果找到了,接着通过UID来判断调用应用和权限树定义app是否一致,如果不一致,抛出异常。
checkPermissionTreeLP通过后,接着看对应的权限是否已经定义,如果未定义,就继续创建DYNAMIC类型的权限并添加到权限列表,如果已经定义了,接着判断已经存在的权限类型是否为DYNAMIC,如果不是,抛出异常,如果是,则更新权限数据。
接着看grantPermission:
@Override public void grantPermission(String packageName, String permissionName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null); synchronized (mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } final BasePermission bp = mSettings.mPermissions.get(permissionName); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permissionName); }
checkGrantRevokePermissions(pkg, bp);
final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { return; } final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps; if (gp.grantedPermissions.add(permissionName)) { if (ps.haveGids) { gp.gids = appendInts(gp.gids, bp.gids); } mSettings.writeLPr(); } } } |
从代码可以看出,要想成功grant,必须满足:
1) packageName对应的package要存在
2) permissionName对应的权限要已经添加到系统权限列表
3) packageName对应的package要声明request这个权限
4) 只有权限保护等级为normal或dangerous,并且在对应的package内还未被required的权限允许grant,这个基本也就DYNAMIC permission能满足了
以上四点必须全部满足,否则就会抛出异常
接着将permission name添加到grantedPermissions,然后append对应的supplementary GID。
2.3.3.4权限查询
App在调用某些系统函数的时候,函数的开头会检查app是否拥有对应的权限,比如:
mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null); |
这个调用绕一大圈,最终还是通过调用PMS.checkPermission来判断调用app是否拥有对应的权限
@Override public int checkPermission(String permName, String pkgName) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(pkgName); if (p != null && p.mExtras != null) { PackageSetting ps = (PackageSetting)p.mExtras; if (ps.sharedUser != null) { if (ps.sharedUser.grantedPermissions.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } } else if (ps.grantedPermissions.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } } } return PackageManager.PERMISSION_DENIED; } |
代码很简单,先查找pkgName对应的Package,接着拿到PackageSetting对象,然后从grantedPermissions中查找是否包含已经对应的permission来确认该权限是否被grant。