(十五)Dex 加密之 Application 替换

版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、Application 创建流程

我们知道,当用户点击桌面上的 app 图标进行启动一个 app,是由 Zygote 进程 fork 一个子进程而来的。然后将 ActivityThread 加载到子进程中,这时候会调用 ActivityThread 中的 main 函数。我们这边从 ActivityThread 讲起。

ActivityThread 的 main

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");
        
        //准备了 Loop
        Looper.prepareMainLooper();

        //创建新的 ActivityThread,调用 attach 函数
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        //与上面对应,调用   Looper.loop()
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

ActivityThread 的 main 中调用了 ActivityThread 的 attach 方法,传递了一个 false 参数。

ActivityThread 的 attach

    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ......
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            //通过 ActivityManager 获取到一个 bundle 对象
            final IActivityManager mgr = ActivityManager.getService();
            try {
                //会调用到 
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
           ......
        } else {
          ......
        }
      ......
    }

ActivityManager.getService() 获取到的对象实际是一个 ActivityManagerProxy,调用了 ActivityManagerProxy 的 attachApplication 方法,通过 Bundle 的流程,最终会调到 ActivityManager 的 bindApplication 方法。

ActivityThread 的 bindApplication

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial) {
            ......
            sendMessage(H.BIND_APPLICATION, data);
        }

    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }


    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

    final H mH = new H();

在 ActivityThread 的 bindApplication 最后会给 mH 发送一个 H.BIND_APPLICATION 的消息, mH 是成员变量,直接初始化,是 ActivityThread 的内部类 H, H 继承自 Handler。查找到 H 中对对应消息的处理。

H 中对 BIND_APPLICATION 的处理

                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

H 中对 BIND_APPLICATION 的处理调用了 ActivityThread 的 handleBindApplication 这个方法,这个方法就是准备 Application 的,包括创建 Application 和创建过程中调用到 Application 中的各个方法。

ActivityThread 的 handleBindApplication

    private void handleBindApplication(AppBindData data) {
        ......

        // Allow disk access during application and provider setup. This could
        // block processing ordered broadcasts, but later processing would
        // probably end up doing the same disk access.
        Application app;
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            //创建 Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            //对 Application 进行赋值记录
            mInitialApplication = app;

            ......
            try {
                //调用 Application 的 onCreate 方法
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                            "Unable to create application " + app.getClass().getName()
                                    + ": " + e.toString(), e);
                }
            }
        ........
    }

我们先查看 app = data.info.makeApplication(data.restrictedBackupMode, null); 这个代码的实现, data.info 是一个 LoadedApk,查看 LoadedApk 的 makeApplication 方法。

LoadedApk 的 makeApplication

    public Application makeApplication(boolean forceDefaultAppClass,
                                       Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

        Application app = null;

        //获取 AndroidManifest.xml 中配置的 Application 全类名
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            
            //我们自己的应用肯定不满足这个 if 判断
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            //创建一个 Context
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //通过反射创建 Application,同时调用 Application 的 attachBaseContext 方法
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            //记录使用 app
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                        "Unable to instantiate application " + appClass
                                + ": " + e.toString(), e);
            }
        }
        //记录使用 app
        mActivityThread.mAllApplications.add(app);
        //记录使用 app
        mApplication = app;

	//这时候传进来的 instrumentation 是 null
        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                            "Unable to create application " + app.getClass().getName()
                                    + ": " + e.toString(), e);
                }
            }
        }

        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
        final int N = packageIdentifiers.size();
        for (int i = 0; i < N; i++) {
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
                continue;
            }

            rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
        }

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        return app;
    }

通过 app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext) 来创建 Application。
newApplication

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }
    
    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        //调用到 Application 的 attach 方法
        app.attach(context);
        return app;
    }

newApplication 调用到 Application 的 attach 方法。

Application 的 attach

    final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

Application 的 attach 中调用了 Application 的 attachBaseContext 方法。attachBaseContext 是在 Application 创建的时候进行调用。

在 LoadedApk 的 makeApplication 中,创建完 Application,有三个地方记录保存了新建的 Application。

        appContext.setOuterContext(app);
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

继续回到 ActivityThread 的 handleBindApplication 方法,往下把获取到的 Application 赋值给成员变量 mInitialApplication。

 mInitialApplication = app;

再继续往下,调用了 mInstrumentation.callApplicationOnCreate(app);

callApplicationOnCreate

    public void callApplicationOnCreate(Application app) {
        app.onCreate();
    }

callApplicationOnCreate 直接调用 Application 的 onCreate 方法。

总结 :由上面流程分析可以知道, Application 在初始化完之后有四个地方使用记录了这个 Application。

1.ActivityThread 的成员变量 mInitialApplication
ActivityThread 的 handleBindApplication 是直接对 ActivityThread 的成员变量 mInitialApplication 进行赋值。

2. ContextImpl 的成员变量 mOuterContext
LoadedApk 的 makeApplication 中 appContext.setOuterContext(app) 对 ContextImpl 的成员变量 mOuterContext 进行赋值。

    final void setOuterContext(Context context) {
        mOuterContext = context;
    }

3. ActivityThread 的成员变量 mAllApplications
LoadedApk 的 makeApplication 中 mActivityThread.mAllApplications.add(app) 直接调用 ActivityThread 的成员变量 mAllApplications,这是一个集合,直接把 Application 保存在 mAllApplications 集合中。

**4. LoadedApk 的成员变量 mApplication **
LoadedApk 的 makeApplication 中 mApplication = app 直接把 Application 赋值保存在 LoadedApk 的成员变量 mApplication 中。

二、分析

通过 Application 的创建流程,可以知道在创建出 Application 后,有 4 个地方保存了 Application 的实例对象,要想替换 Application,需要对这 4 个地方保存的对象进行修改。

接下来要考虑的是如何获取到这些要替换掉成员变量的对象(ActivityThread、ContextImpl 和 LoadedApk)

1.ContextImpl

我们先看一下 ContextImpl 这个对象,
LoadedApk 的 makeApplication:

    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
		......
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
		......
    }

由上面分析可以知道,LoadedApk 的 makeApplication 中,调用 mActivityThread.mInstrumentation.newApplication 通过反射创建 Application,同时调用 Application 的 attachBaseContext 方法。在这个过程中,attachBaseContext 获取的参数 Context 就是 ContextImpl 对象。

2.ActivityThread 和 LoadedApk

ActivityThread 和 LoadedApk 获取的方式比较多,这边介绍一个。
LoadedApk 的 makeApplication:

    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
		......
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
		......
    }

在创建 ContextImpl 对象的时候,传递了两个参数进去,一个是 mActivityThread,另一个是 this,也就是 LoadedApk 的实例对象。

点进去查看,很简单的代码, mActivityThread 与 LoadedApk 的实例对象分别保存在 ContextImpl 的成员变量 mMainThread 和 mPackageInfo。

3.ApplicationInfo 的 className

要替换一个 Application,至少在四大组件中获取的 Application、ApplicationContext 以及 ApplicationInfo 都要是新的 Application 中的信息。所以这还需要进行 ApplicationInfo 的 className 的替换。

:LoadedApk 的 makeApplication 中,先创建了 Application ,这时候调用了 Application 的 attachBaseContext 方法,然后再把 Application 的对象赋值给各个变量。所以,我们修改这些变量不能放在 attachBaseContext 方法中。

三、Application 的替换

ProxyApplication 是在 AndroidManifest.xml 中配置的 Application,真正的 Application 这边以参数的形势配置在
AndroidManifest.xml 中。

        <meta-data android:name="app_name" android:value="com.xiaoyue.replaceapplication.MyApplication"/>

ProxyApplication

public class ProxyApplication extends Application {

    private String app_name;

    private Application delegate;

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Log.e(TAG, "xiaoyue ProxyApplication attachBaseContext:");
        getMetaData();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "xiaoyue ProxyApplication onCreate:");

        try {
            bindRealApplication();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void bindRealApplication() throws Exception {

        //如果用户没有配置 Application 就不用管了
        if (TextUtils.isEmpty(app_name)) {
            return;
        }

        //这个就是attachBaseContext传进来的 ContextImpl
        Context baseContext = getBaseContext();

        //反射创建出真实的用户配置的 Application
        Class<?> delegateClass = Class.forName(app_name);
        delegate = (Application) delegateClass.newInstance();
        //反射获得 attach 函数
        Method attach = Application.class.getDeclaredMethod("attach", Context.class);
        //设置允许访问
        attach.setAccessible(true);
        attach.invoke(delegate, baseContext);

        /**
         *  替换 ContextImpl 的成员变量 mOuterContext
         */
        Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
        //获得 mOuterContext 属性
        Field mOuterContextField = contextImplClass.getDeclaredField("mOuterContext");
        mOuterContextField.setAccessible(true);
        mOuterContextField.set(baseContext, delegate);

        /**
         * 替换 ActivityThread 的成员变量 mInitialApplication 和 mAllApplications
         */
        //通过 ContextImpl 的成员变量 mMainThread 获得 ActivityThread 对象
        Field mMainThreadField = contextImplClass.getDeclaredField("mMainThread");
        mMainThreadField.setAccessible(true);
        Object mMainThread = mMainThreadField.get(baseContext);

        //替换 mInitialApplication
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Field mInitialApplicationField = activityThreadClass.getDeclaredField
                ("mInitialApplication");
        mInitialApplicationField.setAccessible(true);
        mInitialApplicationField.set(mMainThread, delegate);

        //替换 mAllApplications
        Field mAllApplicationsField = activityThreadClass.getDeclaredField
                ("mAllApplications");
        mAllApplicationsField.setAccessible(true);
        ArrayList<Application> mAllApplications = (ArrayList<Application>) mAllApplicationsField.get(mMainThread);
        mAllApplications.remove(this);
        mAllApplications.add(delegate);

        /**
         * 替换 LoadedApk 的成员变量 mApplication
         */
        //通过 ContextImpl 的成员变量 mPackageInfo 获得 LoadedApk 对象
        Field mPackageInfoField = contextImplClass.getDeclaredField("mPackageInfo");
        mPackageInfoField.setAccessible(true);
        Object mPackageInfo = mPackageInfoField.get(baseContext);

        Class<?> loadedApkClass = Class.forName("android.app.LoadedApk");
        Field mApplicationField = loadedApkClass.getDeclaredField("mApplication");
        mApplicationField.setAccessible(true);
        mApplicationField.set(mPackageInfo, delegate);

        //修改 ApplicationInfo 的 className
        Field mApplicationInfoField = loadedApkClass.getDeclaredField("mApplicationInfo");
        mApplicationInfoField.setAccessible(true);
        ApplicationInfo mApplicationInfo = (ApplicationInfo) mApplicationInfoField.get(mPackageInfo);
        mApplicationInfo.className = app_name;

        delegate.onCreate();
    }

    public void getMetaData() {
        try {
            ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo
                    (getPackageName(), PackageManager.GET_META_DATA);
            Bundle metaData = applicationInfo.metaData;
            //是否设置 app_name 与 app_version
            if (null != metaData) {
                //是否存在name为app_name的meta-data数据
                if (metaData.containsKey("app_name")) {
                    app_name = metaData.getString("app_name");
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

替换后,分别在四大组件中对 Application、ApplicationContext 和 ApplicationInfo 的信息进行打印。
在这里插入图片描述

可以发现,四大组件中,只有 Provider 没有替换成功,我们查看下源码,分析下各个组件替换结果的原因。

1.Activity

        Log.e(TAG, "xiaoyue activity:" + getApplication());
        Log.e(TAG, "xiaoyue activity:" + getApplicationContext());
        Log.e(TAG, "xiaoyue activity:" + getApplicationInfo().className);

查看 getApplication() 代码。

Activity 的 getApplication :

    public final Application getApplication() {
        return mApplication;
    }

getApplication 直接返回 mApplication,全局搜索可以发现,mApplication 是在 attach 函数中进行初始化,由外部传递进来的。

Activity 的创建也是在 ActivityThread 中。

ActivityThread :

    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        ........
        final Activity a = performLaunchActivity(r, customIntent);
        ........
        return a;
    }

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        ........
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
        	//与 Application 一样,通过反射创建
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            // makeApplication 直接返回 LoadedApk 的 mApplication,这个我们已经替换过了
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            ........
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
        ........
        return activity;
    }

在 ActivityThread 的 performLaunchActivity 中创建 Activity,然后调用了 Acticity 的 attach 方法,这里的第六个参数传递的就是 Activity 中获取的 Application 信息。而这个 Application 对象是用过获取 LoadedApk 的 mApplication 属性,这个我们已经替换过了,所以 Activity 获取 Application 是替换后的。

2.Service

        Log.e(TAG, "xiaoyue service:" + getApplication());
        Log.e(TAG, "xiaoyue service:" + getApplicationContext());
        Log.e(TAG, "xiaoyue service:" + getApplicationInfo().className);

Service 与 Activity 类似,我们查看 getApplication 。

Service 的 getApplication :

    public final Application getApplication() {
        return mApplication;
    }

可以发现,直接返回属性 mApplication,这个属性也是在 attach 方法中进行赋值,有外部传递进来的。

Service 启动是在 ActivityThread 的 handleCreateService 中。
ActivityThread 的 handleCreateService :

    private void handleCreateService(CreateServiceData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
        ........
    }

与 Activity 一样,也是通过反射创建,创建成功后调用 attach 方法,传递的参数 Application 同样是获取 LoadedApk 的 mApplication 属性。

3.BroadcastReceiver

        Log.e(TAG, "xiaoyue reciver:" + context);
        Log.e(TAG, "xiaoyue reciver:" + context.getApplicationContext());
        Log.e(TAG, "xiaoyue reciver:" + context.getApplicationInfo().className);

BroadcastReceiver 的 getApplicationContext 和 getApplicationInfo().className 是沒有没有问题的,但是 context 是一个我们没有见过的对象。

在这里插入图片描述

BroadcastReceiver 也是在 ActivityThread 中创建的,查看 handleReceiver 方法。

ActivityThread 的 handleReceiver:

    private void handleReceiver(ReceiverData data) {
            ........
            java.lang.ClassLoader cl = context.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = packageInfo.getAppFactory()
                    .instantiateReceiver(cl, data.info.name, data.intent);
            ........
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
            ........
    }

handleReceiver 中也是通过反射创建出 BroadcastReceiver ,然后调用 BroadcastReceiver 的 onReceive 方法,但是传递的参数不是直接传递 context,而是调用 ContextImpl 的 getReceiverRestrictedContext 方法。

ContextImpl 的 getReceiverRestrictedContext:

    final Context getReceiverRestrictedContext() {
        if (mReceiverRestrictedContext != null) {
            return mReceiverRestrictedContext;
        }
        return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
    }

可以发现,在 ContextImpl 的 getReceiverRestrictedContext 中是创建一个 ReceiverRestrictedContext 进行返回,创建 ReceiverRestrictedContext 传递的 getOuterContext 这个其实就是我们的 Application。

ContextImpl 的 getOuterContext:

    final Context getOuterContext() {
        return mOuterContext;
    }

ContextImpl 的 getOuterContext 我们也已经进行替换了,所以返回的 ReceiverRestrictedContext 只是对我们替换过的 Application 进行一次封装。

注: ReceiverRestrictedContext 只是重写几个方法,避免我们在接收到广播后进行注册广播和绑定服务等。

4.ContentProvider

        Log.e(TAG, "xiaoyue provider onCreate:" + getContext());
        Log.e(TAG, "xiaoyue provider onCreate:" + getContext().getApplicationContext());
        Log.e(TAG, "xiaoyue provider onCreate:" + getContext().getApplicationInfo().className);

在这里插入图片描述

由日志可以知道, ContentProvider 的创建是在 Application 的 attachBaseContext 之后,onCreate 之前。在前面的 Application 的 创建流程中可以知道,这一段代码是在 ActivityThread 的 handleBindApplication。

ActivityThread 的 handleBindApplication:

   private void handleBindApplication(AppBindData data) {
		........
        Application app;
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            //调用 Application 的 attachBaseContext  方法
            app = data.info.makeApplication(data.restrictedBackupMode, null);

            // Propagate autofill compat state
            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);

            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            //ContentProvider  的创建
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            try {
            
                //调用 Application 的 onCreate 方法
                mInstrumentation.callApplicationOnCreate(app);
			........
    }

在 ActivityThread 的 handleBindApplication 中,创建了 Application 的实例对象,这是我们配置在 AndroidManifest.xml 中的假的 Application。然后调用了 installContentProviders 方法,把这个假的 Application 传递进去。

ActivityThread 的 installContentProviders:

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

ActivityThread 的 installContentProviders 对 ProviderInfo 进行遍历,调用 installProvider 这个方法进行创建各个 ContentProvider,这时候传递的 Application 也是假的 Application。

ActivityThread 的 installProvider:

    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.provider == null) {
            if (DEBUG_PROVIDER || noisy) {
                Slog.d(TAG, "Loading provider " + info.authority + ": "
                        + info.name);
            }
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            if (context.getPackageName().equals(ai.packageName)) {
                c = context;
            } else if (mInitialApplication != null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
                c = mInitialApplication;
            } else {
                try {
                    c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore
                }
            }
            if (c == null) {
                Slog.w(TAG, "Unable to get context for package " +
                      ai.packageName +
                      " while loading content provider " +
                      info.name);
                return null;
            }
            try {
            	//通过反射创建 ContentProvider
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                // XXX Need to create the correct context for this provider.
                // ContentProvider 调用 attachInfo 方法
                localProvider.attachInfo(c, info);
		........
    }

ActivityThread 的 installProvider中也是通过反射创建出 ContentProvider ,然后调用 attachInfo 这个方法,这时候传递的参数 c ,由于传递的 context 获取的 packageName 与 Application 获取的 packageName 相同,所以 c 就是传递进来的 context,是假的 Application。

四、ContentProvider 的修改

在上面分析可以知道,ContentProvider 创建成功后会调用 attachInfo 这个方法,这时候传递进去的参数 c 是假的 Application,我们现在需要把这个替换成真正的 Application。

可以一步步分析其具体的位置,然后使用反射进行替换,不过较复杂,这边使用一个相对简单的方案。

我们先分析参数 c 的获取。
ActivityThread 的 installProvider:

    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
            ........
            //判断当前 context 的包名是否与 Application 的包名一直
            if (context.getPackageName().equals(ai.packageName)) {
                c = context;
            } else if (mInitialApplication != null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
                //mInitialApplication 在前面分析过,其实也是假的 Application
                c = mInitialApplication;
            } else {
                try {
                    c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore
                }
            }
		........
    }

根据上面,不管是 if 还是 else if ,获取的 c 都肯定是假的 Application。要想使 c 不为假的 Application,那必须要走 else。if 和 else if 都是调用假的 Application 的 getPackageName 方法进行获取包名,然后与 ApplicationInfo 中获取的包名进行比较。所以我们重写这个方法,使这个判断不成立。

重写 getPackageName:

    @Override
    public String getPackageName() {
        //如果meta-data 设置了 application
        //让 ContentProvider 创建的时候使用的上下文 
        //在ActivityThread 中的 installProvider 函数命中else
        if (!TextUtils.isEmpty(app_name)){
            return "";
        }
        return super.getPackageName();
    }

这时候:

 c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE);

context 是 Application ,createPackageContext 是 Application 的父类 ContextWrapper 的方法。

**ContextWrapper 的 createPackageContext: **

    @Override
    public Context createPackageContext(String packageName, int flags)
        throws PackageManager.NameNotFoundException {
        return mBase.createPackageContext(packageName, flags);
    }

ContextWrapper 的 createPackageContext 直接调用了属性 mBase 的 createPackageContext 方法,mBase 的具体实现是 ContextImpl,这个在 LoadedApk 的 makeApplication 的时候传递进来的。

ContextImpl :

    @Override
    public Context createPackageContext(String packageName, int flags)
            throws NameNotFoundException {
        return createPackageContextAsUser(packageName, flags, mUser);
    }

    @Override
    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
            throws NameNotFoundException {
        if (packageName.equals("system") || packageName.equals("android")) {
            // The system resources are loaded in every application, so we can safely copy
            // the context without reloading Resources.
            return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
                    flags, null);
        }

        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
        if (pi != null) {
            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
                    flags, null);

            final int displayId = mDisplay != null
                    ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;

            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
            if (c.mResources != null) {
                return c;
            }
        }

        // Should be a better exception.
        throw new PackageManager.NameNotFoundException(
                "Application package " + packageName + " not found");
    }

在 ContextImpl 的 createPackageContext 中,最终会新建一个 ContextImpl 返回,创建 ContextImpl 使用的 ActivityThread 和 LoadedApk 都是假的 Application 信息,所以创建出来赋值给 c,拥有的 Application 信息还是假的。

回到最上面 c 的创建。

 c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE);

这里的 context 是假的 Application,createPackageContext 方法是 public 的。所以,我们直接重写假的 Application 的 createPackageContext 方法,让它返回真正 Application 的信息。

ProxyApplication 的 createPackageContext:

    @Override
    public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException {
        if (TextUtils.isEmpty(app_name)){
            return super.createPackageContext(packageName, flags);
        }
        try {
            bindRealApplication();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return delegate;
    }

为了避免 bindRealApplication 中重复进行反射创建等操作,对 bindRealApplication 进行判断。

ProxyApplication 的 bindRealApplication:


    public void bindRealApplication() throws Exception {

        //为了避免多次进行反射创建
        if (delegate != null){
            return;
        }

        //如果用户没有配置 Application 就不用管了
        if (TextUtils.isEmpty(app_name)) {
            return;
        }
        ........
    }

运行结果:
在这里插入图片描述

可以看见,在四大组件中,Application 全部替换成功。

五、附

代码链接

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值