特殊权限
本文承接 权限类型、权限检查 和 AppOps 与权限。特殊权限不是一组统一的 requestPermissions() 弹窗权限;它们通常以 app-op protection flag、专用 Settings 页面、AppOps mode 或服务端策略组合实现。本文以 Android 17 的 SYSTEM_ALERT_WINDOW、WRITE_SETTINGS、REQUEST_INSTALL_PACKAGES、MANAGE_EXTERNAL_STORAGE 为例,追踪“Manifest 声明 → Settings 开关 → AppOps → Window/Settings/PackageInstaller/Storage 服务”的真实边界。
1. 特殊权限模型
1.1 不同 owner
| 能力 | Manifest permission | 主要 AppOp | 最终消费者 |
|---|---|---|---|
| 悬浮窗 | SYSTEM_ALERT_WINDOW | OP_SYSTEM_ALERT_WINDOW | WindowManagerService |
| 修改设置 | WRITE_SETTINGS | OP_WRITE_SETTINGS | Settings provider/调用 API |
| 未知来源安装 | REQUEST_INSTALL_PACKAGES | OP_REQUEST_INSTALL_PACKAGES | PackageInstaller/PMS |
| 全文件访问 | MANAGE_EXTERNAL_STORAGE | OP_MANAGE_EXTERNAL_STORAGE | 存储服务 |
Manifest 只声明资格;用户在 Settings 中改变 AppOps mode;最终服务在真正操作时再次检查。不同能力的 AppOp 可能按 package 或 UID 保存,不能假设所有特殊权限使用同一个设置 API。
2. AppOps 映射
2.1 操作常量
源码文件:frameworks/base/core/java/android/app/AppOpsManager.java
public static final int OP_WRITE_SETTINGS =
AppOpEnums.APP_OP_WRITE_SETTINGS;
public static final int OP_SYSTEM_ALERT_WINDOW =
AppOpEnums.APP_OP_SYSTEM_ALERT_WINDOW;
public static final int OP_REQUEST_INSTALL_PACKAGES =
AppOpEnums.APP_OP_REQUEST_INSTALL_PACKAGES;
public static final int OP_MANAGE_EXTERNAL_STORAGE =
AppOpEnums.APP_OP_MANAGE_EXTERNAL_STORAGE;操作码来自生成的 AppOpEnums,不能把旧版本数值硬编码进文章或测试。permissionToOpCode 是稳定的映射入口。
2.2 映射函数
public static int permissionToOpCode(String permission) {
final Integer boxedOpCode = sPermToOp.get(permission);
if (boxedOpCode != null) {
return boxedOpCode;
}
if (permission != null &&
HealthConnectManager.isHealthPermission(
ActivityThread.currentApplication(), permission)) {
return OP_READ_WRITE_HEALTH_DATA;
}
return OP_NONE;
}无映射返回 OP_NONE,不代表特殊权限自动允许;调用者必须根据 permission protection 决定是否继续 hard permission 检查。Health permission 有额外动态映射。
3. 悬浮窗
3.1 Context 便捷 API
源码文件:frameworks/base/core/java/android/provider/Settings.java
public static boolean canDrawOverlays(Context context) {
return Settings.isCallingPackageAllowedToDrawOverlays(
context, Process.myUid(),
context.getOpPackageName(), false)
|| context.checkSelfPermission(
Manifest.permission.SYSTEM_APPLICATION_OVERLAY)
== PackageManager.PERMISSION_GRANTED;
}canDrawOverlays 不只检查 SYSTEM_ALERT_WINDOW permission flags,还调用 Settings 内部的 AppOps/策略判断;system application overlay permission 是独立的系统能力。应用调用该便捷 API 不能推断 WindowManager 一定接受任意窗口类型。
3.2 Settings 页面
源码文件:frameworks/base/core/java/android/provider/Settings.java
public static final String ACTION_MANAGE_OVERLAY_PERMISSION =
"android.settings.action.MANAGE_OVERLAY_PERMISSION";
public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION =
"android.settings.MANAGE_APP_OVERLAY_PERMISSION";普通应用可使用 ACTION_MANAGE_OVERLAY_PERMISSION 引导用户;指定包的受保护 action 需要 INTERNAL_SYSTEM_WINDOW。Settings 页面改变的是 AppOps mode,不是 Manifest 或 permission definition。
3.3 窗口消费
WindowManagerService 在添加 TYPE_APPLICATION_OVERLAY 窗口时再次验证 caller UID/package 和 OP_SYSTEM_ALERT_WINDOW。因此 Settings 开关、便捷 API 返回值和最终 addWindow 结果是三次可能不同的观察点;设备策略、用户限制和 package visibility 都可能让最终调用失败。
4. 修改系统设置
4.1 canWrite
源码文件:frameworks/base/core/java/android/provider/Settings.java
public static boolean canWrite(Context context) {
return isCallingPackageAllowedToWriteSettings(
context, Process.myUid(),
context.getOpPackageName(), false);
}Settings.System.canWrite 通过 Settings 内部检查包名、UID 和 OP_WRITE_SETTINGS;仅在 Manifest 声明 WRITE_SETTINGS 仍不够,用户必须在 ACTION_MANAGE_WRITE_SETTINGS 页面授予能力。
4.2 AppOp 默认模式
源码文件:frameworks/base/core/java/android/app/AppOpsManager.java
new AppOpInfo.Builder(OP_WRITE_SETTINGS,
OPSTR_WRITE_SETTINGS, "WRITE_SETTINGS",
AppOpsManager.MODE_ERRORED)
.setPermission(android.Manifest.permission.WRITE_SETTINGS)
.build();WRITE_SETTINGS 的 AppOp 默认 mode 是 errored;Settings 用户开关或受信系统策略必须显式改变 mode。MODE_DEFAULT 的实际处理由 AppOps/PermissionChecker 继续回退到 permission 检查。
5. 未知来源安装
5.1 Settings action
public static final String ACTION_MANAGE_UNKNOWN_APP_SOURCES =
"android.settings.MANAGE_UNKNOWN_APP_SOURCES";应用通过该 action 引导用户为特定 package 开启安装未知来源能力;PackageInstaller 在创建/提交 session 时还会检查 installer identity、用户确认和设备策略。REQUEST_INSTALL_PACKAGES 的 AppOp 默认也是 errored。
5.2 AppOp 定义
new AppOpInfo.Builder(OP_REQUEST_INSTALL_PACKAGES,
OPSTR_REQUEST_INSTALL_PACKAGES,
"REQUEST_INSTALL_PACKAGES", MODE_ERRORED)
.setSwitchCode(OP_REQUEST_INSTALL_PACKAGES)
.setPermission(
android.Manifest.permission.REQUEST_INSTALL_PACKAGES)
.build();该操作属于 package-level app-op permission 列表,用户开关针对 package/UID 组合保存。即使 AppOp allowed,PMS 仍会检查 session 来源和安装权限。
6. 全文件访问
6.1 AppOp 定义
new AppOpInfo.Builder(OP_MANAGE_EXTERNAL_STORAGE,
OPSTR_MANAGE_EXTERNAL_STORAGE,
"MANAGE_EXTERNAL_STORAGE", MODE_ERRORED)
.setPermission(
android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
.build();MANAGE_EXTERNAL_STORAGE 使用 AppOp 表达用户授予的全文件访问能力。实际存储 API 还会结合 target SDK、分区存储、文件路径和 MediaProvider 策略;AppOps allowed 不是任意路径都可写的保证。
6.2 权限检查层
PermissionChecker 对带 app-op protection 的权限先映射 op,再执行 operation transaction;MODE_IGNORED 或 MODE_ERRORED 会阻止或降级,MODE_DEFAULT 才回退到普通 permission check。存储服务可能把 soft denied 转成空结果或受限路径。
7. 失败边界
7.1 非弹窗路径
特殊权限通常不由 Activity.requestPermissions 弹窗授予。即使 permission 是 app-op 类型,普通 runtime grant API 也不等价于 Settings 专用开关;调用方应使用对应 Settings action 或专用 manager API。
7.2 用户与策略
AppOps mode 按 package/UID/user 保存。用户限制、设备所有者、系统默认策略和 role 可能覆盖用户开关;检查时必须使用正确 userId 与 op package name。
7.3 权限与 op
permission flags granted、AppOps errored 是合法状态。PMS/PermissionChecker 可能返回 permission granted,但 WindowManager、Settings provider、PackageInstaller 或存储服务仍拒绝操作。
8. 验证方法
8.1 悬浮窗
声明 SYSTEM_ALERT_WINDOW,在 Settings 页面切换开关,分别调用 Settings.canDrawOverlays 和添加 TYPE_APPLICATION_OVERLAY 窗口。对照 AppOps mode allowed/ignored/errored,确认最终服务检查独立存在。
8.2 修改设置与安装
声明 WRITE_SETTINGS、REQUEST_INSTALL_PACKAGES,分别使用对应 Settings action 开关,再执行真实 API/PackageInstaller session。关闭开关时应得到拒绝;仅保留 Manifest 声明不能通过。
8.3 全文件访问
在允许和拒绝 MANAGE_EXTERNAL_STORAGE 的 user 下访问受保护路径,比较 AppOps 与存储服务结果。确认 mode allowed 仍受路径和 target SDK 限制。
adb shell appops get <package.name>
adb shell appops set <package.name> SYSTEM_ALERT_WINDOW allow
adb shell appops set <package.name> WRITE_SETTINGS errored
adb shell dumpsys package <package.name>9. 源码路线
AppOpsManager的 op 常量与AppOpInfo:确认特殊权限映射和默认 mode。Settings.canDrawOverlays/canWrite:查看便捷 API 的额外策略。PermissionCheckerService.checkAppOpPermission/checkRuntimePermission:理解 mode 与 permission 的组合。WindowManagerService:追踪悬浮窗最终消费。- PackageInstaller 未知来源检查:连接
REQUEST_INSTALL_PACKAGES与安装 session。 - Storage/MediaProvider:继续阅读
MANAGE_EXTERNAL_STORAGE的资源服务策略。
特殊权限的本质是把“是否拥有 permission 资格”和“这一次具体操作是否允许”拆开:Settings 或专用策略写入 AppOps mode,PermissionChecker 组合 permission 与 op,最终资源服务再执行自己的用户、路径和前台约束。遇到悬浮窗、修改设置、未知来源安装或全文件访问问题时,必须同时检查 Manifest、permission flags、AppOps mode、user/package identity 和最终服务入口。
