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

在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的签名检查》

你可能感兴趣的:(android,解决方法)