权限白名单
Android 的“权限白名单”不是一个只存包名的列表。Android 17 的实现同时保存特权权限、签名权限和 OEM 权限,并且为 system、vendor、product、system_ext、APEX 分别保留来源。最终是否授予权限,要经过 XML 解析、包所在分区、签名匹配、系统启动阶段和更新系统应用兼容逻辑共同决定。
本文沿着真实调用链阅读:SystemConfig 读取配置,PermissionAllowlist 保存配置,AccessCheckingService 把配置交给权限策略,AppIdPermissionPolicy 在权限重算时消费它。
1. 白名单是多张表
1.1 数据字段
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java
public final class PermissionAllowlist {
private final ArrayMap<String, ArrayMap<String, Boolean>> mOemAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mPrivilegedAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mVendorPrivilegedAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mProductPrivilegedAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mSystemExtPrivilegedAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, ArrayMap<String, Boolean>>>
mApexPrivilegedAppAllowlists = new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mSignatureAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mVendorSignatureAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mProductSignatureAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mSystemExtSignatureAppAllowlist =
new ArrayMap<>();
private final ArrayMap<String, ArrayMap<String, Boolean>> mApexSignatureAppAllowlist =
new ArrayMap<>();
}每个普通分区表的形状都是 包名 -> (权限名 -> Boolean)。APEX 特权表多一层模块名:APEX 模块名 -> 包名 -> (权限名 -> Boolean)。这层不是装饰:同一个 APK 包名可能来自不同 APEX,模块名是把配置绑定到发布它的模块的关键。
1.2 查询结果是三值
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java
@Nullable
public Boolean getPrivilegedAppAllowlistState(
@NonNull String packageName, @NonNull String permissionName) {
ArrayMap<String, Boolean> permissions = mPrivilegedAppAllowlist.get(packageName);
if (permissions == null) {
return null;
}
return permissions.get(permissionName);
}
@Nullable
public Boolean getApexPrivilegedAppAllowlistState(
@NonNull String moduleName, @NonNull String packageName,
@NonNull String permissionName) {
ArrayMap<String, ArrayMap<String, Boolean>> allowlist =
mApexPrivilegedAppAllowlists.get(moduleName);
if (allowlist == null) {
return null;
}
ArrayMap<String, Boolean> permissions = allowlist.get(packageName);
if (permissions == null) {
return null;
}
return permissions.get(permissionName);
}null 表示没有这条配置,TRUE 表示明确允许,FALSE 表示明确拒绝。策略层不能把 null 当作 false 直接返回,因为未配置时还要根据调试版本、启动阶段、更新系统应用等规则决定是记录违规还是拒绝授予。
2. XML 分区路由
2.1 顶层标签的入口
源码文件:frameworks/base/services/core/java/com/android/server/SystemConfig.java
case "privapp-permissions":
readPrivappPermissions(parser, permFile, allowPrivappPermissions);
break;
case "signature-permissions":
readSignaturePermissions(parser, permFile, allowSignaturePermissions);
break;
case "oem-permissions":
readOemPermissions(parser, permFile, allowOemPermissions);
break;readPermissionsFromXml 先依据文件所在目录计算该文件属于哪个分区,再把解析任务交给对应方法。allowPrivappPermissions、allowSignaturePermissions 和 allowOemPermissions 是读取权限,而不是 XML 内容里的开关;不允许的分区会记录日志并跳过标签。
2.2 特权分区选择
源码文件:frameworks/base/services/core/java/com/android/server/SystemConfig.java
private void readPrivappPermissions(
XmlPullParser parser, File permFile, boolean allow)
throws IOException, XmlPullParserException {
if (allow) {
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() + "/");
boolean apex = permFile.toPath().startsWith(
Environment.getApexDirectory().toPath() + "/");
if (vendor) {
readPrivAppPermissions(parser,
mPermissionAllowlist.getVendorPrivilegedAppAllowlist());
} else if (product) {
readPrivAppPermissions(parser,
mPermissionAllowlist.getProductPrivilegedAppAllowlist());
} else if (systemExt) {
readPrivAppPermissions(parser,
mPermissionAllowlist.getSystemExtPrivilegedAppAllowlist());
} else if (apex) {
readApexPrivAppPermissions(parser, permFile,
Environment.getApexDirectory().toPath());
} else {
readPrivAppPermissions(parser,
mPermissionAllowlist.getPrivilegedAppAllowlist());
}
} else {
logNotAllowedInPartition("privapp-permissions", permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
}这里的 owner 是 SystemConfig,消费者是 PermissionAllowlist。vendor/odm、product、system_ext 和 system 使用不同 inner map,防止一个分区的配置意外授权另一个分区的特权应用。APEX 走专门的读取函数,因为它必须从文件路径推导模块名并写入三层 map。
2.3 签名分区隔离
源码文件:frameworks/base/services/core/java/com/android/server/SystemConfig.java
private void readSignaturePermissions(
XmlPullParser parser, File permFile, boolean allow)
throws IOException, XmlPullParserException {
if (allow) {
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() + "/");
boolean apex = permFile.toPath().startsWith(
Environment.getApexDirectory().toPath() + "/");
if (vendor) {
readSignaturePermissions(parser,
mPermissionAllowlist.getVendorSignatureAppAllowlist());
} else if (product) {
readSignaturePermissions(parser,
mPermissionAllowlist.getProductSignatureAppAllowlist());
} else if (systemExt) {
readSignaturePermissions(parser,
mPermissionAllowlist.getSystemExtSignatureAppAllowlist());
} else if (apex) {
readSignaturePermissions(parser,
mPermissionAllowlist.getApexSignatureAppAllowlist());
} else {
readSignaturePermissions(parser,
mPermissionAllowlist.getSignatureAppAllowlist());
}
} else {
logNotAllowedInPartition("signature-permissions", permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
}签名白名单和特权白名单的读取隔离,但 XML 的 package/permission 结构相同。区别在于它们最终由不同的 protection flag 分支消费,不能因为 XML 形状相同就混为一谈。
3. XML 三值写入
源码文件:frameworks/base/services/core/java/com/android/server/SystemConfig.java
private static void readPermissionAllowlist(
@NonNull XmlPullParser parser,
@NonNull ArrayMap<String, ArrayMap<String, Boolean>> allowlist,
@NonNull String tagName)
throws IOException, XmlPullParserException {
final String packageName = parser.getAttributeValue(null, "package");
if (TextUtils.isEmpty(packageName)) {
Slog.w(TAG, "package is required for <" + tagName + ">");
return;
}
ArrayMap<String, Boolean> permissions = allowlist.get(packageName);
if (permissions == null) {
permissions = new ArrayMap<>();
}
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
final String name = parser.getName();
if ("permission".equals(name)) {
final String permissionName = parser.getAttributeValue(null, "name");
if (TextUtils.isEmpty(permissionName)) {
Slog.w(TAG, "name is required");
continue;
}
permissions.put(permissionName, Boolean.TRUE);
} else if ("deny-permission".equals(name)) {
String permissionName = parser.getAttributeValue(null, "name");
if (TextUtils.isEmpty(permissionName)) {
Slog.w(TAG, "name is required");
continue;
}
permissions.put(permissionName, Boolean.FALSE);
}
}
allowlist.put(packageName, permissions);
}这段代码有三个值得在调试时记住的行为:
<permission>写入Boolean.TRUE,<deny-permission>写入Boolean.FALSE;拒绝是显式状态,不是“没有配置”的别名。- 同一个 package 再次出现时复用原来的 inner map,因此多个 XML 文件可以合并配置。
- 缺少
package或name只输出 warning 并放弃该条目,不会生成一个半有效的配置。
示例配置:
<permissions>
<privapp-permissions package="com.example.systemui">
<permission name="android.permission.STATUS_BAR_SERVICE"/>
<deny-permission name="android.permission.READ_LOGS"/>
</privapp-permissions>
</permissions>上例在 map 中得到:STATUS_BAR_SERVICE -> TRUE、READ_LOGS -> FALSE。如果完全没有 READ_LOGS 条目,查询结果是 null,策略层仍可能在宽松构建中保留授予或仅记录违规。
4. 配置进入策略
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
fun initialize() {
packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
packageManagerLocal =
LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java)
userManagerService = UserManagerService.getInstance()
systemConfig = SystemConfig.getInstance()
val configPermissions = systemConfig.permissions
val privilegedPermissionAllowlistPackages =
systemConfig.privilegedPermissionAllowlistPackages
val permissionAllowlist = systemConfig.permissionAllowlist
val state = MutableAccessState()
policy.initialize(
state,
userIds,
packageStates,
disabledSystemPackageStates,
knownPackages,
isLeanback,
configPermissions,
privilegedPermissionAllowlistPackages,
permissionAllowlist,
implicitToSourcePermissions,
)
persistence.initialize()
persistence.read(state)
this.state = state
}SystemConfig.getInstance() 产生的白名单对象没有在这里复制成另一套格式,而是作为 externalState.permissionAllowlist 的一部分交给 policy。这样启动时读到的配置和后续权限重算使用的是同一个快照;白名单文件本身不会在每次 checkPermission 调用时重新解析。
5. 特权授予判定
5.1 重算组合条件
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
val mayGrantByPrivileged =
!permission.isPrivileged ||
requestingPackageStates.anyIndexed { _, it ->
checkPrivilegedPermissionAllowlistIfNeeded(it, permission)
}
val shouldGrantBySignature =
permission.isSignature &&
requestingPackageStates.anyIndexed { _, it ->
shouldGrantPermissionBySignature(it, permission)
}
val shouldGrantByProtectionFlags =
requestingPackageStates.anyIndexed { _, it ->
shouldGrantPermissionByProtectionFlags(it, permission)
}
if (mayGrantByPrivileged &&
(shouldGrantBySignature || shouldGrantByProtectionFlags)
) {
PermissionFlags.PROTECTION_GRANTED
} else {
0
}这里的白名单不是单独的“授予 API”。它参与权限状态重算:特权权限先通过 mayGrantByPrivileged,再与签名匹配或 protection flag 条件组合,最后写入 PROTECTION_GRANTED。调用方是权限状态 reconcile 流程,消费者是每个 appId/userId 的 permission flags。
5.2 特权白名单检查
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
private fun MutateStateScope.checkPrivilegedPermissionAllowlistIfNeeded(
packageState: PackageState,
permission: Permission,
): Boolean {
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
return true
}
if (packageState.packageName == PLATFORM_PACKAGE_NAME) {
return true
}
if (!(packageState.isSystem && packageState.isPrivileged)) {
return true
}
if (
permission.packageName !in
newState.externalState.privilegedPermissionAllowlistPackages
) {
return true
}
val allowlistState =
getPrivilegedPermissionAllowlistState(packageState, permission.name)
if (allowlistState != null) {
return allowlistState
}
if (packageState.isUpdatedSystemApp) {
return true
}
if (!newState.externalState.isSystemReady) {
if (!packageState.isApkInUpdatedApex) {
Slog.w(
LOG_TAG,
"Privileged permission ${permission.name} for package" +
" ${packageState.packageName} (${packageState.path}) not in" +
" privileged permission allowlist",
)
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
privilegedPermissionAllowlistViolations +=
"${packageState.packageName}" +
" (${packageState.path}): ${permission.name}"
}
}
}
return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE
}判定顺序决定了很多“看似例外”的行为:
- 关闭控制开关、平台包、非 system/privileged 包,直接返回
true,表示不需要白名单检查。 - permission 声明包不在
privilegedPermissionAllowlistPackages中时,也不进入强制检查。这个集合是“哪些权限需要执行白名单”的门槛,不是包白名单本身。 TRUE返回允许,FALSE返回拒绝;只有null才继续走更新包、启动阶段和 enforce 开关。- 白名单只在
isSystemReady == false的启动阶段收集违规。宽松构建返回true,强制构建返回false,因此同一缺失配置在 userdebug 与 user 构建上的结果可能不同。 - updated system app 和 updated APEX 中的 APK 有专门豁免,避免更新包在恢复或迁移阶段被错误撤销。
6. 分区与 APEX 查询
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
private fun MutateStateScope.getPrivilegedPermissionAllowlistState(
packageState: PackageState,
permissionName: String,
): Boolean? {
val permissionAllowlist = newState.externalState.permissionAllowlist
val packageName = packageState.packageName
val nonApexAllowlistState =
when {
packageState.isVendor || packageState.isOdm ->
permissionAllowlist.getVendorPrivilegedAppAllowlistState(
packageName, permissionName)
packageState.isProduct ->
permissionAllowlist.getProductPrivilegedAppAllowlistState(
packageName, permissionName)
packageState.isSystemExt ->
permissionAllowlist.getSystemExtPrivilegedAppAllowlistState(
packageName, permissionName)
else ->
permissionAllowlist.getPrivilegedAppAllowlistState(
packageName, permissionName)
}
val apexModuleName = packageState.apexModuleName
return if (
apexModuleName != null &&
!(packageState.isVendor || packageState.isOdm || packageState.isSystemExt)
) {
if (nonApexAllowlistState != null) {
Slog.w(
LOG_TAG,
"Package $packageName is an APK in APEX but has permission" +
" allowlist on the system image, please bundle the allowlist in the" +
" $apexModuleName APEX instead",
)
}
val apexAllowlistState =
permissionAllowlist.getApexPrivilegedAppAllowlistState(
apexModuleName, packageName, permissionName)
apexAllowlistState ?: nonApexAllowlistState
} else {
nonApexAllowlistState
}
}普通 APK 只查它所属分区的表。APEX APK 先查模块表;如果模块表没有结果,才回退到非 APEX 表,同时对“把 APK-in-APEX 配置写在 system image”发出迁移警告。FALSE 不会因为回退而变成 null,而是保留显式拒绝。
7. 更新包与原包
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
if ((permission.isPrivileged || permission.isOem) && packageState.isSystem) {
val shouldGrant =
if (packageState.isUpdatedSystemApp) {
val disabledSystemPackageState =
newState.externalState.disabledSystemPackageStates[packageName]
val disabledSystemPackage = disabledSystemPackageState?.androidPackage
disabledSystemPackage != null &&
permission.name in disabledSystemPackage.requestedPermissions &&
shouldGrantPrivilegedOrOemPermission(
disabledSystemPackageState, permission)
} else {
shouldGrantPrivilegedOrOemPermission(packageState, permission)
}
if (shouldGrant) {
return true
}
}更新系统应用的 APK 位于 /data,但它替换的是系统镜像中的原包。策略因此从 disabledSystemPackageStates 取原包:只有原包曾请求该 permission,且原包通过 allowlist 判定,权限才可沿用。这样更新包不能通过新增 manifest 请求绕过出厂白名单。
这条路径的消费者仍然是 permission flags;原包状态只影响“是否允许进入授予条件”,不会把更新包自动变成系统特权包。
8. 签名权限链
8.1 签名先行
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
val hasCommonSigner =
sourceSigningDetails?.hasCommonSignerWithCapability(
packageSigningDetails,
SigningDetails.CertCapabilities.PERMISSION,
) == true ||
packageSigningDetails.hasAncestorOrSelf(platformSigningDetails) ||
platformSigningDetails.checkCapability(
packageSigningDetails,
SigningDetails.CertCapabilities.PERMISSION,
)
if (!Flags.signaturePermissionAllowlistEnabled()) {
return hasCommonSigner
}
if (!hasCommonSigner) {
return false
}白名单开关启用后,签名不匹配会立即失败,白名单不能替代签名校验。只有通过签名能力检查,才继续处理平台签名权限的 allowlist。
8.2 平台签名条件
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
if (permission.packageName == PLATFORM_PACKAGE_NAME) {
val isRequestedByFactoryApp =
if (packageState.isSystem) {
if (packageState.isUpdatedSystemApp) {
val disabledSystemPackage =
newState.externalState.disabledSystemPackageStates[
packageState.packageName]?.androidPackage
disabledSystemPackage != null &&
permission.name in disabledSystemPackage.requestedPermissions
} else {
true
}
} else {
false
}
if (!(isRequestedByFactoryApp ||
getSignaturePermissionAllowlistState(
packageState, permission.name) == true)
) {
Slog.w(
LOG_TAG,
"Signature permission ${permission.name} for package" +
" ${packageState.packageName} (${packageState.path}) not in" +
" signature permission allowlist",
)
if (!Build.isDebuggable() || isSignaturePermissionAllowlistForceEnforced) {
return false
}
}
}平台签名权限有两个允许来源:system 出厂包在原 manifest 中请求过,或包/权限在签名白名单中明确为 TRUE。缺少配置时,debuggable 构建默认只告警,user 构建或强制开关则拒绝。注意这里检查的是 == true:签名白名单中的 FALSE 和 null 都不能满足条件。
8.3 签名分区优先级
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
return when {
packageState.isVendor || packageState.isOdm ->
permissionAllowlist.getVendorSignatureAppAllowlistState(
packageName, permissionName)
packageState.isProduct ->
apexModuleName?.let {
permissionAllowlist.getApexSignatureAppAllowlistState(
packageName, permissionName)
} ?: permissionAllowlist.getProductSignatureAppAllowlistState(
packageName, permissionName)
packageState.isSystemExt ->
permissionAllowlist.getSystemExtSignatureAppAllowlistState(
packageName, permissionName)
else ->
permissionAllowlist.getApexSignatureAppAllowlistState(
packageName, permissionName)
?: permissionAllowlist.getProductSignatureAppAllowlistState(
packageName, permissionName)
?: permissionAllowlist.getVendorSignatureAppAllowlistState(
packageName, permissionName)
?: permissionAllowlist.getSystemExtSignatureAppAllowlistState(
packageName, permissionName)
?: permissionAllowlist.getSignatureAppAllowlistState(
packageName, permissionName)
}vendor/odm 和 system_ext 使用固定分区表;product 中的 APEX 包优先使用 APEX 表。其他包按 APEX、product、vendor、system_ext、system 顺序回退。这个顺序是源码定义的优先级,不能用“最后一个文件覆盖前一个文件”的直觉替代。
9. OEM 保护标志
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
permission.isOem -> {
if (packageState.isOem) {
val allowlistState =
newState.externalState.permissionAllowlist
.getOemAppAllowlistState(packageName, permissionName)
checkNotNull(allowlistState) {
"OEM permission $permissionName for package $packageName " +
"must be explicitly declared"
}
return allowlistState
}
}OEM permission 的语义更严格:包必须被识别为 OEM 包,而且配置必须存在;null 会触发 checkNotNull,不是宽松地继续。配置为 FALSE 时返回明确拒绝。它与 privileged allowlist 的“启动阶段记录违规”策略不同,调试时要看清 permission 的 protection flag。
10. 查询和诊断路径
Android 17 的 PackageManagerShellCommand 暴露了读取白名单的只读命令。源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
private int runGetPrivappPermissions() {
final String pkg = getNextArg();
if (pkg == null) {
getErrPrintWriter().println("Error: no package specified.");
return 1;
}
getOutPrintWriter().println(getPrivAppPermissionsString(pkg, true));
return 0;
}
private int runGetPrivappDenyPermissions() {
final String pkg = getNextArg();
if (pkg == null) {
getErrPrintWriter().println("Error: no package specified.");
return 1;
}
getOutPrintWriter().println(getPrivAppPermissionsString(pkg, false));
return 0;
}对应的帮助文本包含 get-signature-permission-allowlist PARTITION,用于输出 system、vendor、product、system-ext 或 apex 表中的签名权限。特权权限查询还会先识别 APK 所属 APEX,再按 vendor/product/system_ext/system 选择表;因此诊断结果应与包实际路径对应,不能只 grep 一个 XML 文件。
private int runGetOemPermissions() {
final String pkg = getNextArg();
if (pkg == null) {
getErrPrintWriter().println("Error: no package specified.");
return 1;
}
final Map<String, Boolean> oemPermissions = SystemConfig.getInstance()
.getPermissionAllowlist().getOemAppAllowlist().get(pkg);
if (oemPermissions == null || oemPermissions.isEmpty()) {
getOutPrintWriter().println("{}");
} else {
oemPermissions.forEach((permission, granted) ->
getOutPrintWriter().println(permission + " granted:" + granted));
}
return 0;
}缺少 package 参数返回错误码 1;没有配置返回 {}。这两个结果要区分:前者是命令输入错误,后者是查询成功但 map 中没有该包。
11. 排查清单
遇到“特权权限没有授予”时,按源码顺序核对:
- 包是否真的位于 system/priv-app、vendor/priv-app、product/priv-app、system_ext/priv-app 或某个 APEX;
packageState的分区字段决定查询哪张表。 - permission 的 protection level 是否是 privileged、signature 或 OEM;不同分支使用不同白名单和失败策略。
- XML 是否写在允许的分区目录,
package、name是否非空;解析 warning 不会生成配置。 - 查询结果是
TRUE、FALSE还是null;只有null才会继续走启动阶段和构建类型逻辑。 - 包是否为 updated system app;若是,检查 disabled system 原包是否请求该权限,不能只看
/data更新包 manifest。 - 包是否在 updated APEX 中;APEX 表优先,system image 上的旧配置会产生迁移警告。
- 构建是否设置
CONTROL_PRIVAPP_PERMISSIONS_ENFORCE,以及系统是否已经isSystemReady;这决定缺失配置是记录还是拒绝。
12. 源码阅读路线
建议按以下顺序在 Android 17 源码中跳转:
SystemConfig.readPermissionsFromXml:确认文件路径如何映射到privapp-permissions、signature-permissions和oem-permissions。SystemConfig.readPrivappPermissions、readSignaturePermissions、readPermissionAllowlist:确认分区 map、XML 标签和三值写入。PermissionAllowlist的字段与get*AllowlistState:确认普通分区和 APEX 的 key 层级。AccessCheckingService.initialize:确认白名单进入externalState的时机和 owner。AppIdPermissionPolicy.checkPrivilegedPermissionAllowlistIfNeeded、getPrivilegedPermissionAllowlistState:确认 privileged 的豁免、enforce 和 APEX 回退。AppIdPermissionPolicy.shouldGrantPermissionBySignature、getSignaturePermissionAllowlistState:确认签名能力检查和平台签名权限的 allowlist。PackageManagerShellCommand.runGetPrivappPermissions、runGetSignaturePermissionAllowlist、runGetOemPermissions:把内存中的最终 map 与设备诊断输出对应起来。
白名单的核心不是某个 XML 示例,而是“来源分区 -> 三值状态 -> 权限策略 -> permission flags”的闭环。掌握这条链后,才能解释为什么同一个权限在 userdebug、user、更新系统包和 APEX 场景下会得到不同结果。
