Launcher中动态加载APK出现java.lang.SecurityException异常的解决方法(二)

在动态加载APK至Launcher中时遇到java.lang.SecurityException异常,通过分析Android源码发现原因在于发起请求的应用进程与要注册的receiver所在进程不一致。解决方法包括使发起调用的进程为系统进程或将其动态加载的APK包名添加至发起进程的pkgList中。具体步骤包括获取ProcessRecord对象并调用其addPackage方法。最终,通过注释相关代码并替换services.jar文件解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Launcher中动态加载APK,之前有出现过java.lang.SecurityException的异常,

具体的异常信息如下:

09-05 19:05:55.033: E/AndroidRuntime(28637): java.lang.SecurityExceptionGiven caller package com.zhao3546.time is not running in process ProcessRecord{41e74e50 28637:com.zhao3546.launcher/u0a10142} 

解决过程参见这个文档:《某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException异常的解决方法》。

 

这两天我们基于Baidu的APK实现了一个简单的地图应用,在将其动态加载到Launcher中时,发现也遇到了同样的问题,之前的解决虽然可以解决此异常,

但是Baidu地图无法正常显示,看来只能尝试如何解决此异常了。

 

再重新根据“Given caller package” 关键字,搜索Android的源码,在 frameworks\base\services\java\com\android\server\am 下的 ActivityManagerService.java 中找到了这个异常出现的位置:

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        enforceNotIsolatedCaller("registerReceiver");
        int callingUid;
        int callingPid;
        synchronized(this) {
            ProcessRecord callerApp = null;
            if (caller != null) {
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                }
                if (callerApp.info.uid != Process.SYSTEM_UID &&
                        !callerApp.pkgList.contains(callerPackage)) {
------------        throw new SecurityException("Given caller package " + callerPackage
------------                + " is not running in process " + callerApp);
                }
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }


根据代码分析,是因为发起此请求的应用所在的进程不是系统进程,并且此进程的包名列表中,并不包含要注册的receiver对应的package名称。

根据此条件“callerApp.info.uid != Process.SYSTEM_UID && !callerApp.pkgList.contains(callerPackage)” ,要解决此问题,可以有两个方法:

1、发起调用方的进程是系统进程;

2、将动态加载的APK的包名发起此请求的进程的pkgList中;

第一个解决方法,对于普通应用来说,基本上实现不了;第二个解决方法,可以进一步研究Android源码找一下解决方法。

callerApp这个对象的类型是ProcessRecord类型的,此类中有如下方法:

    /*
     *  Return true if package has been added false if not
     */
    public boolean addPackage(String pkg) {
        if (!pkgList.contains(pkg)) {
            pkgList.add(pkg);
            return true;
        }
        return false;
    }

 

只要我们在应用代码中,能获取到ProcessRecord对象即可,ActivityManagerService类有如下方法可以获取到ProcessRecord,但是package的方法,

    final ProcessRecord getRecordForAppLocked(
            IApplicationThread thread) {
        if (thread == null) {
            return null;
        }

        int appIndex = getLRURecordIndexForAppLocked(thread);
        return appIndex >= 0 ? mLruProcesses.get(appIndex) : null;
    }


ActivityManagerService类的继承关系如下:

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback

public abstract class ActivityManagerNative extends Binder implements IActivityManager


应用层要访问ActivityManagerService,必须要通过 ActivityManagerNative.getDefault() 来进行,得到的是一个IActivityManager的对象。

这个对象本身并不对外暴露getRecordForAppLocked()方法,即使暴露,而ProcessRecord类也是Package级别的类,只有同一个包下的类才能访问得到。

看来要走这条路,要对原生代码要做很大的改动才行。

 

比如Activity.java中要启动一个Activity,可以使用startActivityForResult方法,

    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);

 

然后又调用了Instrumentation的execStartActivity()方法,

    public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Fragment target,
        ......
        try {
            intent.setAllowFds(false);
            intent.migrateExtraStreamToClipData();
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mWho : null,
                        requestCode, 0, null, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
        }
        return null;
    


既然如下,要让此异常不出现,必须要修改framework的代码了,那直接直接注释掉如下代码看看效果:

/*                if (callerApp.info.uid != Process.SYSTEM_UID &&
                        !callerApp.pkgList.contains(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                } */

注释此段代码后,在Android代码的源码目录下,执行如下命令,编译出services.jar

mmm frameworks/base/services/java/


使用此jar替换 /system/framework 下的同名文件,重启Android,再测试,问题解决。

注意:services.jar是系统级的文件,要正常替换,请参考:《做自己的Android ROM,屏蔽对framework中的系统APK的签名检查

E FATAL EXCEPTION: main (Ask Gemini) Process: com.android.packageinstaller, PID: 6559 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.packageinstaller/com.android.packageinstaller.InstallStart}: java.lang.SecurityException: UID 10071 does not have permission to content://com.hh.launcher.fileprovider/external_files/IReader.apk [user 0] at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3431) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7660) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) Caused by: java.lang.SecurityException: UID 10071 does not have permission to content://com.hh.launcher.fileprovider/external_files/IReader.apk [user 0] at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) at android.os.Parcel.createException(Parcel.java:2357) at android.os.Parcel.readException(Parcel.java:2340) at android.os.Parcel.readException(Parcel.java:2282) at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:3696) at android.app.Instrumentation.execStartActivity(Instrumentation.java:1723) at android.app.Activity.startActivityForResult(Activity.java:5403) at android.app.Activity.startActivityForResult(Activity.java:5360) at android.app.Activity.startActivity(Activity.java:5750) at android.app.Activity.startActivity(Activity.java:5703) at com.android.packageinstaller.InstallStart.onCreate(InstallStart.java:150) at android.app.Activity.performCreate(Activity.java:8110) at android.app.Activity.performCreate(Activity.java:8075) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3404) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)  at android.os.Handler.dispatchMessage(Handler.java:106)  at android.os.Looper.loop(Looper.java:223)  at android.app.ActivityThread.main(ActivityThread.java:7660)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)  Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.uri.UriGrantsManagerService.checkGrantUriPermissionUnlocked(UriGrantsManagerService.java:1285) at com.android.server.uri.UriGrantsManagerService.checkGrantUriPermissionFromIntentUnlocked(UriGrantsManagerService.java:624) at com.android.server.uri.UriGrantsManagerService.access$1000(UriGrantsManagerService.java:117) at com.android.server.uri.UriGrantsManagerService$LocalService.checkGrantUriPermissionFromIntent(UriGrantsManagerService.java:1442) at com.android.server.wm.ActivityStarter$Request.resolveActivity(ActivityStarter.java:527)
06-30
E AndroidRuntime: FATAL EXCEPTION: IntentService[InstallService] 07-23 20:02:09.374 3567 5161 E AndroidRuntime: Process: com.android.settings, PID: 3567 07-23 20:02:09.374 3567 5161 E AndroidRuntime: java.lang.IllegalArgumentException: Couldn't find meta-data for provider with authority cm.komect.aqb.android.launcher.fileprovider 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at androidx.core.content.FileProvider.getFileProviderPathsMetaData(FileProvider.java:664) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at androidx.core.content.FileProvider.parsePathStrategy(FileProvider.java:695) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at androidx.core.content.FileProvider.getPathStrategy(FileProvider.java:645) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:449) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at com.android.settings.hraservice.InstallService.onHandleIntent(InstallService.java:83) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:78) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:201) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at android.os.Looper.loop(Looper.java:288) 07-23 20:02:09.374 3567 5161 E AndroidRuntime: at android.os.HandlerThread.run(HandlerThread.java:67)
最新发布
07-24
### Java 中因非法包名 `java.com.swufe` 导致的 SecurityException 解决方案 当使用包名为 `java.com.swufe` 的情况下,程序可能抛出 `java.lang.SecurityException: Prohibited package name: java.com.swufe` 异常。这是因为 Java 虚拟机 (JVM) 对以 `java.` 开头的一级包名进行了严格的安全检查[^2]。 #### 原因分析 Java加载(`ClassLoader`) 在预定义阶段会对类的全限定名称进行验证。如果发现该类属于保留命名空间(即以 `java.` 或其他受保护前缀开头),则会立即抛出 `SecurityException` 异常。这是为了防止第三方开发者意外或恶意覆盖核心 JDK 类的行为。 #### 解决策略 要解决此问题,最简单的方法是更改包名结构,避免使用任何以 `java.` 开始的命名约定。推荐遵循标准的反向域名命名法来创建唯一的包名,例如: ```java package com.swufe.yourproject; ``` 通过这种方式可以有效规避 JVM 安全机制触发异常的情况。 #### 正确代码示例 以下是调整后的代码片段展示如何重新设计包名并正常编译运行: ```java // 修改之前的错误包声明 // package java.com.swufe; // 修改之后的标准包声明 package com.swufe.javaexample; public class Main { public static void main(String[] args) { System.out.println("Package name corrected, program runs successfully."); } } ``` #### 额外注意事项 除了更改编码中的包名之外,在部署环境中还需要确认以下几点: - **环境变量配置**:确保项目依赖的所有本地库路径被正确设置于系统的 `java.library.path` 属性中[^4]。 可打印当前属性值用于调试目的: ```java System.out.println(System.getProperty("java.library.path")); ``` - **权限管理**:对于某些敏感操作,需赋予足够的访问许可给应用进程以防发生额外的安全例外情况[^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值