剖析Activity启动及Hook

SystemServer及AMS
  • uboot 在引导 os 启动,然后加载 kernel;当 kernel 加载完成后,进入 init 进程,fork 出 zygote,然后由 zygote 去启动 SystemServer;
  • 在SystemServer中执行run方法,启动AMS,并通过将SystemServer进程可加到AMS中调度管理mActivityManagerService.setSystemProcess();
AMS. setSystemProcess方法代码
public void setSystemProcess() {
        // 将服务加入到ServiceManager中
        ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
        ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
        ServiceManager.addService("meminfo", new MemBinder(this));
        ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
        ServiceManager.addService("dbinfo", new DbBinder(this));
        
        // 设置application info LoadedApkinfo 有关 framework-res.apk
        ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS);
        mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
        
        //给SystemServer进程创建ProcessRecord,adj值,就是将SystemServer进程加入到AMS进程管理机制中,跟应用进程一致
        synchronized (this) {
            ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
            app.persistent = true;
            app.pid = MY_PID;
            app.maxAdj = ProcessList.SYSTEM_ADJ;
            app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked.put(app.pid, app);
            }
            updateLruProcessLocked(app, false, null);
            updateOomAdjLocked();
        }
    }

通过上面步骤,我们知道了AMS的启动及应用进程信息的收集存储,那么我们就可以继续往下了解Activity的启动过程

Activity的启动应该分为四个过程
  • AMS发起启动Activity请求

  • AMS接收后通过socket方式发送fork进程的参数给到Zygote进程

  • Zygote进程接收后fork出应用进程并返回进程的pid

  • 应用进程通过反射调用ActivityThread中的main方法启动ActivityThread线程

图一.jpeg
相关类说明
  • Instrumentation:这个类就是完成对Application和Activity初始化和生命周期的工具类。每个Activity都持有Instrumentation对象的一个引用,但是整个进程只会存在一个Instrumentation对象,这个Instrumentation对象存放在ActivityThread中。
  • ActivityManager:此类提供有关活动,服务和包含过程的信息和交互。
  • IActivityManager:用于与ActivityManagerService交谈的系统专用API。 提供了从应用程序返回到活动管理器的调用。
  • ActivityThread:管理应用程序进程中主线程的执行,根据ActivityManager请求调度和执行Activitys、broadcasts和其他操作。
  • ApplicationThread:ActivityThread内部类,IApplicationThread.aidl的具体实现,提供给ActivityManager,ActivityManager通过它告知应用程序将要做的事。
  • H:继承Handler,ActivityThread内部类,是应用程序进程中主线程的消息管理类。(是hook的一个点)
  • ActivityManagerService:负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作。
  • ActivityStarter:用于解释如何启动活动的控制器。此类收集用于确定如何将意图和标志转变为活动以及相关任务和堆栈的所有逻辑。
  • ActivityStack: 单个活动栈的状态和管理。
  • ActivityStackSupervisor:Activity栈管理。
LAUNCHER

上图是startActivity的启动流程,通常我们点击app启动的时候,会先执行如下

/packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
public final class Launcher extends Activity
        implements View.OnClickListener, OnLongClickListener,LauncherModel.Callbacks,
        View.OnTouchListener {
    ......
    public void onClick(View v) {
            // Make sure that rogue clicks don't get through while allapps is launching, or after the
            // view has detached (it's possible for this to happen if the view is removed mid touch).
            if (v.getWindowToken() == null) {
                return;
            }

            if (!mWorkspace.isFinishedSwitchingState()) {
                return;
            }

           Object tag = v.getTag();
            if (tag instanceof ShortcutInfo) {
                // Open shortcut
               final Intent intent = ((ShortcutInfo) tag).intent;
               int[] pos = new int[2];
                v.getLocationOnScreen(pos);
                intent.setSourceBounds(new Rect(pos[0], pos[1],
                       pos[0] + v.getWidth(), pos[1] + v.getHeight()));

                boolean success = startActivitySafely(v, intent, tag);

               if (success && v instanceof BubbleTextView) {
                    mWaitingForResume = (BubbleTextView) v;
                   mWaitingForResume.setStayPressed(true);
                }
           } else if (tag instanceof FolderInfo) {
               ......
           } else if (v == mAllAppsButton) {
               ......
           }
    } 

    boolean startActivitySafely(View v, Intent intent, Object tag) {
        boolean success = false;
        try {
            success = startActivity(v, intent, tag);
        } catch (ActivityNotFoundException e) {
            ......
        }
        return success;
    }

    boolean startActivity(View v, Intent intent, Object tag) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        try {
            // Only launch using the new animation if the shortcut has not opted out (this is a
           // private contract between launcher and may be ignored in the future).
            boolean useLaunchAnimation = (v != null) &&
                    !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
            UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
            LauncherApps launcherApps = (LauncherApps)
                    this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
            if (useLaunchAnimation) {
                ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
                        v.getMeasuredWidth(), v.getMeasuredHeight());
                if (user == null || user.equals(android.os.Process.myUserHandle())) {
                    // Could be launching some bookkeeping activity
                    startActivity(intent, opts.toBundle());
                } else {
                    launcherApps.startMainActivity(intent.getComponent(), user,
                            intent.getSourceBounds(),
                           opts.toBundle());
               }
            } else {
                if (user == null || user.equals(android.os.Process.myUserHandle())) {
                    startActivity(intent);
                } else {
                    launcherApps.startMainActivity(intent.getComponent(), user,
                            intent.getSourceBounds(), null);
                }
            }
            return true;
        } catch (SecurityException e) {
            ......
        }
        return false;
    }

因为launcher继承于Activity,因此上面的startActivity最终都会调到Activity.startActivity,它实现如下:

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback {
    ......
    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            ....
        }
    }
}
startActivityForResult方法

最终调用InstrumentationexecStartActivity来启动应用

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
                                       @Nullable Bundle options) {
        if (mParent == null) {
            //...
            options = transferSpringboardActivityOptions(options);
            //这里调用instrumentation的execStartActivity()方法
            Instrumentation.ActivityResult ar =
                    mInstrumentation.execStartActivity(
                            this, mMainThread.getApplicationThread(), mToken, this,
                            intent, requestCode, options);
        } else {
            //...
        }
}
execStartActivity()方法

如下 ActivityManager.getService().startActivity涉及到了与AMS通信,通过getService()获取到了远程对象引用(IActivityManager接口对象),这里是一个hook activity的关键点,如果我们想启动一个没有注册的activity,那我们可以hook掉IActivityManager接口对象通过动态代理的方式截取startActivity方法,从而将已注册的代理activity传入,这样就可以让AMS检查activity为注册文件中的activity。

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    //...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        //主要是这段代码,可以看出这里会获取Service,然后调用startActivity方法
        int result = ActivityManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

最终通过ActivityStarterActivityStack等相关类后,走到startSpecificActivityLocked方法

startSpecificActivityLocked方法

先从图一启动activity,在startSpecificActivityLocked中会根据app的processNameuid来判断是否已存在(即是否处于running状态),若是则进入realStartActivityLocked(即图二 5),否则则进入startProcessLocked方法通过socketZygote进程通信,由Zygote进程fork应用进程,在从应用进程中启动ActivityThread线程即app主线程,从而进入图二流程
r.info.flags&ActivityInfo.FLAG_MULTIPROCESS这个是有何用图呢?

   void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.getStack().setLaunchTime(r);

        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
        }
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }
Zygote进程

Zygote进程又称受精卵进程,zygote进程由init通过fork而来,由app_process启动,Zygote进程最大意义是作为一个SocketServer端,接收着各方的进程创建请求,Android中所有的应用进程的创建都是一个应用进程通过Binder请求SystemServer进程,SystemServer进程发送socket消息给Zygote进程,统一由Zygote进程创建出来的。典型的C/S架构。

进程的启动流程:Init进程-->Zygote进程-->SystemServer进程-->应用进程

进程知识点

init:是linux系统中用户空间的第一个进程。由于Android是基于linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号是1。
SystemServer:是由Zygote通过Zygote.forkSystemServer函数fork出来的。
应用进程:是Zygote通过fork创建的子进程

zygote进程将ZygoteInit作为启动类,会执行它的main方法,先注册ZygoteSocket,然后调用runSelectLoop方法,runSelectLoop方法会调用方法在ZygoteSocket上监听请求,如果别的进程通过ZygoteSocket请求孵化进程,则孵化进程。

Zygote进程孵化出应用进程后,会通过反射调用ActivityThread中的main方法启动ActivityThread线程,main方法中会先开启Looper和消息队列,然后调用attach方法将应用进程绑定到AMS,然后进入loop循环,不断地读取消息队列里的消息,并分发消息。

ActivityThread

ui线程又称为主线程,应用程序的入口 当启动应用程序时会由ActivityMangerService孵化一个进程,并且实例化一个ActivityThread对象,该类为final类型,并不是一个线程类

从图一的activity启动流程最终由Zygote进程孵化出应用进程后,通过反射调用ActivityThread中的main方法启动ActivityThread线程从而进入图二流程,如下图

图二.jpeg

关键方法讲解
attachApplicationLocked

关键代码:

  private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {

        // Find the application record that is being attached...  either via
        // the pid if we are running in multiple processes, or just pull the
        // next app record if we are emulating process with anonymous threads.
        ProcessRecord app;
        long startTime = SystemClock.uptimeMillis();
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);
            }
        } else {
            app = null;
        }
                 ····················
  thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
                ···················· 
  mStackSupervisor.attachApplicationLocked(app)
}

通过图二我们知道,首先会通过ActivityThread main()方法通过thread.attach(false)绑定应用进程。涉及到了一次IPC过程,通过将ApplicationThread实例化的对象(即thread对象 该对象其实就是一个binder对象)给AMS,然后调用AMS中的attachApplicationLocked方法,该方法有两个关键方法,依次调用顺序如下:

1) thread.bindApplication(processName, appInfo, providers, app.instrumentationClass...)方法

thread对象为上面传入的对象,该方法是创建Application的关键,该方法是ActivityThread的内部类,通过发送消息给到前面提到的ActivityThread创建的Handler接收,接收消息后执行handleBindApplication方法进行application绑定,然后由LoadeApk对象调用其方法makeApplication。

makeApplication方法中依次执行了两个关键方法:
  • mActivityThread.mInstrumentation.newApplication(
    cl, appClass, appContext);
  • instrumentation.callApplicationOnCreate(app)

看到这里我们明白了application的创建及其onCreate方法的调用过程

2) mStackSupervisor.attachApplicationLocked(app)方法
看完上面执行完thread.bindApplication(processName, appInfo, providers, app.instrumentationClass...)方法后,执行第二个方法mStackSupervisor.attachApplicationLocked,该方法是启动Activity的关键,我们知道StackSupervisor是用来管理ActivityStack(Activity栈管理),从而走到了下面的attachApplicationLocked方法,详情请继续往下看

ActivityThread main()方法

AMS远程调用图二 6 scheduleLaunchActivity方法后,会发送LAUNCH_ACTIVITY消息,还记得在execStartActivity()方法中,我们说过我们可以通过hook IActivityManager接口对象实现动态代理从而截取startActivity方法,将已注册的代理activity传入,但是在ActivityThread中main会开启消息循环,这个时候会读取到我们传来LAUNCH_ACTIVITY消息,而消息中的activity仍是我们hook掉后替换的代理activity,所以我们还需要进行一次hook,将main方法中的handler对象替换成我们自定义的,然后获取到消息后,将代理activity替换成我们想启动的activity,从而完美绕过activity注册检测机制

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

        Environment.initForCurrentUser();
        EventLogger.setReporter(new EventLoggingReporter());
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
attachApplicationLocked方法

该方法主要判断是否需要开启新进程

ActivityStackSupervisor.java
 boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             //当前应用的整个activity堆信息
            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (!isFocusedStack(stack)) {
                    continue;
                }
                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
                final ActivityRecord top = stack.topRunningActivityLocked();
                final int size = mTmpActivityList.size();
                for (int i = 0; i < size; i++) {
                    final ActivityRecord activity = mTmpActivityList.get(i);
                    if (activity.app == null && app.uid == activity.info.applicationInfo.uid
                            && processName.equals(activity.processName)) {
                        try {
                            //如果应用进程与activity进程名称相等则进入,从而调起ActivityThread中的scheduleLaunchActivity方法
                            if (realStartActivityLocked(activity, app,
                                    top == activity /* andResume */, true /* checkConfig */)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                        }
                    }
                }
            }
        }
        if (!didSomething) {
            //如果应用进程与activity进程名称不相等则进入如下方法即图一流程第1.9步,
            //然后由SystemServer进程与Zygote进程通过socket通信,开启新的进程
            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
        }
        return didSomething;
    }
realStartActivityLocked方法

进入realStartActivityLocked方法后,调用app.thread.scheduleLaunchActivity方法进入ThreadActivity中的scheduleLaunchActivity方法,开始发送LAUNCH_ACTIVITY消息
注意:如下的ProcessRecord app对象是怎么来的呢
通过看上面的attachApplicationLocked方法,我们知道其方法内会先从mPidsSelfLocked容器中根据pid获取

app = mPidsSelfLocked.get(pid);
该容器里的数据又是怎么来的呢?
追溯到本文开头AMS中的setSystemProcess方法进行存储的

ActivityStackSupervisor.java
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
                                         ····
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                        System.identityHashCode(r), r.info,
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                        r.persistentState, results, newIntents, !andResume,
                        mService.isNextTransitionForward(), profilerInfo);
                                         ····
}
scheduleLaunchActivity方法
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
            //更新runtime中的进程状态
            updateProcessState(procState, false);
            ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);
            //组装handler消息发送
            sendMessage(H.LAUNCH_ACTIVITY, r);
}
ActivityThread中消息循环接收LAUNCH_ACTIVITY
  case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 } break;
getPackageInfo方法

该方法由getPackageInfoNoCheck方法内部调起,主要作用是获取Package信息,其中LoadedApk对象是APK文件在内存中的表示。 Apk文件的相关信息,诸如Apk文件的代码和资源,甚至代码里面的Activity,Service等组件的信息我们都可以通过此对象获取。

 private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage) {
        final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
        synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (differentUser) {
                // Caching not supported across users
                ref = null;
            } else if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }

            LoadedApk packageInfo = ref != null ? ref.get() : null;
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                        : "Loading resource-only package ") + aInfo.packageName
                        + " (in " + (mBoundApplication != null
                                ? mBoundApplication.processName : null)
                        + ")");
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

                if (mSystemThread && "android".equals(aInfo.packageName)) {
                    packageInfo.installSystemApplicationInfo(aInfo,
                            getSystemContext().mPackageInfo.getClassLoader());
                }

                if (differentUser) {
                    // Caching not supported across users
                } else if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }
performLaunchActivity方法

该方法由handleLaunchActivity方法内部调起,主要逻辑类加载器创建Activity的实例对象

相关方法

isPersistable()方法: 多了个PersistableBundle参数,作用在于当Activity从意外结束恢复时,传递结束前保存的Activity历史数据,从而恢复现场。
mInstrumentation.newActivity方法:调起loadClass从而加载对应的classloader

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
                                             ·····
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //重点 将Activity类文件加载到内存中,创建Activity实例
            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);
            }
        }
        if (r.isPersistable()) {
             //调起activity onCreate方法
             mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
       }
        try {
             //创建application对象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
             }
                                             ·····
    }

Hook 掉Activity

看到这里,不得不提下插件化的一些原理知识,在稍后章节也会做详细剖析。
通常我们在插件化的时候,需要写一些新的Activity,那这个时候你不可能说在主apk中也同时注册进去吧?这样显然不可行,那肯定需要绕过AMS这个检查机制,实现无需在AndroidManifest.xml中注册也能运行。
小编目前想到的三个hook点

  • hook Instrumentation
    重写Instrumentation中的execStartActivity方法,将已注册的代理activity传入
    重写newActivity,在ActivityThread到handler启动Activity过程会调用newActivity,这个时候取出真正需要启动的activity
  • hook AMS和ActivityThread中的handler.callback
    通过hook掉AMS中的IActivityManager接口,实现动态代理,截取startActivity方法,将已注册的代理activity传入
    再hook掉ActivityThread中的handler,替换成自定义的handler,在自定义的handler中接收LAUNCH_ACTIVITY消息,将真正需要启动的activity传入
    因为ActivityThread中的handler接收到LAUNCH_ACTIVITY消息后,需要将传入的intent中包含的activity信息通过performLaunchActivity方法实例化activity对象
  activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
  • hook PackageInfo
    通过看上文提到的getPackageInfo方法,你会发现ActivityThread中消息循环接收LAUNCH_ACTIVITY时,会先去调用getPackageInfo方法获取Package信息,而Package信息有进行缓存,即保存在mPackages容器中,你可以通过hook 往mPackages容器中放入拟定的数据

除了hook外,你也可以在宿主项目中预先在注册文件中加入需要启动的插件activity,如:

        <activity android:name="com.shengyuan.pulgin1.MainActivity2"/>

其实看完源码你就会发现hook activity并不复杂,下面针对第二个方法点列出关键代码

关键代码
第一步hook AMS中的IActivityManager

替换系统的IActivityManager,主要采取动态代理的技术构造IActivityManager
注意:在android8.0上字段名称变更了

 public void hookIActivityManager() throws Exception {
        Log.i(TAG, "start hookIActivityManager");
        Class<?> activityManagerNativeClass;
        Field gDefaultFile;
        /**
         * 核心
         * 由于IActivityManagerSingleton是单例模式,可以拿到系统该单例对象并且修改该对象
         * 只有系统单例的对象修改才有效果
         */
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1){
            //反射获取类
            activityManagerNativeClass = Class.forName("android.app.ActivityManager");
            //获取类中的字段
            gDefaultFile = activityManagerNativeClass.getDeclaredField("IActivityManagerSingleton");
        }else {
            //反射获取类
            activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
            //获取类中的字段
            gDefaultFile = activityManagerNativeClass.getDeclaredField("gDefault");
        }
        //设置字段可访问
        gDefaultFile.setAccessible(true);
        //获取反射字段的值,静态方法,不需要传入对象,所以对象为null
        Object gDefaultFileValue = gDefaultFile.get(null);
        //获取gDefault.get()的值,主要在Singleton中
        Class<?> singletonClass = Class.forName("android.util.Singleton");
        Field mInstanceFile = singletonClass.getDeclaredField("mInstance");
        mInstanceFile.setAccessible(true);
        //非静态方法,需要传入对象,获取系统的IActivityManager
        Object IActivityManager = mInstanceFile.get(gDefaultFileValue);
        //获取IActivityManager接口

        Class<?> IActivityManagerClass = Class.forName("android.app.IActivityManager");
        //接下来需要创建钩子,替换系统的IActivityManager,主要采取动态代理的技术构造IActivityManager
        ProxyIActivityManager proxyIActivityManager= new ProxyIActivityManager(IActivityManager);
        Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{IActivityManagerClass},
                proxyIActivityManager);
        //hook 就是为了替换IActivityManager的值,以下就是替换操作
        mInstanceFile.set(gDefaultFileValue, proxy);
        /////////到这里为止,已经实现了用代理Activity来替换未注册的Activity,通过PackageManagerService校验////////////
        //接下来找到系统的ActivityThread 并且要找到单例对象,才可以修改该对象值
    }

class ProxyIActivityManager implements InvocationHandler{

        private Object iActivityManager;

        public ProxyIActivityManager(Object iActivityManager){
            this.iActivityManager = iActivityManager;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Log.i(TAG, "ProxyIActivityManager invoke:" + method.getName());
            if (method.getName().contains("startActivity")){

                int index = 0;
                Intent realIntent = null;
                for (int i = 0; i<args.length; i++){
                    if (args[i] instanceof Intent){
                        realIntent = (Intent) args[i];//真正的Intent,无法通过PackageManagerService检查
                        index = I;
                        break;
                    }
                }
                //代理Intent,可以通过PackageManagerService检查
                Intent proxyIntent = new Intent(mContext, ProxyActivity.class);
                proxyIntent.putExtra(REAL_INTENT, realIntent);
                args[index] = proxyIntent;
            }
            return method.invoke(iActivityManager, args);
        }
    }
第二步hook ActivityThread中的H (即Handler对象)
  public void hookActivityThreadHandler() throws Exception {
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
        currentActivityThreadField.setAccessible(true);
        Object currentActivityThreadValue = currentActivityThreadField.get(null);
        Field mHandlerField = activityThreadClass.getDeclaredField("mH");
        mHandlerField.setAccessible(true);
        Handler handlerValue = (Handler) mHandlerField.get(currentActivityThreadValue);
        Field mCallbackField = Handler.class.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        mCallbackField.set(handlerValue, new HandlerCallback(handlerValue));
    }

    class HandlerCallback implements Handler.Callback{

        private Handler mHandler;
        public HandlerCallback(Handler handlerValue){
            mHandler = handlerValue;
        }

        @Override
        public boolean handleMessage(Message msg) {
            //LAUNCH_ACTIVITY 的what值是100
            if (msg.what == 100){
                //先处理自己的Handler消息,再处理ActivityThread中自身的handler消息
                try {
                    Log.i(TAG,"LAUNCH_ACTIVITY");
                    Object activityClientRecord = msg.obj;//ActivityClientRecord
                    Field intentField = activityClientRecord.getClass().getDeclaredField("intent");
                    intentField.setAccessible(true);
                    Intent proxyIntent = (Intent) intentField.get(activityClientRecord);
                    Intent realIntent = (Intent) proxyIntent.getParcelableExtra(REAL_INTENT);
                    if (realIntent != null){
                        //方法一,直接替换intent
                        //intentField.set(activityClientRecord, realIntent);
                        //方法二 替换component
                        proxyIntent.setComponent(realIntent.getComponent());
                    }

                }catch (Exception e){

                }
            }
            //处理ActivityThread中自身的handler消息
            mHandler.handleMessage(msg);
            return true;
        }
    }
给大家两个思考的问题
  • ActivityThread究竟是线程吗?
  • 一个应用进程有多少个ActivityThread?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容