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
设置的权限模式通常具有持久性,即使应用或设备重启,权限设置也会被保留。 除非用户在系统设置中手动修改应用的权限,或应用被卸载,否则权限设置将持续有效。