某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException错误的解决办法

某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException异常的解决方法

在某APK中,通过如下方法动态注册了一个BroadcastReceiver,代码参考如下:

    @Override
    protected void onAttachedToWindow()
    {
        super.onAttachedToWindow();
        
        /* monitor time ticks, time changed, timezone */
        
        if (mIntentReceiver == null)
        {
            mIntentReceiver = new TimeChangedReceiver(this);
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_TIME_TICK);
            filter.addAction(Intent.ACTION_TIME_CHANGED);
            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
            getContext().registerReceiver(mIntentReceiver, filter);
Launcher要动态加载此APK(动态加载APK的目的和实现思路,参见我的这个文章:Launcher中动态加载其它APK中Activity的问题解决思路),

出现了如下异常:

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

搜索了一下,很多人遇到过此类问题,但都没有提出解决方法,看来只能自己动手解决了。

动态加载不行,那在AndroidManifest.xml试试静态加载BroadcastReceiver是否可以?

如果是其它的BroadcastRecevier,验证了是ok的,但android.content.Intent.ACTION_TIME_TICK这个不行,Android的注释中已经明确说明了。

String android.content.Intent.ACTION_TIME_TICK = "android.intent.action.TIME_TICK"

Broadcast Action: The current time has changed. Sent every minute. You can not receive this through components declared in manifests, only by exlicitly registering for it with Context.registerReceiver(). 

This is a protected intent that can only be sent by the system.

难道就真的没有解决方法了?

手头有一份Android源码,我根据“Given caller package”和“is not running in process ProcessRecord”这两个关键字全文搜索,

想看看抛出这个异常的代码是在哪里搞出来的,在*.cpp,*.c*,*.java中搜索都没有找到,先放弃此方法吧。

 

回头再来分析此异常,

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

我是在第三方APK中注册的BroadcastReceiver,这个APK在AndroidManifest.xml中声明的包名正好是“com.zhao3546.time”,而最终是和Launcher运行在同一个进程中的,这个进程对应的包名是“com.zhao3546.launcher”,因为Launcher先运行,而APK是被动态加载进来的。

那是不是只要我注册BroadcastReceiver对应的包名只要是“com.zhao3546.launcher”,就可以解决问题了?

Launcher中动态加载其它APK中Activity的问题解决思路,此文中已经通过将Launcher和第三方APK的AndroidManifest.xml中android:sharedUserId属性声明成为一致了,

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zhao3546.time"
    android:sharedUserId="zhao3546.launcher"

所以Launcher和第三方APK实际上是运行在同一个进程中的,那只要将Launcher的context对象传给这个APK,这个APK使用Launcher的context去注册BroadcastReceiver,是不是就可以解决问题了?

第三方APK的com.zhao3546.time包下新增如下类,并实现有public static void setContext(Context context)这个静态方法:

package com.zhao3546.time;

import android.content.Context;

public class ContextHolder
{
    private static Context context = null;
    
    public static void setContext(Context context)
    {
        System.out.println("com.zhao3546.time.ContextHolder setContext");
        
        ContextHolder.context = context;
    }
    
    public static Context getContext()
    {
        System.out.println("com.zhao3546.time.ContextHolder getContext() context="
                + context);
        
        return context;
    }
}

在Launcher代码中,通过反射机制动态地将launcher的context对象传到 com.zhao3546.time.ContextHolder 中,

    private void setContext2Plugin(ClassLoader pluginClassLoader,
            String pluginPackageName, Context launcherContext)
    {
        String className = pluginPackageName + ".ContextHolder";
        try
        {
            Method m = pluginClassLoader.loadClass(className)
                    .getMethod("setContext", Context.class);
            m.invoke(null, launcherContext);
        }
        catch (Exception e)
        {
            Log.w(TAG, "Fail to loadClass " + className + ", skip it", e);
        }
    }

修改之前注册BroadcastReceiver的地方,通过ContextHolder()来注册BroadcastReceiver,把APK重新部署验证,问题解决。

 

看似解决不了的问题,其实有时需要换个思路去尝试不同的方法,可能就会取得意想不到的效果。