Android 15 AOSP 定制:应用安装后自动授予 SYSTEM_ALERT_WINDOW 权限
解决方案:修改 PackageManagerService.java
目标是在应用安装完成后,立即授予指定的应用 SYSTEM_ALERT_WINDOW 权限。Android 系统中,应用安装流程的核心管理类是 PackageManagerService。
1. 定位安装完成回调
在 PackageManagerService.java 中,需要找到应用安装完成后的回调方法。经过源码分析,finishPackageInstall 方法在应用安装流程的末尾被调用,标志着应用安装过程的完成。
void finishPackageInstall(int token, boolean didLaunch) {
PackageManagerServiceUtils.enforceSystemOrRoot(
"Only the system is allowed to finish installs");
if (PackageManagerService.DEBUG_INSTALL) {
Slog.v(PackageManagerService.TAG, "BM finishing package install for " + token);
}
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
final Message msg = mHandler.obtainMessage(PackageManagerService.POST_INSTALL, token,
didLaunch ? 1 : 0);
mHandler.sendMessage(msg);
grantAppOpsPermission();//add by lzerong 2025/4/8 for 54445 Grant dangerous permissions after app installation.
}
在 finishPackageInstall 方法的末尾,添加了 grantAppOpsPermission() 方法的调用。grantAppOpsPermission() 方法是实现自动授予权限的核心逻辑所在。
2. 实现 grantAppOpsPermission 方法
接下来,需要实现 grantAppOpsPermission 方法。此方法的主要任务是:
- 遍历当前正在进行的安装请求 (
mRunningInstalls),找到安装成功的应用。 - 判断安装成功的应用是否在预设的需要自动授权的应用列表中。
- 如果是,则通过
AppOpsManager的setMode方法,授予该应用SYSTEM_ALERT_WINDOW权限。
以下是 grantAppOpsPermission 方法的具体实现代码:
/**
* add by lzerong 2025/4/8
* Grant dangerous permissions after app installation.
*/
private void grantAppOpsPermission(){
//Log.e("lzr", "mRunningInstalls.size():" + mRunningInstalls.size());
for (int i = 0; i < mRunningInstalls.size(); i++) {
final InstallRequest installRequest = mRunningInstalls.valueAt(i);
if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
continue;
}
Log.e("lzr", "finishPackageInstall:" + installRequest.getPkg().getPackageName());
AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
PackageManager pm = mContext.getPackageManager();
final String []itemStringExt = new String[]{"com.tencent.mm","com.tencent.mobileqq"}; // 预设需要授权的应用包名列表
for (int j = 0; j < itemStringExt.length; j++) {
if(installRequest.getPkg().getPackageName().equals(itemStringExt[j])){ // 判断是否在列表中
try {
PackageInfo packageInfo = pm.getPackageInfo(itemStringExt[j], // 获取应用信息
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES |
PackageManager.GET_SIGNATURES);
appOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, // 设置 AppOps 权限模式
packageInfo.applicationInfo.uid, itemStringExt[j], AppOpsManager.MODE_ALLOWED); // 授予 SYSTEM_ALERT_WINDOW 权限,模式为 ALLOWED
} catch (Exception e) {
Log.e("lzr", "Exception when retrieving package:", e); // 异常处理
}
}
}
}
}
代码逻辑详解:
for (int i = 0; i < mRunningInstalls.size(); i++): 遍历mRunningInstalls列表,此列表维护了当前正在进行的安装请求。final InstallRequest installRequest = mRunningInstalls.valueAt(i);: 获取当前的安装请求对象。if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED): 判断当前安装请求是否成功,仅有安装成功的应用才需要进行后续的权限授予操作。AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);: 获取AppOpsManager系统服务。AppOpsManager是 Android 系统中负责管理应用操作权限的核心类。PackageManager pm = mContext.getPackageManager();: 获取PackageManager系统服务,用于获取应用信息。final String []itemStringExt = new String[]{"com.tencent.mm","com.tencent.mobileqq"};: 定义字符串数组itemStringExt,用于存储需要自动授予SYSTEM_ALERT_WINDOW权限的应用包名。 示例性地添加了 "com.tencent.mm" (微信) 和 "com.tencent.mobileqq" (QQ) 的包名。 实际应用中,应根据需求修改此列表。for (int j = 0; j < itemStringExt.length; j++): 遍历itemStringExt列表,检查当前安装的应用包名是否在列表中。if(installRequest.getPkg().getPackageName().equals(itemStringExt[j])): 判断当前安装的应用包名是否与列表中的包名匹配。PackageInfo packageInfo = pm.getPackageInfo(itemStringExt[j], ...);: 如果包名匹配,则通过PackageManager的getPackageInfo方法获取应用的PackageInfo对象。 使用PackageManager.GET_DISABLED_COMPONENTS | PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_SIGNATURES参数,确保能够获取到完整的应用信息。appOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, packageInfo.applicationInfo.uid, itemStringExt[j], AppOpsManager.MODE_ALLOWED);: 核心代码。 调用AppOpsManager的setMode方法来设置应用的SYSTEM_ALERT_WINDOW权限模式。AppOpsManager.OP_SYSTEM_ALERT_WINDOW: 指定要设置的 AppOps 操作类型,即 "显示在其他应用的上层" 权限。packageInfo.applicationInfo.uid: 应用的 UID (User ID),用于唯一标识应用的用户和进程。itemStringExt[j]: 应用的包名。AppOpsManager.MODE_ALLOWED: 设置权限模式为ALLOWED,表示允许该应用执行SYSTEM_ALERT_WINDOW操作。
catch (Exception e): 捕获可能发生的异常,例如PackageManager.NameNotFoundException(如果包名不存在) 等,并打印错误日志。
深入理解 AppOpsManager.setMode
AppOpsManager 是 Android 系统中管理应用操作权限的系统服务。 setMode 方法是 AppOpsManager 的关键方法之一,它允许系统服务 (如 PackageManagerService) 以编程方式设置特定应用的特定操作权限模式。
setMode 方法签名:
public void setMode(int code, int uid, String packageName, int mode)
参数详解:
int code: AppOps 操作码,定义了要控制的具体操作类型。 例如,AppOpsManager.OP_SYSTEM_ALERT_WINDOW代表 "显示在其他应用的上层" 权限,AppOpsManager.OP_CAMERA代表相机权限等。 完整的操作码列表可参考AppOpsManager类的常量定义。int uid: 目标应用的 UID (User ID)。 UID 是 Android 系统用于标识用户和进程的唯一 ID。 通过 UID,AppOpsManager可以精确定位到需要设置权限的应用。String packageName: 目标应用的包名。 包名是应用的唯一标识符。int mode: 要设置的权限模式。AppOpsManager定义了多种权限模式,常用的包括:AppOpsManager.MODE_ALLOWED: 允许操作。AppOpsManager.MODE_IGNORED: 拒绝操作,并静默忽略请求。AppOpsManager.MODE_DENIED: 拒绝操作,并抛出SecurityException异常。AppOpsManager.MODE_DEFAULT: 使用系统默认模式 (通常由权限策略和用户设置决定)。
在此例中,AppOpsManager.MODE_ALLOWED 被使用,目的是在应用安装完成后,直接授予目标应用 SYSTEM_ALERT_WINDOW 权限。
权限模式的持久性:
通过 AppOpsManager.setMode 设置的权限模式通常具有持久性,即使应用或设备重启,权限设置也会被保留。 除非用户在系统设置中手动修改应用的权限,或应用被卸载,否则权限设置将持续有效。