一.SystemConfig的整机权限信息
Android在SystemConfig的构造函数中会通过读取相关的文件来加载整机的权限信息。这些文件是{partition}/etc/permissions下面的文件。其中{partition}指代的分区包含了/system,/vendor,/odm,/oem/,/product/,/system_ext等目录。{partition}/etc/permissions目录下的文件以xml的形式存在以方便解析。
frameworks/base/core/java/com/android/server/SystemConfig.java
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
// Vendors are only allowed to customize these
int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
| ALLOW_ASSOCIATIONS;
if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
// For backward compatibility
vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
}
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
// Allow ODM to customize system configs as much as Vendor, because /odm is another
// vendor partition other than /vendor.
int odmPermissionFlag = vendorPermissionFlag;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
String skuProperty = SystemProperties.get(SKU_PROPERTY, "");
if (!skuProperty.isEmpty()) {
String skuDir = "sku_" + skuProperty;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions", skuDir),
odmPermissionFlag);
}
// Allow OEM to customize these
int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);
// Allow Product to customize all system configs
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "sysconfig"), ALLOW_ALL);
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL);
// Allow /system_ext to customize all system configs
readPermissions(Environment.buildPath(
Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL);
readPermissions(Environment.buildPath(
Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL);
// Skip loading configuration from apex if it is not a system process.
if (!isSystemProcess()) {
return;
}
// Read configuration of libs from apex module.
// TODO(146407631): Use a solid way to filter apex module folders?
for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) {
if (f.isFile() || f.getPath().contains("@")) {
continue;
}
readPermissions(Environment.buildPath(f, "etc", "permissions"), ALLOW_LIBS);
}
}
解析的规则如下。选取一些常用的进行说明:
1.permission标签:定义了一个权限,name为权限的名称,子标签gid表示权限所属的gid,一个权限可以同时属于几个不同的gid;
2.assign-permission标签:授予某个uid某个特定权限;
3.split-permission标签:当系统授予某个应用某个split permission时,如果应用的targetsdk低于split-permission的targetsdk(没有指定的话targetsdk为10001)会同时授予new-permission子标签对应的权限;
4.feature标签:定义系统的特性(feature);
5.unavailable-feature标签:定义了需要移除的系统特性(feature);
6.system-user-whitelisted-app标签:主用户下强制启用的system app;
7.system-user-blacklisted-app标签:主用户下强制不启用的system app;
8.library标签:定义了应用的共享jar包;
9.privapp-permissions标签:为特权应用授予或拒绝权限。如果特权应用需要使用到级别signature|privileged的系统权限,必须显式地在在permission子标签下声明授予权限或者在deny-permission字标签下声明不授予权限,两者必选其一,不然会造成开不了机。参考特许权限白名单;
frameworks/base/core/java/com/android/server/SystemConfig.java
private void readPermissionsFromXml(File permFile, int permissionFlag) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
} catch (FileNotFoundException e) {
Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
return;
}
final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
int type;
while ((type=parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}
if (type != parser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
throw new XmlPullParserException("Unexpected start tag in " + permFile
+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");
}
final boolean allowAll = permissionFlag == ALLOW_ALL;
final boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
final boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
final boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
final boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
final boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS)
!= 0;
final boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
final boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING)
!= 0;
final boolean allowAssociations = (permissionFlag & ALLOW_ASSOCIATIONS) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}
String name = parser.getName();
if (name == null) {
XmlUtils.skipCurrentTag(parser);
continue;
}
switch (name) {
case "group": {
if (allowAll) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = android.os.Process.getGidForName(gidStr);
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
Slog.w(TAG, "<" + name + "> without gid in " + permFile + " at "
+ parser.getPositionDescription());
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "permission": {
if (allowPermissions) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
perm = perm.intern();
readPermission(parser, perm);
} else {
logNotAllowedInPartition(name, permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
} break;
case "assign-permission": {
if (allowPermissions) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile
+ " at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<" + name + "> without uid in " + permFile
+ " at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<" + name + "> with unknown uid \""
+ uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
perm = perm.intern();
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new ArraySet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "split-permission": {
if (allowPermissions) {
readSplitPermission(parser, permFile);
} else {
logNotAllowedInPartition(name, permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
} break;
case "library": {
if (allowLibs) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
String ldependency = parser.getAttributeValue(null, "dependency");
if (lname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
ldependency == null ? new String[0] : ldependency.split(":"));
mSharedLibraries.put(lname, entry);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "feature": {
if (allowFeatures) {
String fname = parser.getAttributeValue(null, "name");
int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
boolean allowed;
if (!lowRam) {
allowed = true;
} else {
String notLowRam = parser.getAttributeValue(null, "notLowRam");
allowed = !"true".equals(notLowRam);
}
if (fname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (allowed) {
addFeature(fname, fversion);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "unavailable-feature": {
if (allowFeatures) {
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mUnavailableFeatures.add(fname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "allow-in-power-save-except-idle": {
if (allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInPowerSaveExceptIdle.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "allow-in-power-save": {
if (allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInPowerSave.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "allow-in-data-usage-save": {
if (allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInDataUsageSave.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "allow-unthrottled-location": {
if (allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowUnthrottledLocation.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "allow-ignore-location-settings": {
if (allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowIgnoreLocationSettings.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "allow-implicit-broadcast": {
if (allowAll) {
String action = parser.getAttributeValue(null, "action");
if (action == null) {
Slog.w(TAG, "<" + name + "> without action in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowImplicitBroadcasts.add(action);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "app-link": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mLinkedApps.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "system-user-whitelisted-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mSystemUserWhitelistedApps.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "system-user-blacklisted-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mSystemUserBlacklistedApps.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "default-enabled-vr-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
String clsname = parser.getAttributeValue(null, "class");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else if (clsname == null) {
Slog.w(TAG, "<" + name + "> without class in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "backup-transport-whitelisted-service": {
if (allowFeatures) {
String serviceName = parser.getAttributeValue(null, "service");
if (serviceName == null) {
Slog.w(TAG, "<" + name + "> without service in "
+ permFile + " at " + parser.getPositionDescription());
} else {
ComponentName cn = ComponentName.unflattenFromString(serviceName);
if (cn == null) {
Slog.w(TAG, "<" + name + "> with invalid service name "
+ serviceName + " in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mBackupTransportWhitelist.add(cn);
}
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "disabled-until-used-preinstalled-carrier-associated-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
String carrierPkgname = parser.getAttributeValue(null,
"carrierAppPackage");
if (pkgname == null || carrierPkgname == null) {
Slog.w(TAG, "<" + name
+ "> without package or carrierAppPackage in " + permFile
+ " at " + parser.getPositionDescription());
} else {
List<String> associatedPkgs =
mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
carrierPkgname);
if (associatedPkgs == null) {
associatedPkgs = new ArrayList<>();
mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
carrierPkgname, associatedPkgs);
}
associatedPkgs.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "disabled-until-used-preinstalled-carrier-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG,
"<" + name + "> without "
+ "package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "privapp-permissions": {
if (allowPrivappPermissions) {
// privapp permissions from system, vendor, product and system_ext
// partitions are stored separately. This is to prevent xml files in
// the vendor partition from granting permissions to priv apps in the
// system partition and vice versa.
boolean vendor = permFile.toPath().startsWith(
Environment.getVendorDirectory().toPath() + "/")
|| permFile.toPath().startsWith(
Environment.getOdmDirectory().toPath() + "/");
boolean product = permFile.toPath().startsWith(
Environment.getProductDirectory().toPath() + "/");
boolean systemExt = permFile.toPath().startsWith(
Environment.getSystemExtDirectory().toPath() + "/");
if (vendor) {
readPrivAppPermissions(parser, mVendorPrivAppPermissions,
mVendorPrivAppDenyPermissions);
} else if (product) {
readPrivAppPermissions(parser, mProductPrivAppPermissions,
mProductPrivAppDenyPermissions);
} else if (systemExt) {
readPrivAppPermissions(parser, mSystemExtPrivAppPermissions,
mSystemExtPrivAppDenyPermissions);
} else {
readPrivAppPermissions(parser, mPrivAppPermissions,
mPrivAppDenyPermissions);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
} break;
case "oem-permissions": {
if (allowOemPermissions) {
readOemPermissions(parser);
} else {
logNotAllowedInPartition(name, permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
} break;
case "hidden-api-whitelisted-app": {
if (allowApiWhitelisting) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mHiddenApiPackageWhitelist.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "allow-association": {
if (allowAssociations) {
String target = parser.getAttributeValue(null, "target");
if (target == null) {
Slog.w(TAG, "<" + name + "> without target in " + permFile
+ " at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
String allowed = parser.getAttributeValue(null, "allowed");
if (allowed == null) {
Slog.w(TAG, "<" + name + "> without allowed in " + permFile
+ " at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
target = target.intern();
allowed = allowed.intern();
ArraySet<String> associations = mAllowedAssociations.get(target);
if (associations == null) {
associations = new ArraySet<>();
mAllowedAssociations.put(target, associations);
}
Slog.i(TAG, "Adding association: " + target + " <- " + allowed);
associations.add(allowed);
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
case "bugreport-whitelisted": {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mBugreportWhitelistedPackages.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} break;
default: {
Slog.w(TAG, "Tag " + name + " is unknown in "
+ permFile + " at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
} break;
}
}
} catch (XmlPullParserException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} catch (IOException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} finally {
IoUtils.closeQuietly(permReader);
}
// Some devices can be field-converted to FBE, so offer to splice in
// those features if not already defined by the static config
if (StorageManager.isFileEncryptedNativeOnly()) {
addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);
addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
}
// Help legacy devices that may not have updated their static config
if (StorageManager.hasAdoptable()) {
addFeature(PackageManager.FEATURE_ADOPTABLE_STORAGE, 0);
}
if (ActivityManager.isLowRamDeviceStatic()) {
addFeature(PackageManager.FEATURE_RAM_LOW, 0);
} else {
addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
}
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
}
Settings#readLPw和Settings#writeLPr
在开机阶段,PMS扫描应用之前,PMS会调用Settings#readLPw来初始化权限信息。如果是首次开机,这个步骤会被跳过;如果不是首次开机,Settings#readLPw将会读取系统的/data/system/packages.xml和data/system/users/{userid}/runtime-permissions.xml来获取上次关机时刻保存的安装权限信息和运行时权限信息。
frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
...
packages.xml和权限相关的节点主要有permissions节点,permission-trees节点还有package节点下的perms子节点。
permissions节点形式如下所示。每一个item代表了一个定义的权限。name是权限的名称,package是定义这个权限的包名,protection是这个权限的安全级别。
<permissions>
<item name="com.google.android.gms.auth.api.phone.permission.SEND" package="com.google.android.gms" protection="2" />
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
<item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />
...
</permissions>
permission-trees节点形式如下所示。每一个item代表了一个定义的权限树。name是权限树的名称,package是定义这个权限树的包名。
读取permissions节点的结果是更新Settings–>mPermissions(PermissionSettings)–>mPermissions(ArrayMap<String, BasePermission>)数据结构。读取permission-trees节点的结果是更新Settings–>mPermissions(PermissionSettings)–>mPermissionTrees(ArrayMap<String, BasePermission>)数据结构。
<permission-trees>
<item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
</permission-trees>
package节点下的perms子节点如下。每一个Item表示和应用相关的安装权限(就是除了运行时权限以外的权限,运行时权限信息在 /data/system/users/{userid}/runtime-permissions.xml体现)信息。granted表示授权状态。flags表示这个权限的flag。
<package name="com.google.android.gms" codePath="/system/product/priv-app/GmsCore" nativeLibraryPath="/system/product/priv-app/GmsCore/lib" primaryCpuAbi="arm64-v8a" secondaryCpuAbi="armeabi-v7a" publicFlags="-1605714363" privateFlags="524296" ft="17290fd31e0" it="17290fd31e0" ut="17290fd31e0" version="201516037" sharedUserId="10153" isOrphaned="true">
<sigs count="1" schemeVersion="3">
<cert index="8" />
</sigs>
<perms>
<item name="com.google.android.gms.auth.api.phone.permission.SEND" granted="true" flags="0" />
<item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
<item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
<item name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" granted="true" flags="0" />
<item name="android.permission.INTENT_FILTER_VERIFICATION_AGENT" granted="true" flags="0" />
<item name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" granted="true" flags="0" />
读取package节点的结果是更新Settings–>mPackages(ArrayMap<String, PackageSetting>),插入应用包名为键,记录包信息的PackageSetting为值的记录。
frameworks/base/services/core/java/com/android/server/pm/Settings.java
private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
...
} else if (userId > 0) {//独立UID应用,构建一个PackageSetting后记录到Settings-->mPackages
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
pkgPrivateFlags, parentPackageName, null /*childPackageNames*/,
null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
...
} else if (sharedIdStr != null) {
if (sharedUserId > 0) {//shareduserid应用,构建一个PackageSetting在解析packages.xml完成后记录到Settings-->mPackages,因为可能存在多个应用共用shareduserid,所以必须在解析packages.xml完成后才记录到Settings-->mPackages,方便在对应的SharedUserSetting记录所有共用这个shareduserid的应用
packageSetting = new PackageSetting(name.intern(), realName, new File(
codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
null /*childPackageNames*/, sharedUserId,
null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
mPendingPackages.add(packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name
+ ": sharedUserId=" + sharedUserId + " pkg=" + packageSetting);
...
} else if (tagName.equals(TAG_PERMISSIONS)) {//解析package的权限信息
readInstallPermissionsLPr(parser,
packageSetting.getPermissionsState());
packageSetting.installPermissionsFixed = true;
...
}
前面提过packages.xml中package标签下的perms标签的每一项权限都是安装权限,根据其granted的值来预先设置权限状态。granted为true,使用grantInstallPermission授予安装权限并设置flags标签表示的flag;granted为false,使用revokeInstallPermission移除安装权限设置flags标签表示的flag。
frameworks/base/services/core/java/com/android/server/pm/Settings.java
void readInstallPermissionsLPr(XmlPullParser parser,
PermissionsState permissionsState) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals(TAG_ITEM)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
BasePermission bp = mPermissions.getPermission(name);
if (bp == null) {
Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
final boolean granted = grantedStr == null
|| Boolean.parseBoolean(grantedStr);
String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
if (granted) {
if (permissionsState.grantInstallPermission(bp) ==
PermissionsState.PERMISSION_OPERATION_FAILURE) {
Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
XmlUtils.skipCurrentTag(parser);
} else {
permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
}
} else {
if (permissionsState.revokeInstallPermission(bp) ==
PermissionsState.PERMISSION_OPERATION_FAILURE) {
Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
XmlUtils.skipCurrentTag(parser);
} else {
permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
}
}
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
}
读取运行时权限信息代码入口:
frameworks/base/services/core/java/com/android/server/pm/Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
...
for (UserInfo user : users) {
mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
}
...
data/system/users/{userid}/runtime-permissions.xml部分内容如下。安装权限是对用所有用户都生效的,运行时权限时在不同用户下可能有不同的状态。
pkg节点下item表示每一项和应用相关的运行时权限。name是运行时权限名字。granted表示授予状态。flags表示该运行时权限的flag。
shared-user 节点下item表示该shareuserid下的通用运行时权限状态。name是运行时权限名字。granted表示授予状态。flags表示该运行时权限的flag。
<runtime-permissions version="8" fingerprint="OPPO/CPH1871/CPH1871:10/QKQ1.191008.001/1590481568:user/release-keys">
<pkg name="com.coloros.backuprestore">
<item name="android.permission.ACCESS_FINE_LOCATION" granted="false" flags="300" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="false" flags="2380" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="false" flags="300" />
<item name="android.permission.READ_PHONE_STATE" granted="false" flags="300" />
<item name="android.permission.WRITE_CONTACTS" granted="false" flags="300" />
<item name="android.permission.CAMERA" granted="false" flags="300" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="false" flags="2300" />
<item name="android.permission.ACCESS_BACKGROUND_LOCATION" granted="false" flags="2300" />
</pkg>
...
<shared-user name="android.uid.phone">
<item name="android.permission.READ_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="1030" />
<item name="android.permission.RECEIVE_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="1030" />
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="1030" />
<item name="android.permission.SEND_SMS" granted="true" flags="3030" />
<item name="android.permission.CALL_PHONE" granted="true" flags="30" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="1030" />
<item name="android.permission.CAMERA" granted="true" flags="1030" />
<item name="android.permission.WRITE_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.USE_SIP" granted="true" flags="1030" />
<item name="android.permission.PROCESS_OUTGOING_CALLS" granted="true" flags="3030" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="1030" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.RECORD_AUDIO" granted="true" flags="1030" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
<item name="android.permission.ACCESS_BACKGROUND_LOCATION" granted="true" flags="3030" />
<item name="com.android.voicemail.permission.ADD_VOICEMAIL" granted="true" flags="1030" />
</shared-user>
每次更新,授予和移除权限都会触发回调。
frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java
private PermissionCallback mPermissionCallback = new PermissionCallback() {
@Override
public void onGidsChanged(int appId, int userId) {
mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
}
@Override
public void onPermissionGranted(int uid, int userId) {
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Not critical; if this is lost, the application has to request again.
synchronized (mPackages) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
@Override
public void onInstallPermissionGranted() {
synchronized (mPackages) {
scheduleWriteSettingsLocked();
}
}
@Override
public void onPermissionRevoked(int uid, int userId) {
mOnPermissionChangeListeners.onPermissionsChanged(uid);
synchronized (mPackages) {
// Critical; after this call the application should never have the permission
mSettings.writeRuntimePermissionsForUserLPr(userId, true);
}
final int appId = UserHandle.getAppId(uid);
killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
}
@Override
public void onInstallPermissionRevoked() {
synchronized (mPackages) {
scheduleWriteSettingsLocked();
}
}
@Override
public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
synchronized (mPackages) {
for (int userId : updatedUserIds) {
mSettings.writeRuntimePermissionsForUserLPr(userId, sync);
}
}
}
@Override
public void onInstallPermissionUpdated() {
synchronized (mPackages) {
scheduleWriteSettingsLocked();
}
}
@Override
public void onPermissionRemoved() {
synchronized (mPackages) {
mSettings.writeLPr();
}
}
};
安装权限被更新flag时会回调onInstallPermissionUpdated,从而执行PMS#scheduleWriteSettingsLocked–>Settings#writeLPr,从而更新/data/system/packages.xml。
而运行时权限更新flag时会回调onPermissionUpdated,从而执行Settings#writeRuntimePermissionsForUserLPr-来更新data/system/users/{userid}/runtime-permissions.xml。
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void updatePermissionFlags(String permName, String packageName, int flagMask,
...
if (permissionUpdated && callback != null) {
// Install and runtime permissions are stored in different places,
// so figure out what permission changed and persist the change.
if (permissionsState.getInstallPermissionState(permName) != null) {//安装权限
callback.onInstallPermissionUpdated();
} else if (permissionsState.getRuntimePermissionState(permName, userId) != null//运行时权限
|| hadState) {
callback.onPermissionUpdated(new int[] { userId }, false);
}
}
}
此外,运行时权限在授予或者移除时会分别回调onPermissionGranted和onPermissionRevoked,这两个函数也会更新data/system/users/{userid}/runtime-permissions.xml。安装权限在授予或者移除时没有回调。onInstallPermissionGranted和onInstallPermissionRevoked是在安全级别包含development级别的权限被授予或者移除时触发的,与一般的安装权限无关。
PMS扫描应用权限信息
PMS开机扫描各个应用时,会根据AndroidManifest,xml的内容获得应用需要的权限,应用定义的权限等信息,保存到特定的数据结构里面。常用的几个权限相关的标签说明:
uses-permission标签:声明应用需要使用到的权限。将权限的名称保存到PackageParser.Package的requestedPermissions集合中。
frameworks/base/core/ava/android/content/pm/PackageParser.java
private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
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);
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;
}
}
final String requiredFeature = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
final String requiredNotfeature = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
sa.recycle();
XmlUtils.skipCurrentTag(parser);
if (name == null) {
return true;
}
if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
return true;
}
// Only allow requesting this permission if the platform supports the given feature.
if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(requiredFeature)) {
return true;
}
// Only allow requesting this permission if the platform doesn't support the given feature.
if (requiredNotfeature != null && mCallback != null
&& mCallback.hasFeature(requiredNotfeature)) {
return true;
}
int index = pkg.requestedPermissions.indexOf(name);
if (index == -1) {
pkg.requestedPermissions.add(name.intern());
} else {
Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+ name + " in package: " + pkg.packageName + " at: "
+ parser.getPositionDescription());
}
return true;
}
permission标签:定义权限。name指定了权限的名称,protectionLevel指定了权限的保护等级,没有指定的话默认是Normal级别的。icon指定了权限图标。description 指明权限描述。label 指明权限标签。permissionGroup 为权限分组,方便将权限进行逻辑分组。backgroundPermission是对应的后台权限,name对应的则是前台权限,两者的关系后面会提到。permissionFlags指定了权限的flag。权限的信息保存在PermissionInfo中的结构中,后台权限和PermissionInfo保存在Permission中,所有Permission会被添加到PackageParser.Package的permissions集合中。
frameworks/base/core/ava/android/content/pm/PackageParser.java
private boolean parsePermission(Package owner, Resources res,
XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermission);
String backgroundPermission = null;
if (sa.hasValue(
com.android.internal.R.styleable.AndroidManifestPermission_backgroundPermission)) {
if ("android".equals(owner.packageName)) {
backgroundPermission = sa.getNonResourceString(
com.android.internal.R.styleable
.AndroidManifestPermission_backgroundPermission);
} else {
Slog.w(TAG, owner.packageName + " defines a background permission. Only the "
+ "'android' package can do that.");
}
}
Permission perm = new Permission(owner, backgroundPermission);
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission>", sa, true /*nameRequired*/,
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_roundIcon,
com.android.internal.R.styleable.AndroidManifestPermission_logo,
com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
// Note: don't allow this value to be a reference to a resource
// that may change.
perm.info.group = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup);
if (perm.info.group != null) {
perm.info.group = perm.info.group.intern();
}
perm.info.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermission_description,
0);
perm.info.requestRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermission_request, 0);
perm.info.protectionLevel = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
PermissionInfo.PROTECTION_NORMAL);
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
// For now only platform runtime permissions can be restricted
if (!perm.info.isRuntime() || !"android".equals(perm.info.packageName)) {
perm.info.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
perm.info.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
} else {
// The platform does not get to specify conflicting permissions
if ((perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
&& (perm.info.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
throw new IllegalStateException("Permission cannot be both soft and hard"
+ " restricted: " + perm.info.name);
}
}
sa.recycle();
if (perm.info.protectionLevel == -1) {
outError[0] = "<permission> does not specify protectionLevel";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
if (perm.info.getProtectionFlags() != 0) {
if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
outError[0] = "<permission> protectionLevel specifies a non-instant flag but is "
+ "not based on signature type";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.permissions.add(perm);
return true;
}
permission-group标签:定义权限组。icon指定了权限组图标,label指定了权限组标签。description指定了权限组描述。request指定申请前台权限时显示的申请请求字符串。requestDetail指定了申请前台权限时的提示信息。backgroundRequest指定申请后台权限时显示的申请请求字符串。backgroundRequestDetail指定申请后台权限时的提示信息。目前使用了后台权限的前台权限有android.permission.ACCESS_FINE_LOCATION,android.permission.ACCESS_COARSE_LOCATION
这些位置相关权限。更多详细信息请参考Google官方说明:三态位置权限
frameworks/base/core/ava/android/content/pm/PackageParser.java
private boolean parsePermissionGroup(Package owner, int flags, Resources res,
XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermissionGroup);
int requestDetailResourceId = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
int backgroundRequestResourceId = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
0);
int backgroundRequestDetailResourceId = sa.getResourceId(
com.android.internal.R.styleable
.AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
PermissionGroup perm = new PermissionGroup(owner, requestDetailResourceId,
backgroundRequestResourceId, backgroundRequestDetailResourceId);
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission-group>", sa, true /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_roundIcon,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
perm.info.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
0);
perm.info.requestRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_request, 0);
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
perm.info.priority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0);
sa.recycle();
if (!parseAllMetaData(res, parser, "<permission-group>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.permissionGroups.add(perm);
return true;
}
permission-tree标签:permission-tree标签主要用于使用PackageManager#addPermission动态添加权限。permission-tree本身不声明权限,只是声明了一组权限的命名空间。使用PackageManager#addPermission的方法是:
.AndroidManifest.xml声明一个permission-tree,name的名称是必须的,是要动态添加的权限名称的一个子字符串。当要动态添加的权限在系统中没有记录时,调用PackageManager#addPermission的应用UID要和定义permission-tree的应用UID一致。
举个例子,一个应用在其AndroidManifest.xml声明了name为“com.example.foo”的permission-tree的节点,那么,该应用可以自由地使用PackageManager#addPermission添加名字带有“com.example.foo”的任意权限,例如“com.example.foo.FOR_EXAMPLE”,而且这些要添加的权限不需要在系统或者本应用中使用permission标签显式定义,如果要添加的权限名称在系统中曾经被使用过,那么它一定要是使用动态方法添加的权限,不然会报错:“Not allowed to modify non-dynamic permission xxx”。
解析AndroidManifest,xml的permission-tree节点:
frameworks/base/core/java/android/content/pm/PackageParser.java
private boolean parsePermissionTree(Package owner, Resources res,
XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner, (String) null);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermissionTree);
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission-tree>", sa, true /*nameRequired*/,
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_roundIcon,
com.android.internal.R.styleable.AndroidManifestPermissionTree_logo,
com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
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 false;
}
perm.info.descriptionRes = 0;
perm.info.requestRes = 0;
perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
perm.tree = true;
if (!parseAllMetaData(res, parser, "<permission-tree>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.permissions.add(perm);
return true;
}
动态添加权限的实现:
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private boolean addDynamicPermission(
PermissionInfo info, int callingUid, PermissionCallback callback) {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant apps can't add permissions");
}
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
}
final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);//查找要动态添加的权限在系统中是否有记录
final boolean added;
final boolean changed;
synchronized (mLock) {
BasePermission bp = mSettings.getPermissionLocked(info.name);
added = bp == null;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
if (added) {//没有记录
enforcePermissionCapLocked(info, tree);
bp = new BasePermission(info.name, tree.getSourcePackageName(),
BasePermission.TYPE_DYNAMIC);
} else if (!bp.isDynamic()) {//不是动态添加的记录就报错
throw new SecurityException("Not allowed to modify non-dynamic permission "
+ info.name);
}
changed = bp.addToTree(fixedLevel, info, tree);//设置新添加权限的字段
if (added) {
mSettings.putPermissionLocked(info.name, bp);//添加到系统记录
}
}
if (changed && callback != null) {
callback.onPermissionChanged();
}
return added;
}
权限状态更新
在包扫描的结尾阶段,都会有两步跟权限相关的操作:commitReconciledScanResultLocked和updateSettingsLI。
commitReconciledScanResultLocked这一步会同步扫描得到的权限信息到PermissionManagerService,以向PermissionManagerService报告本次扫描的apk新增了哪些权限、权限树、权限组,然后让PermissionManagerService记录下来,这个在下章节Systemconfig/packages.xml/扫描包 权限区别会介绍到。
updateSettingsLI这一步主要是根据扫描apk的结果改变apk的权限状态(PermissionsState)。
frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java
@GuardedBy("mPackages")
private void commitPackagesLocked(final CommitRequest request) {
...
commitReconciledScanResultLocked(reconciledPkg);//第一步
updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason);//第二步
...
updateSettingsLI最终会调用到updateSettingsInternalLI,通过updatePermissions来更新权限状态。
frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java
private void updateSettingsInternalLI(PackageParser.Package pkg,
String installerPackageName, int[] allUsers, int[] installedForUsers,
PackageInstalledInfo res, UserHandle user, int installReason) {
...
synchronized (mPackages) {
// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
mPermissionManager.updatePermissions(pkg.packageName, pkg, true, mPackages.values(),
mPermissionCallback);
...
最终会调用到PermissionManagerService#updatePermissions。该函数的第一个参数是要更新权限状态的包名,第二个参数是要更新权限状态的 PackageParser.Package,第三个参数是第二个参数对应的Volumn UUID,第四个参数的传入的flag,第五个参数是一个PackageParser.Package集合,一般是PMS记录的包集合,第六个参数是权限状态发生变化时的回调。
flag参数会决定更新权限状态的策略。当带有UPDATE_PERMISSIONS_ALL标志位时,系统会尝试更新所有包的权限状态,这个通常发生在PermissionManagerService#updateAllPermissions调用时,在PackageManagerService.java构造函数扫描包动作结束后的一个点,PermissionManagerService#updateAllPermissions会被调用。当带有UPDATE_PERMISSIONS_REPLACE_PKG标志位时,系统会重置这个包的权限状态(PermissionSettings)再作修改,否则就会在原来的权限状态上作修改。UPDATE_PERMISSIONS_REPLACE_ALL标志位和UPDATE_PERMISSIONS_ALL搭配使用,表示既要更新所有包的权限状态,又要重置这些包的权限状态。
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void updatePermissions(String changingPkgName, PackageParser.Package changingPkg,
String replaceVolumeUuid, int flags, Collection<PackageParser.Package> allPackages,
PermissionCallback callback) {
// TODO: Most of the methods exposing BasePermission internals [source package name,
// etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
// have package settings, we should make note of it elsewhere [map between
// source package name and BasePermission] and cycle through that here. Then we
// define a single method on BasePermission that takes a PackageSetting, changing
// package name and a package.
// NOTE: With this approach, we also don't need to tree trees differently than
// normal permissions. Today, we need two separate loops because these BasePermission
// objects are stored separately.
// Make sure there are no dangling permission trees.
flags = updatePermissionTrees(changingPkgName, changingPkg, flags);
// Make sure all dynamic permissions have been assigned to a package,
// and make sure there are no dangling permissions.
flags = updatePermissions(changingPkgName, changingPkg, flags);
synchronized (mLock) {
if (mBackgroundPermissions == null) {
// Cache background -> foreground permission mapping.
// Only system declares background permissions, hence mapping does never change.
mBackgroundPermissions = new ArrayMap<>();
for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
if (bp.perm != null && bp.perm.info != null
&& bp.perm.info.backgroundPermission != null) {
String fgPerm = bp.name;
String bgPerm = bp.perm.info.backgroundPermission;
List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
if (fgPerms == null) {
fgPerms = new ArrayList<>();
mBackgroundPermissions.put(bgPerm, fgPerms);
}
fgPerms.add(fgPerm);
}
}
}
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "restorePermissionState");
// Now update the permissions for all packages, in particular
// replace the granted permissions of the system packages.
if ((flags & UPDATE_PERMISSIONS_ALL) != 0) {//带有UPDATE_PERMISSIONS_ALL表示要更新所有包的权限状态,先把入参changingPkg以外的包权限状态更新
for (PackageParser.Package pkg : allPackages) {
if (pkg != changingPkg) {
// Only replace for packages on requested volume
final String volumeUuid = getVolumeUuidForPackage(pkg);
final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
&& Objects.equals(replaceVolumeUuid, volumeUuid);//带有UPDATE_PERMISSIONS_REPLACE_ALL表示当前处于安卓大版本升级后或者创建新用户的状态
restorePermissionState(pkg, replace, changingPkgName, callback);
}
}
}
if (changingPkg != null) {//单独更新入参changingPkg包的权限状态
// Only replace for packages on requested volume
final String volumeUuid = getVolumeUuidForPackage(changingPkg);
final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
&& Objects.equals(replaceVolumeUuid, volumeUuid);
restorePermissionState(changingPkg, replace, changingPkgName, callback);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
restorePermissionState是更新权限状态的关键,参考代码中的注释:
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void restorePermissionState(@NonNull PackageParser.Package pkg, boolean replace,
@Nullable String packageOfInterest, @Nullable PermissionCallback callback) {
// IMPORTANT: There are two types of permissions: install and runtime.
// Install time permissions are granted when the app is installed to
// all device users and users added in the future. Runtime permissions
// are granted at runtime explicitly to specific users. Normal and signature
// protected permissions are install time permissions. Dangerous permissions
// are install permissions if the app's target SDK is Lollipop MR1 or older,
// otherwise they are runtime permissions. This function does not manage
// runtime permissions except for the case an app targeting Lollipop MR1
// being upgraded to target a newer SDK, in which case dangerous permissions
// are transformed from install time to runtime ones.
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
}
final PermissionsState permissionsState = ps.getPermissionsState();
PermissionsState origPermissions = permissionsState;
final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
boolean runtimePermissionsRevoked = false;
int[] updatedUserIds = EMPTY_INT_ARRAY;
boolean changedInstallPermission = false;
//当带有UPDATE_PERMISSIONS_REPLACE_ALL或UPDATE_PERMISSIONS_REPLACE_PKG标志位时,replace为true。当包没有shareduserid时,重置权限状态。当包有shareduserid时,检测对应的SharedUserSetting的权限状态,如果发现SharedUserSetting授予了未被PermissionManagerService记录的权限(包括安装权限和运行时权限),则移除掉这些权限。
if (replace) {
ps.setInstallPermissionsFixed(false);
if (!ps.isSharedUser()) {
origPermissions = new PermissionsState(permissionsState);
permissionsState.reset();
} else {
// We need to know only about runtime permission changes since the
// calling code always writes the install permissions state but
// the runtime ones are written only if changed. The only cases of
// changed runtime permissions here are promotion of an install to
// runtime and revocation of a runtime from a shared user.
synchronized (mLock) {
updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
ps.getSharedUser(), UserManagerService.getInstance().getUserIds());
if (!ArrayUtils.isEmpty(updatedUserIds)) {
runtimePermissionsRevoked = true;
}
}
}
}
permissionsState.setGlobalGids(mGlobalGids);
synchronized (mLock) {
ArraySet<String> newImplicitPermissions = new ArraySet<>();
final int N = pkg.requestedPermissions.size();
for (int i = 0; i < N; i++) {
//遍历包中每一个使用uses-permission声明的权限
final String permName = pkg.requestedPermissions.get(i);
final BasePermission bp = mSettings.getPermissionLocked(permName);
final boolean appSupportsRuntimePermissions =
pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
String upgradedActivityRecognitionPermission = null;
if (DEBUG_INSTALL) {
Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
}
if (bp == null || bp.getSourcePackageSetting() == null) {
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Unknown permission " + permName
+ " in package " + pkg.packageName);
}
}
continue;
}
// Cache newImplicitPermissions before modifing permissionsState as for the shared
// uids the original and new state are the same object
if (!origPermissions.hasRequestedPermission(permName)
&& (pkg.implicitPermissions.contains(permName)
|| (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
if (pkg.implicitPermissions.contains(permName)) {
// If permName is an implicit permission, try to auto-grant
newImplicitPermissions.add(permName);
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, permName + " is newly added for " + pkg.packageName);
}
} else {
// Special case for Activity Recognition permission. Even if AR permission
// is not an implicit permission we want to add it to the list (try to
// auto-grant it) if the app was installed on a device before AR permission
// was split, regardless of if the app now requests the new AR permission
// or has updated its target SDK and AR is no longer implicit to it.
// This is a compatibility workaround for apps when AR permission was
// split in Q.
final List<PermissionManager.SplitPermissionInfo> permissionList =
getSplitPermissions();
int numSplitPerms = permissionList.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
PermissionManager.SplitPermissionInfo sp =
permissionList.get(splitPermNum);
String splitPermName = sp.getSplitPermission();
if (sp.getNewPermissions().contains(permName)
&& origPermissions.hasInstallPermission(splitPermName)) {
upgradedActivityRecognitionPermission = splitPermName;
newImplicitPermissions.add(permName);
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, permName + " is newly added for "
+ pkg.packageName);
}
break;
}
}
}
}
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
if (DEBUG_PERMISSIONS) {
Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+ " for package " + pkg.packageName);
}
continue;
}
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
if (DEBUG_PERMISSIONS) {
Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+ " for package " + pkg.packageName);
}
continue;
}
final String perm = bp.getName();
boolean allowedSig = false;
int grant = GRANT_DENIED;
// Keep track of app op permissions.
if (bp.isAppOp()) {
mSettings.addAppOpPackage(perm, pkg.packageName);
}
if (bp.isNormal()) {
// For all apps normal permissions are install time ones.
//对于应用申请的普通级别的权限,是直接授权的(GRANT_INSTALL)
grant = GRANT_INSTALL;
} else if (bp.isRuntime()) {//对于运行时权限,如果以前是安装权限的现在变更为运行时权限的,视作升级(GRANT_UPGRADE)的授权方式;如果是普通的运行时权限,视作运行时(GRANT_RUNTIME)授权方式
if (origPermissions.hasInstallPermission(bp.getName())
|| upgradedActivityRecognitionPermission != null) {
// Before Q we represented some runtime permissions as install permissions,
// in Q we cannot do this anymore. Hence upgrade them all.
grant = GRANT_UPGRADE;
} else {
// For modern apps keep runtime permissions unchanged.
grant = GRANT_RUNTIME;
}
} else if (bp.isSignature()) {//对于签名级别的权限,如果签名校对成功,可直接授权(GRANT_INSTALL)
// For all apps signature permissions are install time ones.
allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
if (allowedSig) {
grant = GRANT_INSTALL;
}
}
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Considering granting permission " + perm + " to package "
+ pkg.packageName);
}
if (grant != GRANT_DENIED) {
if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) {//对于1.非系统应用;2.非重置权限状态下;3.目标更改权限不是运行时权限;4.目标更改权限是安装权限或者是签名权限但是签名不匹配时;5.目前包没有对目标权限授权 这5种情况同时满足的情形下,拒绝授权。例如部分厂商手机OTA时会对data分区部分app做升级,新的app声明了一些以前版本没有声明过的安装权限,那系统会拒绝为这个新app自动授予这个安装权限
// If this is an existing, non-system package, then
// we can't add any new permissions to it. Runtime
// permissions can be added any time - they ad dynamic.
if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
if (!isNewPlatformPermissionForPackage(perm, pkg)) {
grant = GRANT_DENIED;
}
}
}
switch (grant) {
case GRANT_INSTALL: {
// Revoke this as runtime permission to handle the case of
// a runtime permission being downgraded to an install one.
// Also in permission review mode we keep dangerous permissions
// for legacy apps
//运行时权限降级为安装权限的情况,移除掉这个运行时权限
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (origPermissions.getRuntimePermissionState(
perm, userId) != null) {
// Revoke the runtime permission and clear the flags.
origPermissions.revokeRuntimePermission(bp, userId);
origPermissions.updatePermissionFlags(bp, userId,
PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
// If we revoked a permission permission, we have to write.
updatedUserIds = ArrayUtils.appendInt(
updatedUserIds, userId);
}
}
// Grant an install permission.
//正常情况下,直接授予安装权限
if (permissionsState.grantInstallPermission(bp) !=
PERMISSION_OPERATION_FAILURE) {
changedInstallPermission = true;
}
} break;
case GRANT_RUNTIME: {
boolean hardRestricted = bp.isHardRestricted();
boolean softRestricted = bp.isSoftRestricted();
for (int userId : currentUserIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
//PermissionPolicyService是否已经初始化。PermissionPolicyService初始化后会针对限制权限作特殊处理,并且回调updateAllPermissions来重新设置权限状态。如果没有初始化,此处不必理会限制权限。
final boolean permissionPolicyInitialized =
mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.isInitialized(userId);
PermissionState permState = origPermissions
.getRuntimePermissionState(perm, userId);
int flags = permState != null ? permState.getFlags() : 0;
boolean wasChanged = false;
//限制免除
boolean restrictionExempt =
(origPermissions.getPermissionFlags(bp.name, userId)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
//限制使用
boolean restrictionApplied = (origPermissions.getPermissionFlags(
bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {//app支持运行时权限
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {//PermissionPolicyService已经初始化而且变成的权限时硬性限制权限的情况下
if (!restrictionExempt) {
if (permState != null && permState.isGranted()
&& permissionsState.revokeRuntimePermission(
bp, userId) != PERMISSION_OPERATION_FAILURE) {//非限制免除情况下,移除掉已经授予过的运行时权限
wasChanged = true;
}
if (!restrictionApplied) {//原来没有限制使用标志位的情况下,加上限制使用标志位
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If soft restricted we allow holding in a restricted form
} else if (permissionPolicyInitialized && softRestricted) {
// Regardless if granted set the restriction flag as it
// may affect app treatment based on this permission.
if (!restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// Remove review flag as it is not necessary anymore
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
|| (!hardRestricted || restrictionExempt)) {
if (permState != null && permState.isGranted()) {
if (permissionsState.grantRuntimePermission(bp, userId)
== PERMISSION_OPERATION_FAILURE) {//如果1.PermissionPolicyService未被初始化;2.目标权限不是硬性限制或者可以限制免除(影响限制权限在非限制免除的情况下不能被豁免) ;两种情况满足之一,且原来的权限状态是授予这个运行时权限的,新的权限状态也应该授予
wasChanged = true;
}
}
}
} else {
if (permState == null) {
// New permission
if (PLATFORM_PACKAGE_NAME.equals(
bp.getSourcePackageName())) {
if (!bp.isRemoved()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED
| FLAG_PERMISSION_REVOKE_ON_UPGRADE;
wasChanged = true;
}
}
}
if (!permissionsState.hasRuntimePermission(bp.name, userId)
&& permissionsState.grantRuntimePermission(bp, userId)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
// If legacy app always grant the permission but if restricted
// and not exempt take a note a restriction should be applied.
if (permissionPolicyInitialized
&& (hardRestricted || softRestricted)
&& !restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If unrestricted or restriction exempt, don't apply restriction.
//如果PermissionPolicyService已经初始化,目标权限不是限制权限或者限制免除的情况下,移除掉目标权限的FLAG_PERMISSION_APPLY_RESTRICTION标志位(如果有的话)
if (permissionPolicyInitialized) {
if (!(hardRestricted || softRestricted) || restrictionExempt) {
if (restrictionApplied) {
flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
// Dropping restriction on a legacy app implies a review
if (!appSupportsRuntimePermissions) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
}
wasChanged = true;
}
}
}
if (wasChanged) {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
permissionsState.updatePermissionFlags(bp, userId,
MASK_PERMISSION_FLAGS_ALL, flags);
}
} break;
case GRANT_UPGRADE: {
// Upgrade from Pre-Q to Q permission model. Make all permissions
// runtime
PermissionState permState = origPermissions
.getInstallPermissionState(perm);
int flags = (permState != null) ? permState.getFlags() : 0;
BasePermission bpToRevoke =
upgradedActivityRecognitionPermission == null
? bp : mSettings.getPermissionLocked(
upgradedActivityRecognitionPermission);
// Remove install permission
//安装权限升级运行时权限,先把安装权限移除掉
if (origPermissions.revokeInstallPermission(bpToRevoke)
!= PERMISSION_OPERATION_FAILURE) {
origPermissions.updatePermissionFlags(bpToRevoke,
UserHandle.USER_ALL,
(MASK_PERMISSION_FLAGS_ALL
& ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
changedInstallPermission = true;
}
boolean hardRestricted = bp.isHardRestricted();
boolean softRestricted = bp.isSoftRestricted();
for (int userId : currentUserIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
final boolean permissionPolicyInitialized =
mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.isInitialized(userId);
boolean wasChanged = false;
boolean restrictionExempt =
(origPermissions.getPermissionFlags(bp.name, userId)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
boolean restrictionApplied = (origPermissions.getPermissionFlags(
bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
if (permState != null && permState.isGranted()
&& permissionsState.revokeRuntimePermission(
bp, userId) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If soft restricted we allow holding in a restricted form
} else if (permissionPolicyInitialized && softRestricted) {
// Regardless if granted set the restriction flag as it
// may affect app treatment based on this permission.
if (!restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// Remove review flag as it is not necessary anymore
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized ||
(!hardRestricted || restrictionExempt)) {
if (permissionsState.grantRuntimePermission(bp, userId) !=
PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
} else {
if (!permissionsState.hasRuntimePermission(bp.name, userId)
&& permissionsState.grantRuntimePermission(bp,
userId) != PERMISSION_OPERATION_FAILURE) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
// If legacy app always grant the permission but if restricted
// and not exempt take a note a restriction should be applied.
if (permissionPolicyInitialized
&& (hardRestricted || softRestricted)
&& !restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If unrestricted or restriction exempt, don't apply restriction.
if (permissionPolicyInitialized) {
if (!(hardRestricted || softRestricted) || restrictionExempt) {
if (restrictionApplied) {
flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
// Dropping restriction on a legacy app implies a review
if (!appSupportsRuntimePermissions) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
}
wasChanged = true;
}
}
}
if (wasChanged) {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
permissionsState.updatePermissionFlags(bp, userId,
MASK_PERMISSION_FLAGS_ALL, flags);
}
} break;
default: {
if (packageOfInterest == null
|| packageOfInterest.equals(pkg.packageName)) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " because it was previously installed without");
}
}
} break;
}
} else {//结果是拒绝授权的情况下,移除掉目标权限
if (permissionsState.revokeInstallPermission(bp) !=
PERMISSION_OPERATION_FAILURE) {
// Also drop the permission flags.
permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
MASK_PERMISSION_FLAGS_ALL, 0);
changedInstallPermission = true;
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + pkg.packageName
+ " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
} else if (bp.isAppOp()) {
// 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 (DEBUG_PERMISSIONS
&& (packageOfInterest == null
|| packageOfInterest.equals(pkg.packageName))) {
Slog.i(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
}
}
}
}
if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
!ps.isSystem() || ps.isUpdatedSystem()) {
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
ps.setInstallPermissionsFixed(true);
}
updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
updatedUserIds);
updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
permissionsState, pkg, newImplicitPermissions, updatedUserIds);
updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, updatedUserIds);
}
// Persist the runtime permissions state for users with changes. If permissions
// were revoked because no app in the shared user declares them we have to
// write synchronously to avoid losing runtime permissions state.
if (callback != null) {
callback.onPermissionUpdated(updatedUserIds, runtimePermissionsRevoked);
}
for (int userId : updatedUserIds) {
notifyRuntimePermissionStateChanged(pkg.packageName, userId);
}
}
Systemconfig/packages.xml/扫描包 权限区别
Systemconfig能读取权限,packages.xml能读取权限,扫描包也可以读取权限,那这些权限有什么区别呢?
Systemconfig定义的权限都是属于android这个包的,也就是系统权限,能够设置的权限属性比较少,常见的是通过group子标签设置一个gid。最终会记录到PermissionManagerService的mSettings(PermissionSettings)中。此外,SystemConfig不仅仅为权限服务,还有很多全局的系统配置都在里面实现。
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
PermissionManagerService(Context context,
@NonNull Object externalLock) {
mContext = context;
mLock = externalLock;
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
mSettings = new PermissionSettings(mLock);
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler);
mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
context, mHandlerThread.getLooper(), this);
SystemConfig systemConfig = SystemConfig.getInstance();
mSystemPermissions = systemConfig.getSystemPermissions();
mGlobalGids = systemConfig.getGlobalGids();
// propagate permission configuration
final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
SystemConfig.getInstance().getPermissions();
synchronized (mLock) {
for (int i=0; i<permConfig.size(); i++) {
final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.getPermissionLocked(perm.name);
if (bp == null) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.putPermissionLocked(perm.name, bp);//记录来自SystemConifg的权限
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
}
PermissionManagerServiceInternalImpl localService =
new PermissionManagerServiceInternalImpl();
LocalServices.addService(PermissionManagerServiceInternal.class, localService);
LocalServices.addService(PermissionManagerInternal.class, localService);
}
而读取packages.xml的权限信息则是直接同步权限和权限树信息到PermissionManagerService的mSettings(PermissionSettings)中。packages.xml的权限信息来源于Systemconfig和扫描包得到的权限信息,本身不修改任何权限信息,只是帮助系统将权限记录(PermissionSettings)和状态(PermissionsState)初始化。
如下所示,解析出来的权限信息会加入到Settings的mPermissions(PermissionSettings)中。
frameworks/base/services/core/java/com/android/server/pm/Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
...
} else if (tagName.equals("permissions")) {
mPermissions.readPermissions(parser);
Settings的mPermissions和PermissionManagerService的mSettings是同一个,见下面代码。
此外,packages.xml还提供了记录应用权限状态(PermissionState)的记录&恢复功能,
shareuserid状态(SharedUserSetting)的记录&恢复功能。
frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
// Create sub-components that provide services / data. Order here is important.
synchronized (mInstallLock) {
synchronized (mPackages) {
// Expose private service for system components to use.
LocalServices.addService(
PackageManagerInternal.class, new PackageManagerInternalImpl());
sUserManager = new UserManagerService(context, this,
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
mComponentResolver = new ComponentResolver(sUserManager,
LocalServices.getService(PackageManagerInternal.class),
mPackages);
mPermissionManager = PermissionManagerService.create(context,
mPackages /*externalLock*/);
mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
mSettings = new Settings(Environment.getDataDirectory(),
mPermissionManager.getPermissionSettings(), mPackages);//Settings的mPermissions和PermissionManagerService的mSettings是同一个
}
扫描包得到的权限和权限树信息是记录在PackageParser.Package的permissions(ArrayList< Permission>)中,权限组信息是记录在PackageParser.Package的permissionGroups(ArrayList< PermissionGroup>)中。在每次扫描包的最后阶段(PMS#commitPackageSettings),系统会通过PMS#addAllPermissionGroups和PMS#addAllPermissions将PackageParser.Package包含的权限组和权限信息同步到PermissionManagerService的mSettings(PermissionSettings)中。
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
final int N = pkg.permissions.size();
for (int i=0; i<N; i++) {
PackageParser.Permission p = pkg.permissions.get(i);
// Assume by default that we did not install this permission into the system.
p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
synchronized (PermissionManagerService.this.mLock) {
// Now that permission groups have a special meaning, we ignore permission
// groups for legacy apps to prevent unexpected behavior. In particular,
// permissions for one app being granted to someone just because they happen
// to be in a group defined by another app (before this had no implications).
if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
p.group = mSettings.mPermissionGroups.get(p.info.group);
// Warn for a permission in an unknown group.
if (DEBUG_PERMISSIONS
&& p.info.group != null && p.group == null) {
Slog.i(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " in an unknown group " + p.info.group);
}
}
if (p.tree) {
final BasePermission bp = BasePermission.createOrUpdate(
mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
mSettings.getAllPermissionTreesLocked(), chatty);
mSettings.putPermissionTreeLocked(p.info.name, bp);
} else {
final BasePermission bp = BasePermission.createOrUpdate(
mSettings.getPermissionLocked(p.info.name),
p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
mSettings.putPermissionLocked(p.info.name, bp);
}
}
}
}
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void addAllPermissionGroups(PackageParser.Package pkg, boolean chatty) {
final int N = pkg.permissionGroups.size();
StringBuilder r = null;
for (int i=0; i<N; i++) {
final PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
final PackageParser.PermissionGroup cur = mSettings.mPermissionGroups.get(pg.info.name);
final String curPackageName = (cur == null) ? null : cur.info.packageName;
final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
if (cur == null || isPackageUpdate) {
mSettings.mPermissionGroups.put(pg.info.name, pg);
if (chatty && DEBUG_PACKAGE_SCANNING) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
if (isPackageUpdate) {
r.append("UPD:");
}
r.append(pg.info.name);
}
} else {
Slog.w(TAG, "Permission group " + pg.info.name + " from package "
+ pg.info.packageName + " ignored: original from "
+ cur.info.packageName);
if (chatty && DEBUG_PACKAGE_SCANNING) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append("DUP:");
r.append(pg.info.name);
}
}
}
if (r != null && DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, " Permission Groups: " + r);
}
}
相比Systemconfig,扫描包定义的权限可以包含更多的信息。例如可以定义所属权限组,图标,标签,描述,后台权限,安全等级等。以下是系统的AndroidManifest.xml定义的一个权限:
frameworks/base/core/res/AndroidManifest.xml
<permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
android:protectionLevel="dangerous|instant" />