剖析Activity啟動(dòng)及Hook

SystemServer及AMS
  • uboot 在引導(dǎo) os 啟動(dòng),然后加載 kernel;當(dāng) kernel 加載完成后,進(jìn)入 init 進(jìn)程,fork 出 zygote,然后由 zygote 去啟動(dòng) SystemServer;
  • 在SystemServer中執(zhí)行run方法,啟動(dòng)AMS,并通過(guò)將SystemServer進(jìn)程可加到AMS中調(diào)度管理mActivityManagerService.setSystemProcess();
AMS. setSystemProcess方法代碼
public void setSystemProcess() {
        // 將服務(wù)加入到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));
        
        // 設(shè)置application info LoadedApkinfo 有關(guān) framework-res.apk
        ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS);
        mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
        
        //給SystemServer進(jìn)程創(chuàng)建ProcessRecord,adj值,就是將SystemServer進(jìn)程加入到AMS進(jìn)程管理機(jī)制中,跟應(yīng)用進(jìn)程一致
        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();
        }
    }

通過(guò)上面步驟,我們知道了AMS的啟動(dòng)及應(yīng)用進(jìn)程信息的收集存儲(chǔ),那么我們就可以繼續(xù)往下了解Activity的啟動(dòng)過(guò)程

Activity的啟動(dòng)應(yīng)該分為四個(gè)過(guò)程
  • AMS發(fā)起啟動(dòng)Activity請(qǐng)求

  • AMS接收后通過(guò)socket方式發(fā)送fork進(jìn)程的參數(shù)給到Zygote進(jìn)程

  • Zygote進(jìn)程接收后fork出應(yīng)用進(jìn)程并返回進(jìn)程的pid

  • 應(yīng)用進(jìn)程通過(guò)反射調(diào)用ActivityThread中的main方法啟動(dòng)ActivityThread線程

圖一.jpeg
相關(guān)類(lèi)說(shuō)明
  • Instrumentation:這個(gè)類(lèi)就是完成對(duì)Application和Activity初始化和生命周期的工具類(lèi)。每個(gè)Activity都持有Instrumentation對(duì)象的一個(gè)引用,但是整個(gè)進(jìn)程只會(huì)存在一個(gè)Instrumentation對(duì)象,這個(gè)Instrumentation對(duì)象存放在ActivityThread中。
  • ActivityManager:此類(lèi)提供有關(guān)活動(dòng),服務(wù)和包含過(guò)程的信息和交互。
  • IActivityManager:用于與ActivityManagerService交談的系統(tǒng)專(zhuān)用API。 提供了從應(yīng)用程序返回到活動(dòng)管理器的調(diào)用。
  • ActivityThread:管理應(yīng)用程序進(jìn)程中主線程的執(zhí)行,根據(jù)ActivityManager請(qǐng)求調(diào)度和執(zhí)行Activitys、broadcasts和其他操作。
  • ApplicationThread:ActivityThread內(nèi)部類(lèi),IApplicationThread.aidl的具體實(shí)現(xiàn),提供給ActivityManager,ActivityManager通過(guò)它告知應(yīng)用程序?qū)⒁龅氖隆?/li>
  • H:繼承Handler,ActivityThread內(nèi)部類(lèi),是應(yīng)用程序進(jìn)程中主線程的消息管理類(lèi)。(是hook的一個(gè)點(diǎn))
  • ActivityManagerService:負(fù)責(zé)系統(tǒng)中四大組件的啟動(dòng)、切換、調(diào)度及應(yīng)用進(jìn)程的管理和調(diào)度等工作。
  • ActivityStarter:用于解釋如何啟動(dòng)活動(dòng)的控制器。此類(lèi)收集用于確定如何將意圖和標(biāo)志轉(zhuǎn)變?yōu)榛顒?dòng)以及相關(guān)任務(wù)和堆棧的所有邏輯。
  • ActivityStack: 單個(gè)活動(dòng)棧的狀態(tài)和管理。
  • ActivityStackSupervisor:Activity棧管理。
LAUNCHER

上圖是startActivity的啟動(dòng)流程,通常我們點(diǎn)擊app啟動(dòng)的時(shí)候,會(huì)先執(zhí)行如下

/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;
    }

因?yàn)閘auncher繼承于Activity,因此上面的startActivity最終都會(huì)調(diào)到Activity.startActivity,它實(shí)現(xiàn)如下:

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方法

最終調(diào)用InstrumentationexecStartActivity來(lái)啟動(dòng)應(yīng)用

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

如下 ActivityManager.getService().startActivity涉及到了與AMS通信,通過(guò)getService()獲取到了遠(yuǎn)程對(duì)象引用(IActivityManager接口對(duì)象),這里是一個(gè)hook activity的關(guān)鍵點(diǎn),如果我們想啟動(dòng)一個(gè)沒(méi)有注冊(cè)的activity,那我們可以hook掉IActivityManager接口對(duì)象通過(guò)動(dòng)態(tài)代理的方式截取startActivity方法,從而將已注冊(cè)的代理activity傳入,這樣就可以讓AMS檢查activity為注冊(cè)文件中的activity。

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    //...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        //主要是這段代碼,可以看出這里會(huì)獲取Service,然后調(diào)用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;
}

最終通過(guò)ActivityStarterActivityStack等相關(guān)類(lèi)后,走到startSpecificActivityLocked方法

startSpecificActivityLocked方法

先從圖一啟動(dòng)activity,在startSpecificActivityLocked中會(huì)根據(jù)app的processName、uid來(lái)判斷是否已存在(即是否處于running狀態(tài)),若是則進(jìn)入realStartActivityLocked(即圖二 5),否則則進(jìn)入startProcessLocked方法通過(guò)socketZygote進(jìn)程通信,由Zygote進(jìn)程fork應(yīng)用進(jìn)程,在從應(yīng)用進(jìn)程中啟動(dòng)ActivityThread線程即app主線程,從而進(jìn)入圖二流程
r.info.flags&ActivityInfo.FLAG_MULTIPROCESS這個(gè)是有何用圖呢?

   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進(jìn)程

Zygote進(jìn)程又稱(chēng)受精卵進(jìn)程,zygote進(jìn)程由init通過(guò)fork而來(lái),由app_process啟動(dòng),Zygote進(jìn)程最大意義是作為一個(gè)SocketServer端,接收著各方的進(jìn)程創(chuàng)建請(qǐng)求,Android中所有的應(yīng)用進(jìn)程的創(chuàng)建都是一個(gè)應(yīng)用進(jìn)程通過(guò)Binder請(qǐng)求SystemServer進(jìn)程,SystemServer進(jìn)程發(fā)送socket消息給Zygote進(jìn)程,統(tǒng)一由Zygote進(jìn)程創(chuàng)建出來(lái)的。典型的C/S架構(gòu)。

進(jìn)程的啟動(dòng)流程:Init進(jìn)程-->Zygote進(jìn)程-->SystemServer進(jìn)程-->應(yīng)用進(jìn)程

進(jìn)程知識(shí)點(diǎn)

init:是linux系統(tǒng)中用戶(hù)空間的第一個(gè)進(jìn)程。由于Android是基于linux內(nèi)核的,所以init也是Android系統(tǒng)中用戶(hù)空間的第一個(gè)進(jìn)程,它的進(jìn)程號(hào)是1。
SystemServer:是由Zygote通過(guò)Zygote.forkSystemServer函數(shù)fork出來(lái)的。
應(yīng)用進(jìn)程:是Zygote通過(guò)fork創(chuàng)建的子進(jìn)程

zygote進(jìn)程將ZygoteInit作為啟動(dòng)類(lèi),會(huì)執(zhí)行它的main方法,先注冊(cè)ZygoteSocket,然后調(diào)用runSelectLoop方法,runSelectLoop方法會(huì)調(diào)用方法在ZygoteSocket上監(jiān)聽(tīng)請(qǐng)求,如果別的進(jìn)程通過(guò)ZygoteSocket請(qǐng)求孵化進(jìn)程,則孵化進(jìn)程。

Zygote進(jìn)程孵化出應(yīng)用進(jìn)程后,會(huì)通過(guò)反射調(diào)用ActivityThread中的main方法啟動(dòng)ActivityThread線程,main方法中會(huì)先開(kāi)啟Looper和消息隊(duì)列,然后調(diào)用attach方法將應(yīng)用進(jìn)程綁定到AMS,然后進(jìn)入loop循環(huán),不斷地讀取消息隊(duì)列里的消息,并分發(fā)消息。

ActivityThread

ui線程又稱(chēng)為主線程,應(yīng)用程序的入口 當(dāng)啟動(dòng)應(yīng)用程序時(shí)會(huì)由ActivityMangerService孵化一個(gè)進(jìn)程,并且實(shí)例化一個(gè)ActivityThread對(duì)象,該類(lèi)為final類(lèi)型,并不是一個(gè)線程類(lèi)

從圖一的activity啟動(dòng)流程最終由Zygote進(jìn)程孵化出應(yīng)用進(jìn)程后,通過(guò)反射調(diào)用ActivityThread中的main方法啟動(dòng)ActivityThread線程從而進(jìn)入圖二流程,如下圖

圖二.jpeg

關(guān)鍵方法講解
attachApplicationLocked

關(guān)鍵代碼:

  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)
}

通過(guò)圖二我們知道,首先會(huì)通過(guò)ActivityThread main()方法通過(guò)thread.attach(false)綁定應(yīng)用進(jìn)程。涉及到了一次IPC過(guò)程,通過(guò)將ApplicationThread實(shí)例化的對(duì)象(即thread對(duì)象 該對(duì)象其實(shí)就是一個(gè)binder對(duì)象)給AMS,然后調(diào)用AMS中的attachApplicationLocked方法,該方法有兩個(gè)關(guān)鍵方法,依次調(diào)用順序如下:

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

thread對(duì)象為上面?zhèn)魅氲膶?duì)象,該方法是創(chuàng)建Application的關(guān)鍵,該方法是ActivityThread的內(nèi)部類(lèi),通過(guò)發(fā)送消息給到前面提到的ActivityThread創(chuàng)建的Handler接收,接收消息后執(zhí)行handleBindApplication方法進(jìn)行application綁定,然后由LoadeApk對(duì)象調(diào)用其方法makeApplication。

makeApplication方法中依次執(zhí)行了兩個(gè)關(guān)鍵方法:
  • mActivityThread.mInstrumentation.newApplication(
    cl, appClass, appContext);
  • instrumentation.callApplicationOnCreate(app)

看到這里我們明白了application的創(chuàng)建及其onCreate方法的調(diào)用過(guò)程

2) mStackSupervisor.attachApplicationLocked(app)方法
看完上面執(zhí)行完thread.bindApplication(processName, appInfo, providers, app.instrumentationClass...)方法后,執(zhí)行第二個(gè)方法mStackSupervisor.attachApplicationLocked,該方法是啟動(dòng)Activity的關(guān)鍵,我們知道StackSupervisor是用來(lái)管理ActivityStack(Activity棧管理),從而走到了下面的attachApplicationLocked方法,詳情請(qǐng)繼續(xù)往下看

ActivityThread main()方法

AMS遠(yuǎn)程調(diào)用圖二 6 scheduleLaunchActivity方法后,會(huì)發(fā)送LAUNCH_ACTIVITY消息,還記得在execStartActivity()方法中,我們說(shuō)過(guò)我們可以通過(guò)hook IActivityManager接口對(duì)象實(shí)現(xiàn)動(dòng)態(tài)代理從而截取startActivity方法,將已注冊(cè)的代理activity傳入,但是在ActivityThread中main會(huì)開(kāi)啟消息循環(huán),這個(gè)時(shí)候會(huì)讀取到我們傳來(lái)LAUNCH_ACTIVITY消息,而消息中的activity仍是我們hook掉后替換的代理activity,所以我們還需要進(jìn)行一次hook,將main方法中的handler對(duì)象替換成我們自定義的,然后獲取到消息后,將代理activity替換成我們想啟動(dòng)的activity,從而完美繞過(guò)activity注冊(cè)檢測(cè)機(jī)制

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方法

該方法主要判斷是否需要開(kāi)啟新進(jìn)程

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) {
             //當(dāng)前應(yīng)用的整個(gè)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 {
                            //如果應(yīng)用進(jìn)程與activity進(jìn)程名稱(chēng)相等則進(jìn)入,從而調(diào)起ActivityThread中的scheduleLaunchActivity方法
                            if (realStartActivityLocked(activity, app,
                                    top == activity /* andResume */, true /* checkConfig */)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                        }
                    }
                }
            }
        }
        if (!didSomething) {
            //如果應(yīng)用進(jìn)程與activity進(jìn)程名稱(chēng)不相等則進(jìn)入如下方法即圖一流程第1.9步,
            //然后由SystemServer進(jìn)程與Zygote進(jìn)程通過(guò)socket通信,開(kāi)啟新的進(jìn)程
            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
        }
        return didSomething;
    }
realStartActivityLocked方法

進(jìn)入realStartActivityLocked方法后,調(diào)用app.thread.scheduleLaunchActivity方法進(jìn)入ThreadActivity中的scheduleLaunchActivity方法,開(kāi)始發(fā)送LAUNCH_ACTIVITY消息
注意:如下的ProcessRecord app對(duì)象是怎么來(lái)的呢
通過(guò)看上面的attachApplicationLocked方法,我們知道其方法內(nèi)會(huì)先從mPidsSelfLocked容器中根據(jù)pid獲取

app = mPidsSelfLocked.get(pid);
該容器里的數(shù)據(jù)又是怎么來(lái)的呢?
追溯到本文開(kāi)頭AMS中的setSystemProcess方法進(jìn)行存儲(chǔ)的

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中的進(jìn)程狀態(tài)
            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消息發(fā)送
            sendMessage(H.LAUNCH_ACTIVITY, r);
}
ActivityThread中消息循環(huán)接收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方法內(nèi)部調(diào)起,主要作用是獲取Package信息,其中LoadedApk對(duì)象是APK文件在內(nèi)存中的表示。 Apk文件的相關(guān)信息,諸如Apk文件的代碼和資源,甚至代碼里面的Activity,Service等組件的信息我們都可以通過(guò)此對(duì)象獲取。

 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方法內(nèi)部調(diào)起,主要邏輯類(lèi)加載器創(chuàng)建Activity的實(shí)例對(duì)象

相關(guān)方法

isPersistable()方法: 多了個(gè)PersistableBundle參數(shù),作用在于當(dāng)Activity從意外結(jié)束恢復(fù)時(shí),傳遞結(jié)束前保存的Activity歷史數(shù)據(jù),從而恢復(fù)現(xiàn)場(chǎng)。
mInstrumentation.newActivity方法:調(diào)起loadClass從而加載對(duì)應(yīng)的classloader

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
                                             ·····
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //重點(diǎn) 將Activity類(lèi)文件加載到內(nèi)存中,創(chuàng)建Activity實(shí)例
            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()) {
             //調(diào)起activity onCreate方法
             mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
       }
        try {
             //創(chuàng)建application對(duì)象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
             }
                                             ·····
    }

Hook 掉Activity

看到這里,不得不提下插件化的一些原理知識(shí),在稍后章節(jié)也會(huì)做詳細(xì)剖析。
通常我們?cè)诓寮臅r(shí)候,需要寫(xiě)一些新的Activity,那這個(gè)時(shí)候你不可能說(shuō)在主apk中也同時(shí)注冊(cè)進(jìn)去吧?這樣顯然不可行,那肯定需要繞過(guò)AMS這個(gè)檢查機(jī)制,實(shí)現(xiàn)無(wú)需在AndroidManifest.xml中注冊(cè)也能運(yùn)行。
小編目前想到的三個(gè)hook點(diǎn)

  • hook Instrumentation
    重寫(xiě)Instrumentation中的execStartActivity方法,將已注冊(cè)的代理activity傳入
    重寫(xiě)newActivity,在ActivityThread到handler啟動(dòng)Activity過(guò)程會(huì)調(diào)用newActivity,這個(gè)時(shí)候取出真正需要啟動(dòng)的activity
  • hook AMS和ActivityThread中的handler.callback
    通過(guò)hook掉AMS中的IActivityManager接口,實(shí)現(xiàn)動(dòng)態(tài)代理,截取startActivity方法,將已注冊(cè)的代理activity傳入
    再hook掉ActivityThread中的handler,替換成自定義的handler,在自定義的handler中接收LAUNCH_ACTIVITY消息,將真正需要啟動(dòng)的activity傳入
    因?yàn)锳ctivityThread中的handler接收到LAUNCH_ACTIVITY消息后,需要將傳入的intent中包含的activity信息通過(guò)performLaunchActivity方法實(shí)例化activity對(duì)象
  activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
  • hook PackageInfo
    通過(guò)看上文提到的getPackageInfo方法,你會(huì)發(fā)現(xiàn)ActivityThread中消息循環(huán)接收LAUNCH_ACTIVITY時(shí),會(huì)先去調(diào)用getPackageInfo方法獲取Package信息,而Package信息有進(jìn)行緩存,即保存在mPackages容器中,你可以通過(guò)hook 往mPackages容器中放入擬定的數(shù)據(jù)

除了hook外,你也可以在宿主項(xiàng)目中預(yù)先在注冊(cè)文件中加入需要啟動(dòng)的插件activity,如:

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

其實(shí)看完源碼你就會(huì)發(fā)現(xiàn)hook activity并不復(fù)雜,下面針對(duì)第二個(gè)方法點(diǎn)列出關(guān)鍵代碼

關(guān)鍵代碼
第一步hook AMS中的IActivityManager

替換系統(tǒng)的IActivityManager,主要采取動(dòng)態(tài)代理的技術(shù)構(gòu)造IActivityManager
注意:在android8.0上字段名稱(chēng)變更了

 public void hookIActivityManager() throws Exception {
        Log.i(TAG, "start hookIActivityManager");
        Class<?> activityManagerNativeClass;
        Field gDefaultFile;
        /**
         * 核心
         * 由于IActivityManagerSingleton是單例模式,可以拿到系統(tǒng)該單例對(duì)象并且修改該對(duì)象
         * 只有系統(tǒng)單例的對(duì)象修改才有效果
         */
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1){
            //反射獲取類(lèi)
            activityManagerNativeClass = Class.forName("android.app.ActivityManager");
            //獲取類(lèi)中的字段
            gDefaultFile = activityManagerNativeClass.getDeclaredField("IActivityManagerSingleton");
        }else {
            //反射獲取類(lèi)
            activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
            //獲取類(lèi)中的字段
            gDefaultFile = activityManagerNativeClass.getDeclaredField("gDefault");
        }
        //設(shè)置字段可訪問(wèn)
        gDefaultFile.setAccessible(true);
        //獲取反射字段的值,靜態(tài)方法,不需要傳入對(duì)象,所以對(duì)象為null
        Object gDefaultFileValue = gDefaultFile.get(null);
        //獲取gDefault.get()的值,主要在Singleton中
        Class<?> singletonClass = Class.forName("android.util.Singleton");
        Field mInstanceFile = singletonClass.getDeclaredField("mInstance");
        mInstanceFile.setAccessible(true);
        //非靜態(tài)方法,需要傳入對(duì)象,獲取系統(tǒng)的IActivityManager
        Object IActivityManager = mInstanceFile.get(gDefaultFileValue);
        //獲取IActivityManager接口

        Class<?> IActivityManagerClass = Class.forName("android.app.IActivityManager");
        //接下來(lái)需要?jiǎng)?chuàng)建鉤子,替換系統(tǒng)的IActivityManager,主要采取動(dòng)態(tài)代理的技術(shù)構(gòu)造IActivityManager
        ProxyIActivityManager proxyIActivityManager= new ProxyIActivityManager(IActivityManager);
        Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{IActivityManagerClass},
                proxyIActivityManager);
        //hook 就是為了替換IActivityManager的值,以下就是替換操作
        mInstanceFile.set(gDefaultFileValue, proxy);
        /////////到這里為止,已經(jīng)實(shí)現(xiàn)了用代理Activity來(lái)替換未注冊(cè)的Activity,通過(guò)PackageManagerService校驗(yàn)////////////
        //接下來(lái)找到系統(tǒng)的ActivityThread 并且要找到單例對(duì)象,才可以修改該對(duì)象值
    }

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,無(wú)法通過(guò)PackageManagerService檢查
                        index = I;
                        break;
                    }
                }
                //代理Intent,可以通過(guò)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對(duì)象)
  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;
        }
    }
給大家兩個(gè)思考的問(wèn)題
  • ActivityThread究竟是線程嗎?
  • 一個(gè)應(yīng)用進(jìn)程有多少個(gè)ActivityThread?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容