一.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" />