赞
踩
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); } }
在开机阶段,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开机扫描各个应用时,会根据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定义的权限都是属于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" />
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。