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),找到安装成功的应用。
  • 判断安装成功的应用是否在预设的需要自动授权的应用列表中。
  • 如果是,则通过 AppOpsManagersetMode 方法,授予该应用 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], ...);: 如果包名匹配,则通过 PackageManagergetPackageInfo 方法获取应用的 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);: 核心代码。 调用 AppOpsManagersetMode 方法来设置应用的 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 设置的权限模式通常具有持久性,即使应用或设备重启,权限设置也会被保留。 除非用户在系统设置中手动修改应用的权限,或应用被卸载,否则权限设置将持续有效。

95~我想带你去海边