Note: The following source code is based on 9.0. You can download the relevant source code locally by the following methods:

  • Git clone aosp.tuna.tsinghua.edu.cn/platform/pa…
  • Git clone aosp.tuna.tsinghua.edu.cn/platform/fr…

How to download and read the Android source code

preface

This article will be based on the source code of the anatomy of the Android Activity startup process, it should be noted that the following analysis is based on Android9.0, 9.0 compared to the previous several versions of the many changes and reconstruction, but the overall process is little change. According to the different startup time of the Activity, it can be divided into the root Activity startup process and the common Activity startup process. The root Activity startup process can also be called the application startup process, that is, click an application icon on the desktop to enter the first application Activity process. The normal Activity startup process is the process of starting another Activity in an application. Since the two startup processes overlap, and the root Activity startup process is more complex, we will focus on the root Activity startup process, while the normal Activity startup process will be mentioned a little where covered. Considering the length of the paper, this paper will be divided into two parts.

This article will examine the creation of an application process in the startup process:

  • The Launcher process requests AMS
  • AMS sends a request to create an application process
  • The Zygote process accepts the request and incubates the application process
  • The application process starts ActivityThread

The Launcher process requests AMS

As mentioned above, the startup process of the root Activity is actually the process of clicking an application icon on the desktop to enter the first Activity of the application. In fact, the desktop can also be regarded as a program, that is, a Launcher. When the system starts up, the Launcher is launched, and the installed application icon is displayed on the desktop. Therefore, when we click an application icon, it is equivalent to clicking a button in the activity, and the corresponding event is the Launcher process requesting AMS to start the application.

1. The sequence diagram

2. Detailed process

The entry point to the request is the startActivitySafe method of the Launcher, which looks like this:

packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {...// The root Activity is started in a new task stack
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        
        try{...if (isShortcut) {
                // Shortcuts need some special checks due to legacy reasons.
                startShortcutIntentSafely(intent, optsBundle, item);
            } else if (user == null || user.equals(Process.myUserHandle())) {
                / / call startActivity
                startActivity(intent, optsBundle);
            } else {
                LauncherAppsCompat.getInstance(this).startActivityForProfile( intent.getComponent(), user, intent.getSourceBounds(), optsBundle); }...return true;
        } catch (ActivityNotFoundException|SecurityException e) {
            ...
        }
        return false;
    }
Copy the code

You can see that this method sets a flag for the root Activity, which is started in the new task stack. The familiar startActivity method is then called, and we don’t have a method in the Launcher, so we naturally think of a parent method. Let’s look at what classes the Launcher inherits.

public class Launcher extends BaseDraggingActivity implements LauncherExterns.LauncherModel.Callbacks.LauncherProviderChangeListener.UserEventDelegate{}public abstract class BaseDraggingActivity extends BaseActivity
        implements WallpaperColorInfo.OnChangeListener {}public abstract class BaseActivity extends Activity implements UserEventDelegate{}Copy the code

In fact, if you keep going, you’ll see that the startActivity that the Launcher calls is actually the startActivity in the Activity. Launcher is an Activity. So starting an app with a Launcher is basically the same as starting Activity. StartActivity = startActivity; startActivity = startActivity; startActivity = startActivity

Source: frameworks/base/core/Java/android/app/Activity. The Java

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        // The second parameter -1 indicates that the Launcher does not need to know the result of the root Activity launch
        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) {
        startActivityForResult(intent, requestCode, null);
    }


    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        //mParent indicates the parent class of the current Activity. If the root Activity has not been created, mParent==null
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); . }... }Copy the code

As you can see from the code above, the final implementation of startActivity is startActivityForResult, The second parameter of startActivity() is -1, which means that the Launcher does not need to know the result of the root Activity. Then in startActivityForResult, since the root Activity has not yet been created, MParent =null, so we only need to focus on the case where mParent=null. In this case, the execStartActivity of the Instrumentation is called. At this point you may ask what the Instrumentation is? Why would you leave it to her? In fact, the Instrumentation class is very important, important in the Activity life cycle call can not do without her. Each Activity holds a reference to the Instrumentation object, but only one Instrumentation object exists for the entire process. Back to business, let’s look at the execStartActivity method of Instrumentation.

frameworks/base/core/java/android/app/Instrumentation.java

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {...try{...// Get the AMS proxy object
            intresult = 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;
    }
Copy the code

This method calls ActivityManager’s getService method to get the AMS proxy object, and then calls the proxy object’s

StartActivity method, so who is this proxy object? Let’s find out

    @UnsupportedAppUsage
    public static IActivityManager getService(a) {
        return IActivityManagerSingleton.get();
    }

    @UnsupportedAppUsage
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create(a) {
                    // Get the activity's service reference, which is an AMS reference of type IBinder
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
					// Convert to an IActivityManager object
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    returnam; }};Copy the code

As you can see in the create method of Singleton, since B is AMS reference as the server in the SystemServer process, it is not in the same process as the current Launcher process as the client and server. Therefore, AM returns the proxy object of iActivityManager. Stub. If you want to realize the communication between the client and the server process, you only need to inherit the iActivityManager. Stub class and implement the corresponding method in AMS. AMS inherits the iActivityManager. Stub class, so that the Launcher process, as the client, has a proxy object for AMS on the server, and can then call AMS methods to implement specific functions. That’s how AMS implements the Launcher.

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback {}Copy the code

AMS sends a request to create an application process

From the above analysis, we already know that the task is now assigned to AMS and the entry is AMS ‘startActivity.

1. The sequence diagram

2. Detailed process

2.1 AMS transfers the request task to Process

Let’s start with the startActivity method in AMS:

Source: frameworks/base/services/core/Java/com/android/server/am/ActivityManagerService. Java

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
                true /*validateIncomingUser*/);
    }

    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {

			
        // Determine whether the caller's process is isolated
        enforceNotIsolatedCaller("startActivity");
        // Check caller permissions
        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // TODO: Switch to user app stacks here.
        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setMayWait(userId)
                .execute();

    }
Copy the code

The startActivity method executes the startActivityAsUser method through multiple method calls, and the startActivityAsUser method returns a long chain of calls to the mActivityStartController, If you were an AlertDialog, it would be easy to see that all of these chained methods return an object of the same type. We only need to look at the return type of obtainStarter to see what type this object is.

frameworks/base/services/core/java/com/android/server/am/ActivityStartController.java

    ActivityStarter obtainStarter(Intent intent, String reason) {
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }
Copy the code

As you can see, the obtainStarter returns an ActivityStarter, so the chain method is to set up the ActivityStarter object with information about the activity to start, and then call the ActivityStarter object execute method. So the next thing we need to look at is this execute method.

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

    int execute(a) {
        try {
            // TODO(b/64750076): Look into passing request directly to these methods to allow
            // for transactional diffs and preprocessing.
            if (mRequest.mayWait) {
                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                        mRequest.inTask, mRequest.reason,
                        mRequest.allowPendingRemoteAnimationRegistryLookup,
                        mRequest.originatingPendingIntent);
            } else {
                returnstartActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo, mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.callingPid, mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.componentSpecified, mRequest.outActivity, mRequest.inTask, mRequest.reason, mRequest.allowPendingRemoteAnimationRegistryLookup, mRequest.originatingPendingIntent); }}finally{ onExecutionComplete(); }}Copy the code

Since we called setMayWait in the chained method of startActivityAsUser, mRequest.mayWait is true, so startActivityMayWait will continue.

ActivityStarter#startActivityMayWait

    private int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
            int userId, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup,
            PendingIntentRecord originatingPendingIntent) {...// Select an appropriate activity from the system based on the intent.
		// The ResolverActivity pops up to let the user select the appropriate application.
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);


        intres = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent); . }private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup) {... mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord, inTask, allowPendingRemoteAnimationRegistryLookup); .return getExternalResult(mLastStartActivityResult);
    }

    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            SafeActivityOptions options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) {...return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, checkedOptions, inTask, outActivity);
    }

    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity) {... result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, outActivity); . postStartActivityProcessing(r, result, mTargetStack);return result;
    }
Copy the code

The startActivityMayWait method calls the startActivityUnchecked method after calling the startActivity method several times. This method determines how to start an Activity based on the launch flag and Activity launch mode, and whether to call the deliverNewIntent method to notify the Activity that an Intent has attempted to restart it. For example, if we set the activity to FLAG_ACTIVITY_NEW_TASK at the beginning, we’ll create a stack of tasks and leave the rest to the code.

  private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {...if (mStartActivity.resultTo == null && mInTask == null&&! mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) ! =0) {
            newTask = true;
            // Create a new TaskRecord
            result = setTaskFromReuseOrCreateNewTask(
                    taskToAffiliate, preferredLaunchStackId, topStack);
        } else if(mSourceRecord ! =null) {
            result = setTaskFromSourceRecord();
        } else if(mInTask ! =null) {
            result = setTaskFromInTask();
        } else{ setTaskToCurrentTopOrCreateNewTask(); }...if (mDoResume) {
            final ActivityRecord topTaskActivity =
                    mStartActivity.getTask().topRunningActivityLocked();
            if(! mTargetStack.isFocusable() || (topTaskActivity ! =null&& topTaskActivity.mTaskOverlay && mStartActivity ! = topTaskActivity)) { ... }else {
                if(mTargetStack.isFocusable() && ! mSupervisor.isFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityUnchecked"); } mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); }}else{ mTargetStack.addRecentActivityLocked(mStartActivity); }... }Copy the code

Then no matter in what mode will eventually call ActivityStackSupervisor. ResumeFocusedStackTopActivityLocked method.

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

    boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {...if(targetStack ! =null && isFocusedStack(targetStack)) {
            returntargetStack.resumeTopActivityUncheckedLocked(target, targetOptions); }... }Copy the code

And he called the ActivityStack resumeTopActivityUncheckedLocked

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

    @GuardedBy("mService")
    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {...try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); }... }private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {... mStackSupervisor.startSpecificActivityLocked(next,true.true);
       }
        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
       return true;
}
Copy the code

emmmm….. , see the estimated meng force, jumped a few class also don’t know stem what, don’t panic,. Let’s stick to the next ActivityStackSupervisor startSpecificActivityLocked, because this method is very important. This method will be a fork in the road between the normal Activity and the root Activity launch process.

ActivityStackSupervisor#startSpecificActivityLocked

    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Gets the application process in which the Activity is to be started
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);
        // If the application process already exists
        if(app ! =null&& app.thread ! =null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }
		// If the application process has not yet been created, a request is sent to Zygote through AMS calling startProcessLocked
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true.0."activity", r.intent.getComponent(), false.false.true);
    }
Copy the code

Reading the code above, we can see that in the method we first get the application process in which the Activity is to be started. If it is a normal Activity start process, this process must exist, so we will execute realStartActivityLocked. However, we are talking about the start process of the root Activity. Since none of the applications have been started, that means the application process that the root Activity is in has not been created yet, and mService is actually AMS, so we will call AMS startProcessLocked. So we’re back to where we started, AMS.

ActivityManagerService.java

    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
                hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                null /* ABI override */.null /* entryPoint */.null /* entryPointArgs */.null /* crashHandler */);
    }

    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {...final booleansuccess = startProcessLocked(app, hostingType, hostingNameStr, abiOverride); . }@GuardedBy("this")
    private final boolean startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride) {
        return startProcessLocked(app, hostingType, hostingNameStr,
                false /* disableHiddenApiChecks */, abiOverride);
    }

    private final boolean startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {...int uid = app.uid; // ID of the user who created the application process
            int[] gids = null;
            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
            if(! app.isolated) {int[] permGids = null;
                try {
                    checkTime(startTime, "startProcess: getting gids from package manager");
                    final IPackageManager pm = AppGlobals.getPackageManager();
                    permGids = pm.getPackageGids(app.info.packageName,
                            MATCH_DEBUG_TRIAGED_MISSING, app.userId);
                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
                            StorageManagerInternal.class);
                    mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
                            app.info.packageName);
                } catch (RemoteException e) {
                    throw e.rethrowAsRuntimeException();
                }

                /* * Create and assign user groups */
                if (ArrayUtils.isEmpty(permGids)) {
                    gids = new int[3];
                } else {
                    gids = new int[permGids.length + 3];
                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
                }
                gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));

                // Replace any invalid GIDs
                if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
                if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2]; }...// This parameter will be mentioned later
            final String entryPoint = "android.app.ActivityThread";    

            return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
                    startTime);        
    }
    private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
            long startTime) {...// Focus on
                    finalProcessStartResult startResult = startProcess(app.hostingType, entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime); . }private ProcessStartResult startProcess(String hostingType, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
            long startTime) {...if (hostingType.equals("webview_service")) {
                startResult = startWebView(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null.new String[] {PROC_START_SEQ_IDENT + app.startSeq});

            } else {

			
				// Create a Process for the application using the process. start method
                startResult = Process.start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, invokeWith,
                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
            }
            checkTime(startTime, "startProcess: returned from zygote!");
            return startResult;
        } finally{ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); }}Copy the code

Are you shocked again? Well, so am I! You’ll end up calling the startProcess method after calling multiple startProcessLocked methods anyway, but it’s important to look at the fourth startProcessLocked above, A entryPoint parameters in this method as “android. App. ActivityThread”, this parameter will start after the schematics process when it comes to creating an ActivityThread is used. Process.start is then called in the startProcess method to send the application’s request to create the Process. AMS then leaves it up to Process to send the request

2.2 Process Sends a request to create an application Process to the Zygote Process

frameworks/base/core/java/android/os/Process.java

    public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int runtimeFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String invokeWith,
                                  String[] zygoteArgs) {
        return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith,
                    /*useBlastulaPool=*/ true, zygoteArgs);
    }


    public final Process.ProcessStartResult start(final String processClass,
                                                  final String niceName,
                                                  int uid, int gid, int[] gids,
                                                  int runtimeFlags, int mountExternal,
                                                  int targetSdkVersion,
                                                  String seInfo,
                                                  String abi,
                                                  String instructionSet,
                                                  String appDataDir,
                                                  String invokeWith,
                                                  boolean useBlastulaPool,
                                                  String[] zygoteArgs) {
        try {

		    // Focus on
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith,
                    /*startChildZygote=*/false,
                    useBlastulaPool, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex); }}Copy the code

As can be seen from the above, the implementation of the start method in Process is the startViaZygote method, so we focus on this method.

    private Process.ProcessStartResult startViaZygote(final String processClass,
                                                      final String niceName,
                                                      final int uid, final int gid,
                                                      final int[] gids,
                                                      int runtimeFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      String seInfo,
                                                      String abi,
                                                      String instructionSet,
                                                      String appDataDir,
                                                      String invokeWith,
                                                      boolean startChildZygote,
                                                      boolean useBlastulaPool,
                                                      String[] extraArgs)
                                                      throws ZygoteStartFailedEx {
        ArrayList<String> argsForZygote = new ArrayList<String>();

        // --runtime-args, --setuid=, --setgid=,
        // Create the string argsForZygote and save the startup parameters that start the application process in this list
        argsForZygote.add("--runtime-args");
        argsForZygote.add("--setuid=" + uid);
        argsForZygote.add("--setgid=" + gid);
        argsForZygote.add("--runtime-flags=" + runtimeFlags);
        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
            argsForZygote.add("--mount-external-default");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
            argsForZygote.add("--mount-external-read");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
            argsForZygote.add("--mount-external-write");
        }
        argsForZygote.add("--target-sdk-version="+ targetSdkVersion); .synchronized(mLock) {


		    // Focus on
            returnzygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, argsForZygote); }}Copy the code

Will create a string list in startViaZygote argsForZygote to save will create application process launch parameters, and then finally will call zygoteSendArgsAndGetResult method, In this method, the first argument calls the openZygoteSocketIfNeeded method and the third argument is the start argument list. So let’s first look at the implementation of the openZygoteSocketIfNeeded method.

    private ZygoteState openZygoteSocketIfNeeded(String abi)
            throws ZygoteStartFailedEx {

        Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
		// In a 64-bit process
        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
            try {


			    // Connect to mZygoteSocketAddress by calling ZygoteState's connect function.
                // mZygoteSocketAddress = zygote
                primaryZygoteState =
                    ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
            }

            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
        }

        if (primaryZygoteState.matches(abi)) {

		    // The Socket returns a ZygoteState object when it successfully connects and matches the ABI
            return primaryZygoteState;
        }
		

        // 32-bit Zygote is in process
        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
            try {
                secondaryZygoteState =
                    ZygoteState.connect(mZygoteSecondarySocketAddress,
                                        mBlastulaPoolSecondarySocketAddress);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
            }

            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
        }

        if (secondaryZygoteState.matches(abi)) {
            return secondaryZygoteState;
        }

        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
    } 
Copy the code

The openZygoteSocketIfNeeded method is used to establish Socket connections with Zygote. This is confirmed by the code, which establishes the corresponding Socket connection based on the number of Zygote processes, and then returns an object of type ZygoteState. Now that Zygote has established a Socket connection, the next course is to send a request! So let’s take a look at the zygoteSendArgsAndGetResult this method is how to send the request!

	// Write the application process startup argument argsForZygote passed in to ZygoteState
    @GuardedBy("mLock")
    private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args)
            throws ZygoteStartFailedEx {
        String msgStr = Integer.toString(args.size()) + "\n"
                        + String.join("\n", args) + "\n";

        // Should there be a timeout on this?
        Process.ProcessStartResult result = new Process.ProcessStartResult();

        // TODO (chriswailes): Move branch body into separate function.
        if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) {
            LocalSocket blastulaSessionSocket = null;

            try {
                blastulaSessionSocket = zygoteState.getBlastulaSessionSocket();

                final BufferedWriter blastulaWriter =
                        new BufferedWriter(
                                new OutputStreamWriter(blastulaSessionSocket.getOutputStream()),
                                Zygote.SOCKET_BUFFER_SIZE);
                final DataInputStream blastulaReader =
                        new DataInputStream(blastulaSessionSocket.getInputStream());

                blastulaWriter.write(msgStr);
                blastulaWriter.flush();

                result.pid = blastulaReader.readInt();
                // Blastulas can't be used to spawn processes that need wrappers.
                result.usingWrapper = false;

                if (result.pid < 0) {
                    throw new ZygoteStartFailedEx("Blastula specialization failed");
                }

                return result;
            } catch (IOException ex) {
                // If there was an IOException using the blastula pool we will log the error and
                // attempt to start the process through the Zygote.
                Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - "
                               + ex.toString());
            } finally {
                try {
                    blastulaSessionSocket.close();
                } catch (IOException ex) {
                    Log.e(LOG_TAG, "Failed to close blastula session socket: "+ ex.getMessage()); }}}try {
            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

            zygoteWriter.write(msgStr);
            zygoteWriter.flush();

            // Always read the entire result from the input stream to avoid leaving
            // bytes in the stream for future process starts to accidentally stumble
            // upon.
            result.pid = zygoteInputStream.readInt();
            result.usingWrapper = zygoteInputStream.readBoolean();
        } catch (IOException ex) {
            zygoteState.close();
            Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
                    + ex.toString());
            throw new ZygoteStartFailedEx(ex);
        }

        if (result.pid < 0) {
            throw new ZygoteStartFailedEx("fork() failed");
        }

        return result;
    }

Copy the code

Since openZygoteSocketIfNeeded has established a Socket connection with the Zygote process, this method writes the application’s startup argument argsForZygote to ZygoteState. AMS then completes the task of sending a request to the Zygote process to create the application process.

Zygote process accepts the request and incubates the application process

AMS has established a Socket connection with Zygote and sent a request to create an application process. Where does Zygote receive the request and how does Zygote process the request? Here can stop to see Liu Wangshu’s Android system start process (two) parsing Zygote process start process, write very good. Since the source code we analyzed is based on 8.0, it may be slightly different, but the general flow is the same. After reading this, we know that the Zygote process accepts requests in the main method of ZygoteInit. So the entry point now is ZygoteInit’s main method.

1. The sequence diagram

2. Detailed process

From the sequence diagram and the analysis above, we know that Zygote processes now accept requests in the main method. Let’s look at the main method

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    @UnsupportedAppUsage
    public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();
        Runnable caller;
        try{...// Create a Socket named zygotezygoteServer.createZygoteSocket(socketName); .// Since the start-system-server parameter is set in init.rc, so
			// Start SystemServer and see the first process of SystemServer created by Zygote
            if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
                if(r ! =null) {
                    r.run();
                    return;
                }
            }
           
            caller = Zygote.initBlastulaPool();
            if (caller == null) {
                Log.i(TAG, "Accepting command socket connections");
                // Wait for AMS requestcaller = zygoteServer.runSelectLoop(abiList); }}catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();
        }

        // We're in the child process and have exited the select loop. Proceed to execute the
        // command.
        if(caller ! =null) { caller.run(); }}Copy the code

Using the main method, we can see that the main method first creates a Server Socket, The Socket with the name “zygote” is used to wait for ActivityManagerService to request Zygote to create a new application process. In the analysis of AMS requests above, we also know that the client will use this name to establish a connection with zygote’s Socket. The next step is to start the SystemServer process, which starts various system services, such as AMS, related to the Activity launch. Finally invokes ZygoteServer. RunSelectLoop (abiList) to create Socket into an infinite loop, waiting for the AMS request. Let’s look at the runSelectLoop

frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

    Runnable runSelectLoop(String abiList) {
   
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
        peers.add(null);
        while (true) {

            while (--pollIndex >= 0) {
                if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                    continue;
                }

                if (pollIndex == 0) {
				  // Listen for Socket connections and act as server sockets
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    socketFDs.add(newPeer.getFileDescriptor());
                } else if (pollIndex < blastulaPoolEventFDIndex) { 
                    try {
						// Process the client's AMS request continuously, then pass it to processOneCommand
                        ZygoteConnection connection = peers.get(pollIndex);
                        final Runnable command = connection.processOneCommand(this); }... }}}}Copy the code

You can see that this method is an infinite loop, which means that the Socket is constantly listening for connections. The acceptCommandPeer method listens to see if a request has been received, and if so hands it to processOneCommand

frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

    Runnable processOneCommand(ZygoteServer zygoteServer) {
        String args[];
        ZygoteArguments parsedArgs = null;
        FileDescriptor[] descriptors;
        try {
		    // Gets the startup parameters of the application process
            args = Zygote.readArgumentList(mSocketReader);
            // TODO (chriswailes): Remove this and add an assert.
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            throw new IllegalStateException("IOException on command socket", ex); }... parsedArgs =newZygoteArguments(args); .//fork The current process creates a child process
        pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
                parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion);

        try {

		    // If pid is 0, this process is a child process, that is, a newly created application process
            if (pid == 0) {
                zygoteServer.setForkChild();
                zygoteServer.closeServerSocket();
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;

                return handleChildProc(parsedArgs, descriptors, childPipeFd,
                        parsedArgs.mStartChildZygote);
            } else {
                // In the parent. A pid < 0 indicates a failure and will be handled in
                // handleParentProc.
                IoUtils.closeQuietly(childPipeFd);
                childPipeFd = null;
                handleParentProc(pid, descriptors, serverPipeFd);
                return null; }}finally{ IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); }}Copy the code

In this method the request is processed by first getting the startup parameters of the application process to be launched, and then calling forkAndSpecialize to create the application process.

frameworks/base/core/java/com/android/internal/os/Zygote.java

    public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
            int targetSdkVersion) {
        ZygoteHooks.preFork();
        // Resets nice priority for zygote process.
        resetNicePriority();
        int pid = nativeForkAndSpecialize(
                uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                fdsToIgnore, startChildZygote, instructionSet, appDataDir);
        // Enable tracing as soon as possible for the child process.
        if (pid == 0) {
            Zygote.disableExecuteOnly(targetSdkVersion);
            Trace.setTracingEnabled(true, runtimeFlags);

            // Note that this event ends at the end of handleChildProc,
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
        }
        ZygoteHooks.postForkCommon();
        return pid;
    }
Copy the code

In ForkandWis, the task of creating application processes is ultimately left to NativeForkandWIs, and this method can be seen as a local method, so we won’t go into the details of how it was created. Here we need to know nativeForkAndSpecialize is ultimately through the fork the current process to create a child process, and there will be a return value to pid after fork:

  • In the parent process, fork returns the newly created child process PID;
  • In the child process, fork returns 0;
  • When an error occurs, fork returns a negative number.

So at this point the child process or the application process is incubated. You think this is the end of it? It’s still early! Don’t forget that our ultimate mission is to start the root Activity, and now we just have the application process required by the root Activity. The revolution is not yet complete, and we still need to work hard!

4. The application starts ActivityThread

1. The sequence diagram

2. Detailed process

From the above we know that the application process has been created. What happens after it is created? This requires us to go back to the processOneCommand method above. If you are careful, you will see that there is a return value after incubating the application process.

frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

    Runnable processOneCommand(ZygoteServer zygoteServer) {...//fork The current process creates a child process
        pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
                parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion);

        try {

		    // If pid is 0, this process is a child process, that is, a newly created application process
            if (pid == 0) {...return handleChildProc(parsedArgs, descriptors, childPipeFd,
                        parsedArgs.mStartChildZygote);
            } else{... }}... }Copy the code

In the above analysis, when PID =0, it means that the current process is already a child process, that is, the application process. So the next step is to execute the handleChildProc method.

    private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
            FileDescriptor pipeFd, boolean isZygote) {...if(! isZygote) {return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else{... }}Copy the code

HandleChildProc eventually calls the zygoteinit.zygoteinit method. The following

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {
        if (RuntimeInit.DEBUG) {
            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();
		// Set no exception catcher for the current VM
        RuntimeInit.commonInit();
		// The Binder driver is initialized. After this method is completed, the process can communicate with Binder
        ZygoteInit.nativeZygoteInit();
		// Call SystemServer's main method
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }
Copy the code

This method creates the current process’s Binder thread pool for subsequent communication with other processes, and calls RuntimeInit’s applicationInit method as follows:

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

   protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {...final Arguments args = new Arguments(argv);
        // Remaining arguments are passed to the start class's static main
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }
Copy the code

This method will eventually call the findStaticMain method, but note that the first argument to the method, args.startClass, is the same argument we highlighted at the end of AMS’s transition to Process: Android. App. ActivityThread. Then let’s look at the implementation of findStaticMain

    protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<? > cl;try {
            cl = Class.forName(className, true, classLoader);/ / 1
        } catch (ClassNotFoundException ex) {
           ....
        }
        Method m;
        try {
            m = cl.getMethod("main".new Class[] { String[].class });/ / 2
        } catch (NoSuchMethodException ex) {
            ...
        } catch (SecurityException ex) {
            ...
        }
        return new MethodAndArgsCaller(m, argv);
    }
Copy the code

In this method, first of all, in the note 1 by reflecting access to android. The app. ActivityThread classes, and then in 2 get ActivityThread main method, finally, the main method to construct MethodAndArgsCaller. And what is this MethodAndArgsCaller? As follows:

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

static class MethodAndArgsCaller implements Runnable {
        /** method to call */
        private final Method mMethod;

        /** argument array */
        private final String[] mArgs;

        public MethodAndArgsCaller(Method method, String[] args) {
            mMethod = method;
            mArgs = args;
        }

        public void run(a) {
            try {
                mMethod.invoke(null.new Object[] { mArgs });
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InvocationTargetException ex) {
                Throwable cause = ex.getCause();
                if (cause instanceof RuntimeException) {
                    throw (RuntimeException) cause;
                } else if (cause instanceof Error) {
                    throw (Error) cause;
                }
                throw newRuntimeException(ex); }}}Copy the code

MethodAndArgsCaller is actually an inner class of RuntimeInit that inherits Runnable, and then reflection calls mMethod in the run method, which is the main method of ActivityThread, The run method will execute the main method of ActivityThread. When will the run method execute? Let’s look at the main method of ZygoteInit from the very beginning.

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    @UnsupportedAppUsage
    public static void main(String argv[]) {
        Runnable caller;
        try{... caller = Zygote.initBlastulaPool();if (caller == null) {
                Log.i(TAG, "Accepting command socket connections");
                // Wait for AMS requestcaller = zygoteServer.runSelectLoop(abiList); }}catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();
        }
        if(caller ! =null) { caller.run(); }}Copy the code

Did you have an Epiphany? From the analysis of the Zygote process to accept the request and incubation application process in the beginning, we is to analyze zygoteServer. RunSelectLoop (abiList) this approach, At the end of the analysis, findStaticMain will return a MethodAndArgsCaller object (inherits Runnable), so ZygoteInit’s main method caller will equal this object. As caller does not equal null, the main method of ActivityThread is executed. The application process successfully starts ActivityThread.

subsequent

Analysis to this, we have been closer and closer to the end of the long march! But still have to rest, nourish enough energy, to walk to the end. The next blog, Android 9.0Activity Startup Process (2), examines the rest of the Activity startup process. That is:

  • Application processes bind to AMS
  • AMS sends a request to start the Activity
  • The Handler for ActivityThread handles the request to start the Activity

Reference blog:

  • Android8.0 root Activity startup process

  • Start the Android application process

  • Understand the Activity startup process in depth

  • The Activity startup process is fully resolved