SoFunction
Updated on 2025-03-11

Android10 App starts the analysis process to create source code analysis

text

Previous article# Android 10 Startup Analysis SystemServer Chapter (IV)It can be seen from the system that after completing all initialization work, it will pass

(currentUserId, "systemReady");

This statement starts the first app of the Android system, namely the Launcher application. In this article, we will use Launcher as the introduction to discuss the startup process of the App. What operations did the system do when starting the App?

existAMSCallstartHomeOnAllDisplaysAfter the method, after layers of traceability, we finally set our sights onRootActivityContainerIn-housestartHomeOnDisplaymethod.

RootActivityContainer

RootActivityContainerThe source code path is/frameworks/base/services/core/java/com/android/server/wm/, the relevant code is as follows:

boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
         boolean fromHomeKey) {
     // If displayId is invalid, return to the top level display device with focus     if (displayId == INVALID_DISPLAY) {
         displayId = getTopDisplayFocusedStack().mDisplayId;
     }
     Intent homeIntent = null;
     ActivityInfo aInfo = null;
     if (displayId == DEFAULT_DISPLAY) {
         //Get the intent of the first page of the lancher, its category is Intent.CATEGORY_HOME         homeIntent = ();
         // Find the activity tag corresponding to AndroidManifest in the launcher through intent, parse the corresponding ActivityInfo and applicationInfo information         aInfo = resolveHomeActivity(userId, homeIntent);
     } else if (shouldPlaceSecondaryHomeOnDisplay(displayId)) {
         //Multi-screen display function, refer to /devices/tech/display/multi_display/system-decorations#launcher, no analysis is done here Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId);         aInfo = ;
         homeIntent = ;
     }
     if (aInfo == null || homeIntent == null) {
         return false;
     }
     //Before starting, check whether the startup conditions are met, probably verify the validity of displayId, the startup mode of the corresponding activity, whether it is in factory test mode, etc.     if (!canStartHomeOnDisplay(aInfo, displayId, allowInstrumenting)) {
         return false;
     }
     // Update component information     (new ComponentName(, ));
     (() | FLAG_ACTIVITY_NEW_TASK);
     // The value of the passed fromHomeKey is false     if (fromHomeKey) {
         (WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
     }
     // Update the reason for ANR debugging to verify if the user activity is the one that
     // actually launched.
     final String myReason = reason + ":" + userId + ":" + (
             ) + ":" + displayId;
       //Go to ActivityStartController class to continue calling the startHomeActivity method     ().startHomeActivity(homeIntent, aInfo, myReason,
             displayId);
     return true;
 }

Let's continue to track:

ActivityStartController

ActivityStartControllerThe source code path is/frameworks/base/services/core/java/com/android/server/wm/

The relevant codes are as follows:

  void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
      //This sentence just creates an ActivityOptions object        final ActivityOptions options = ();
        //FULLSCREEN mode starts the Activity        (WINDOWING_MODE_FULLSCREEN);
        if (!()) {
            // Determine whether there are multiple launchers currently and are in the selected launcher state. If not, set the ACTIVITY_TYPE_HOME attribute and directly start the launcher activity            (ACTIVITY_TYPE_HOME);
        }
        (displayId);
        //Execute the startup task        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                .setOutActivity(tmpOutRecord)
                .setCallingUid(0)
                .setActivityInfo(aInfo)
                .setActivityOptions(())
                .execute();
        mLastHomeActivityStartRecord = tmpOutRecord[0];
        final ActivityDisplay display =
                (displayId);
        final ActivityStack homeStack = display != null ? () : null;
        if (homeStack != null &amp;&amp; ) {
            // If we are in resume section already, home activity will be initialized, but not
            // resumed (to avoid recursive resume) and will stay that way until something pokes it
            // again. We need to schedule another resume.
            ();
        }
    }

We're fromobtainStarterThis line of code starts to look, and the execution of this code means that it begins to enter the activity startup process.

ActivityStarter obtainStarter(Intent intent, String reason) {
        return ().setIntent(intent).setReason(reason);
}

heremFactoryIt's oneFactoryThe interface, its implementation class isDefaultFactory

 public ActivityStarter obtain() {
            ActivityStarter starter = ();
            if (starter == null) {
                starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
            }
            return starter;
        }

CallobtainWhen the method is used, it will first try to get aActivityStarterIf the object cannot be obtained, then create a new one.

We turn our eyes back and focus again onexecuteOn this method.

 int execute() {
        try {
            if () {
                return startActivityMayWait(, ,
                        , , ,
                        , ,
                        , , ,
                        , , ,
                        , , ,
                        , , ,
                        , ,
                        ,
                        , );
            } else {
                return startActivity(, , ,
                        , , ,
                        , , ,
                        , , ,
                        , , ,
                        , , ,
                        , ,
                        , , ,
                        ,
                        , );
            }
        } finally {
            onExecutionComplete();
        }
    }

mayWait is false, enter the else branch.

  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,
            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
        ...
        //Create the ActivityRecord object corresponding to the started activity        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                callingPackage, intent, resolvedType, aInfo, (),
                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                mSupervisor, checkedOptions, sourceRecord);
        if (outActivity != null) {
            outActivity[0] = r;
        }
        final ActivityStack stack = ();
        //If the activity we started is different from the uid of the currently restored activity, please check whether the application is allowed to switch.        if (voiceSession == null &amp;&amp; (() == null
                || (). != realCallingUid)) {
            if (!(callingPid, callingUid,
                    realCallingPid, realCallingUid, "Activity start")) {
                if (!(restrictedBgActivity &amp;&amp; handleBackgroundActivityAbort(r))) {
                    (new PendingActivityLaunch(r,
                            sourceRecord, startFlags, stack, callerApp));
                }
                (checkedOptions);
                return ActivityManager.START_SWITCHES_CANCELED;
            }
        }
        ();
        (false);
        final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
        ().notifyActivityLaunched(res, outActivity[0]);
        return res;
    }

At the end, the code will go to the following branch:

   private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity, boolean restrictedBgActivity) {
        int result = START_CANCELED;
        final ActivityStack startedActivityStack;
        try {
            ();
            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
        } finally {
            final ActivityStack currentStack = ();
            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
            if ((result)) {
                if (startedActivityStack != null) {
                    // If there is no state change (. a resumed activity is reparented to
                    // top of another display) to trigger a visibility/configuration checking,
                    // we have to update the configuration for changing to different display.
                    final ActivityRecord currentTop =
                            ();
                    if (currentTop != null && ()) {
                        (
                                currentTop, (),
                                true /* markFrozenIfConfigChanged */, false /* deferResume */);
                    }
                }
            } else {
                // If we are not able to proceed, disassociate the activity from the task.
                // Leaving an activity in an incomplete state can lead to issues, such as
                // performing operations without a window container.
                final ActivityStack stack = ();
                if (stack != null) {
                    (mStartActivity, RESULT_CANCELED,
                            null /* intentResultData */, "startActivity", true /* oomAdj */);
                }
                // Stack should also be detached from display and be removed if it's empty.
                if (startedActivityStack != null && ()
                        && () == 0
                        && !()) {
                    ();
                }
            }
            ();
        }
        postStartActivityProcessing(r, result, startedActivityStack);
        return result;
    }

Call the startActivityUnchecked method

Among them, the startActivityUnchecked method is called.

 private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity, boolean restrictedBgActivity) {
      ...
        //If the activity being started is the same as the activity currently on the top, then we need to check if it should only be started once        final ActivityStack topStack = ();
        final ActivityRecord topFocused = ();
        final ActivityRecord top = (mNotTop);
        final boolean dontStart = top != null &amp;&amp;  == null
                &amp;&amp; ()
                &amp;&amp;  == 
                &amp;&amp; ()
                &amp;&amp; ((mLaunchFlags &amp; FLAG_ACTIVITY_SINGLE_TOP) != 0
                || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
                // This allows home activity to automatically launch on secondary display when
                // display added, if home was the top activity on default display, instead of
                // sending new intent to the home activity on default display.
                &amp;&amp; (!() || () == mPreferredDisplayId);
        if (dontStart) {
            // For paranoia, make sure we have correctly resumed the top activity.
             = null;
            if (mDoResume) {
                ();
            }
            (mOptions);
            if ((mStartFlags &amp; START_FLAG_ONLY_IF_NEEDED) != 0) {
                // We don't need to start a new activity, and the client said not to do
                // anything if that is the case, so this is it!
                return START_RETURN_INTENT_TO_CALLER;
            }
            deliverNewIntent(top);
            // Don't use  to show the toast. We're not starting a new activity
            // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
            ((), preferredWindowingMode,
                    mPreferredDisplayId, topStack);
            return START_DELIVERED_TO_TOP;
        }
        ...
        //Write the startup activity record into the ActivityStack and update the TaskRecord information at the same time        (mStartActivity, topFocused, newTask, mKeepCurTransition,
                mOptions);
        if (mDoResume) {
            final ActivityRecord topTaskActivity =
                    ().topRunningActivityLocked();
            if (!()
                    || (topTaskActivity != null &amp;&amp; 
                    &amp;&amp; mStartActivity != topTaskActivity)) {
                // If the activity is focusless, we can't recover it, but still want to make sure it becomes visible when it is started (this will also trigger the entry animation).  Furthermore, we do not want to restore the activity in the task that currently has the overlay, because the activity being started only needs to be in a visible pause state until the overlay is removed.                (mStartActivity, 0, !PRESERVE_WINDOWS);
                // Go ahead and tell window manager to execute app transition for this activity
                // since the app transition will not be triggered through the resume channel.
                ().();
            } else {
                // If the target stack was not previously focusable (previous top running activity
                // on that stack was not visible) then any prior calls to move the stack to the
                // will not update the focused stack.  If starting the new activity now allows the
                // task stack to be focusable, then ensure that we now update the focused stack
                // accordingly.
                if (()
                        &amp;&amp; !(mTargetStack)) {
                    ("startActivityUnchecked");
                }
                (
                        mTargetStack, mStartActivity, mOptions);
            }
        } else if (mStartActivity != null) {
            (());
        }
        (, mTargetStack);
        ((),
                preferredWindowingMode, mPreferredDisplayId, mTargetStack);
        return START_SUCCESS;
    }

Let's pay attention to this line of code.

 (
                        mTargetStack, mStartActivity, mOptions);

It calledRootActivityContainerofresumeFocusedStacksTopActivitiesMethod, start the top-most activity of the target ActivityStack.

We then track down and it calledActivityStackofresumeTopActivityInnerLockedmethod.

 private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
       ...
        //Pause the previous activity        boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
        if (mResumedActivity != null) {
            if (DEBUG_STATES) (TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, next, false);
        }
       ...
  // If the recent activity has no history but is stopped because the device goes to sleep, instead of stop + destroyed, we need to make sure to destroy it because we are putting a new activity at the top level.        if (shouldSleepActivities() &amp;&amp; mLastNoHistoryActivity != null &amp;&amp;
                !) {
            if (DEBUG_STATES) (TAG_STATES,
                    "no-history finish of " + mLastNoHistoryActivity + " on new resume");
            requestFinishActivityLocked(, Activity.RESULT_CANCELED,
                    null, "resume-no-history", false);
            mLastNoHistoryActivity = null;
        }
        if (prev != null &amp;&amp; prev != next &amp;&amp; ) {
            /*The next activity is already visible, so now hide the window of the previous activity so that we can display the new activity as soon as possible.
             We only do this at the end of the previous one, which means it's above the resume one, so it's good to hide it quickly.
             Otherwise, we want to display resume's activity in the normal way,
             This way we can decide whether the previous activity should be hidden based on whether the new activity is full screen.  */
            if () {
                (false);
                if (DEBUG_SWITCH) (TAG_SWITCH,
                        "Not waiting for visible to hide: " + prev
                        + ", nowVisible=" + );
            } else {
                if (DEBUG_SWITCH) (TAG_SWITCH,
                        "Previous already visible but still waiting to hide: " + prev
                        + ", nowVisible=" + );
            }
        }
        // Launching this app's activity, make sure the app is no longer
        // considered stopped.
        try {
            ().setPackageStoppedState(
                    , false, ); /* TODO: Verify if correct userid */
        } catch (RemoteException e1) {
        } catch (IllegalArgumentException e) {
            (TAG, "Failed trying to unstop package "
                    +  + ": " + e);
        }
        // We are starting the next activity, so we tell the window manager that the previous activity will be hidden soon.  This way, it can know to ignore it when calculating the desired screen orientation.        boolean anim = true;
        final DisplayContent dc = getDisplay().mDisplayContent;
        if (prev != null) {
            if () {
                if (DEBUG_TRANSITION) (TAG_TRANSITION,
                        "Prepare close transition: prev=" + prev);
                if ((prev)) {
                    anim = false;
                    (TRANSIT_NONE, false);
                } else {
                    (
                            () == () ? TRANSIT_ACTIVITY_CLOSE
                                    : TRANSIT_TASK_CLOSE, false);
                }
                (false);
            } else {
                if (DEBUG_TRANSITION) (TAG_TRANSITION,
                        "Prepare open transition: prev=" + prev);
                if ((next)) {
                    anim = false;
                    (TRANSIT_NONE, false);
                } else {
                    (
                            () == () ? TRANSIT_ACTIVITY_OPEN
                                    :  ? TRANSIT_TASK_OPEN_BEHIND
                                            : TRANSIT_TASK_OPEN, false);
                }
            }
        } else {
            if (DEBUG_TRANSITION) (TAG_TRANSITION, "Prepare open transition: no previous");
            if ((next)) {
                anim = false;
                (TRANSIT_NONE, false);
            } else {
                (TRANSIT_ACTIVITY_OPEN, false);
            }
        }
        if (anim) {
            ();
        } else {
            ();
        }
        ();
        if (()) {
            ... 
            //Only the activated activity will enter this branch        } else {
            // Whoops, need to restart this activity!
            if (!) {
                 = true;
            } else {
                if (SHOW_APP_STARTING_PREVIEW) {
                    //The transition page of cold start will be loaded here. If not, the default white screen or black screen will be blank screen.                    (null /* prev */, false /* newTask */,
                            false /* taskSwich */);
                }
                if (DEBUG_SWITCH) (TAG_SWITCH, "Restarting: " + next);
            }
            if (DEBUG_STATES) (TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
            (next, true, true);
        }
        return true;
    }

Through the source code, we can know: first perform pause operation on the previous activity, then start the target activity, and finally enter the method. Before starting the Activity, a method is called to display a window, which is why the white screen appears during cold startup.

Let's continue to track:

ActivityStackSupervisor

ActivityStackSupervisorThe source code path is/frameworks/base/services/core/java/com/android/server/wm/

 void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        final WindowProcessController wpc =
                (, );
        boolean knownToBeDead = false;
        if (wpc != null &amp;&amp; ()) {
            try {
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                (TAG, "Exception when starting activity "
                        + ().flattenToShortString(), e);
            }
            // If a dead object exception was thrown -- fall through to
            // restart the application.
            knownToBeDead = true;
        }
        // Suppress transition until the new activity becomes ready, otherwise the keyguard can
        // appear for a short amount of time before the new process with the new activity had the
        // ability to set its showWhenLocked flags.
        if (getKeyguardController().isKeyguardLocked()) {
            ();
        }
        try {
            if ((TRACE_TAG_ACTIVITY_MANAGER)) {
                (TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
                        + );
            }
            // Publish a message to start the process, thereby avoiding the possible deadlock when calling AMS when the ATMS lock is held            final Message msg = (
                    ActivityManagerInternal::startProcess, , ,
                    , knownToBeDead, "activity", ());
            (msg);
        } finally {
            (TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

There is a very important judgment in the above method.if(wpc != null && ()), obtained through the process name and application uidWindowProcessControllerThe object is checked to determine whether the activity process to be started has been started. Obviously, when the app is launched for the first time, the process does not exist yet, so you must first create the application process.

final Message msg = (
                 ActivityManagerInternal::startProcess, , ,
                 , knownToBeDead, "activity", ());
         (msg);

Here, through the Handler, a Message is sent to avoid possible deadlocks when calling AMS when the ATMS lock is held. This Message actually callsActivityManagerServiceofstartProcessmethod.

Start the process

ActivityManagerServiceThe source code location is/frameworks/base/services/core/java/com/android/server/am/

startProcessThe actual call isstartProcessLockedMethod, we start directly from it.

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

What is called here isProcessListIn the categorystartProcessLockedLet's continue to read the method.

  final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
        ...
        ProcessRecord app;
        // We do not need to do any additional treatment in the following three situations        // (1) There is an application record        // (2) The caller thinks it is still alive, or there is no thread object connected to it, so we know it has no crash        // (3) There is a pid assigned to it, so it is either starting or running        if (app != null &amp;&amp;  &gt; 0) {
            if ((!knownToBeDead &amp;&amp; !) ||  == null) {
                // We already have the application running, or are waiting for it to appear (we have a pid but don't have its thread yet), so keep it                if (DEBUG_PROCESSES) (TAG_PROCESSES, "App already running: " + app);
                // If this is a new package in the process, add the package to the list
                (, , );
                checkSlow(startTime, "startProcess: done, added package to proc");
                return app;
            }
            // An application record is attached to a previous process,
            // clean it up now.
            if (DEBUG_PROCESSES) (TAG_PROCESSES, "App died: " + app);
            checkSlow(startTime, "startProcess: bad proc running, killing");
            (, );
            (app, true, true);
            checkSlow(startTime, "startProcess: done killing old proc");
        }
        if (app == null) {
            checkSlow(startTime, "startProcess: creating new process record");
            //Create a ProcessRecord            app = new ProcessRecordLocked(info, processName, isolated, isolatedUid, hostingRecord);
            if (app == null) {
                (TAG, "Failed making new process record for "
                        + processName + "/" +  + " isolated=" + isolated);
                return null;
            }
             = crashHandler;
             = entryPoint;
             = entryPointArgs;
            checkSlow(startTime, "startProcess: done creating new process record");
        } else {
            // If this is a new package in the process, add the package to the list
            (, , );
            checkSlow(startTime, "startProcess: added package to existing proc");
        }
        // If the system is not ready yet, then hold off on starting this
        // process until it is.
        if (!
                &amp;&amp; !(info)
                &amp;&amp; !allowWhileBooting) {
            if (!(app)) {
                (app);
            }
            if (DEBUG_PROCESSES) (TAG_PROCESSES,
                    "System not ready, putting on hold: " + app);
            checkSlow(startTime, "startProcess: returning with proc on hold");
            return app;
        }
        checkSlow(startTime, "startProcess: stepping in to startProcess");
        final boolean success = startProcessLocked(app, hostingRecord, abiOverride);
        checkSlow(startTime, "startProcess: done starting proc!");
        return success ? app : null;
    }

It can be seen that when starting a new process, there are two more important steps. One is throughnewProcessRecordLockedMethod first creates a process record and then passesstartProcessLockedMethod to create a process.

 boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
            boolean disableHiddenApiChecks, boolean mountExtStorageFull,
            String abiOverride) {
            int uid = ;
            int[] gids = null;
            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
            if (!) {
               ...
            final String entryPoint = "";
            return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
                    startTime);
        } catch (RuntimeException e) {
            (, "Failure starting process " + , e);
            (, (),
                    false, false, true, false, false, , "start failure");
            return false;
        }
    }

Please note hereentryPointThe value of this variable——, it will go through a long chain of calls and eventuallyRuntimeInitThis class plays its role.

boolean startProcessLocked(HostingRecord hostingRecord,
            String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
            long startTime) {
         = true;
         = false;
         = false;
         = false;
       ...
        final long startSeq =  = ++mProcStartSeqCounter;
        (uid, hostingRecord, seInfo, startTime);
        (invokeWith != null
                || ("wrap." + ) != null);
        (startSeq, app);
        if (.FLAG_PROCESS_START_ASYNC) {
        //By default, start asynchronously            (() -&gt; {
                try {
                    final  startResult = startProcess(,
                            entryPoint, app, , gids, runtimeFlags, mountExternal,
                            , requiredAbi, instructionSet, invokeWith, );
                    synchronized (mService) {
                        handleProcessStartedLocked(app, startResult, startSeq);
                    }
                } catch (RuntimeException e) {
                    synchronized (mService) {
                        (, "Failure starting process "
                                + , e);
                        (startSeq);
                         = false;
                        (,
                                (),
                                false, false, true, false, false, , "start failure");
                    }
                }
            });
            return true;
        } else {
            try {
                final  startResult = startProcess(hostingRecord,
                        entryPoint, app,
                        uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
                        invokeWith, startTime);
                handleProcessStartedLocked(app, , ,
                        startSeq, false);
            } catch (RuntimeException e) {
                (, "Failure starting process "
                        + , e);
                 = false;
                (, (),
                        false, false, true, false, false, , "start failure");
            }
            return  &gt; 0;
        }
    }

Keep watchingstartProcessmethod:

private  startProcess(HostingRecord hostingRecord, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
            long startTime) {
        try {
            final  startResult;
            //hostingRecord initialization parameter is null, and the mHostingZygote property is not specified, so the last branch will be entered.            if (()) {
                startResult = startWebView(entryPoint,
                        , uid, uid, gids, runtimeFlags, mountExternal,
                        , seInfo, requiredAbi, instructionSet,
                        , null, ,
                        new String[] {PROC_START_SEQ_IDENT + });
            } else if (()) {
                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
                startResult = ().start(entryPoint,
                        , uid, uid, gids, runtimeFlags, mountExternal,
                        , seInfo, requiredAbi, instructionSet,
                        , null, ,
                        /*useUsapPool=*/ false,
                        new String[] {PROC_START_SEQ_IDENT + });
            } else {
                startResult = (entryPoint,
                        , uid, uid, gids, runtimeFlags, mountExternal,
                        , seInfo, requiredAbi, instructionSet,
                        , invokeWith, ,
                        new String[] {PROC_START_SEQ_IDENT + });
            }
            checkSlow(startTime, "startProcess: returned from zygote!");
            return startResult;
        } finally {
            (Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

ProcessThe source code location isframeworks/base/core/java/android/os/

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

inZYGOTE_PROCESSIt's oneZygoteProcessThe object, which actually callsZygoteProcessstart method in.

public final  start(@NonNull final String processClass,
                                                  final String niceName,
                                                  int uid, int gid, @Nullable int[] gids,
                                                  int runtimeFlags, int mountExternal,
                                                  int targetSdkVersion,
                                                  @Nullable String seInfo,
                                                  @NonNull String abi,
                                                  @Nullable String instructionSet,
                                                  @Nullable String appDataDir,
                                                  @Nullable String invokeWith,
                                                  @Nullable String packageName,
                                                  boolean useUsapPool,
                                                  @Nullable String[] zygoteArgs) {
        // TODO (chriswailes): Is there a better place to check this value?
        if (fetchUsapPoolEnabledPropWithMinInterval()) {
            informZygotesOfUsapPoolStatus();
        }
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                    packageName, useUsapPool, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            (LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }
 private  startViaZygote(@NonNull final String processClass,
                                                      @Nullable final String niceName,
                                                      final int uid, final int gid,
                                                      @Nullable final int[] gids,
                                                      int runtimeFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      @Nullable String seInfo,
                                                      @NonNull String abi,
                                                      @Nullable String instructionSet,
                                                      @Nullable String appDataDir,
                                                      @Nullable String invokeWith,
                                                      boolean startChildZygote,
                                                      @Nullable String packageName,
                                                      boolean useUsapPool,
                                                      @Nullable String[] extraArgs)
                                                      throws ZygoteStartFailedEx {
        ArrayList<String> argsForZygote = new ArrayList<>();
        // --runtime-args, --setuid=, --setgid=,
        // and --setgroups= must go first
        ("--runtime-args");
        ("--setu--setg--runtime-flags=" + runtimeFlags);
        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
            ("--mount-external-default");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
            ("--mount-external-read");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
            ("--mount-external-write");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
            ("--mount-external-full");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
            ("--mount-external-installer");
        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_LEGACY) {
            ("--mount-external-legacy");
        }
        ("--target-sdk-version=" + targetSdkVersion);
        // --setgroups is a comma-separated list
        if (gids != null &&  > 0) {
            StringBuilder sb = new StringBuilder();
            ("--setgroups=");
            int sz = ;
            for (int i = 0; i < sz; i++) {
                if (i != 0) {
                    (',');
                }
                (gids[i]);
            }
            (());
        }
        if (niceName != null) {
            ("--nice-name=" + niceName);
        }
        if (seInfo != null) {
            ("--seinfo=" + seInfo);
        }
        if (instructionSet != null) {
            ("--instruction-set=" + instructionSet);
        }
        if (appDataDir != null) {
            ("--app-data-dir=" + appDataDir);
        }
        if (invokeWith != null) {
            ("--invoke-with");
            (invokeWith);
        }
        if (startChildZygote) {
            ("--start-child-zygote");
        }
        if (packageName != null) {
            ("--package-name=" + packageName);
        }
        (processClass);
        if (extraArgs != null) {
            (argsForZygote, extraArgs);
        }
        synchronized(mLock) {
            // The USAP pool can not be used if the application will not use the systems graphics
            // driver.  If that driver is requested use the Zygote application start path.
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                              useUsapPool,
                                              argsForZygote);
        }
    }

It is worth noting that the above code isopenZygoteSocketIfNeededThis method uses socket communication method and attempts to connect to the path of/dev/socket/, name iszygoteserver side.

zygoteSendArgsAndGetResultThe method is actually calledattemptZygoteSendArgsAndGetResultMethod, the content is as follows:

 private  attemptZygoteSendArgsAndGetResult(
            ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
        try {
            final BufferedWriter zygoteWriter = ;
            final DataInputStream zygoteInputStream = ;
            (msgStr);
            ();
            // 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 = new ();
             = ();
             = ();
            if ( < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            return result;
        } catch (IOException ex) {
            ();
            (LOG_TAG, "IO Exception while communicating with Zygote - "
                    + ());
            throw new ZygoteStartFailedEx(ex);
        }
    }

Please note the above methodzygoteWriterThis variable is responsible for sending process startup parameters to the server, and the server will incubate the process. So, which category is this server?

The answer isThe source code path isframeworks/base/core/java/com/android/internal/os/.

ZygoteServerWhen the system starts, a Socket server will be created to receive the client's fork requests.ZygoteServerAfter the initialization is completed, it will be calledrunSelectLoopMethod, used to process client messages. When the client requests the fork process,runSelectLoopThe method will be passed toZygoteConnectionClassicprocessOneCommandMethod to handle.

(ps: For more information about Zegote, please refer to the articleAndroid 10 Startup Analysis Chapter Zygote

 Runnable processOneCommand(ZygoteServer zygoteServer) {
        String args[];
        ZygoteArguments parsedArgs = null;
        FileDescriptor[] descriptors;
       ...
        pid = (, , ,
                , rlimits, , ,
                , fdsToClose, fdsToIgnore, ,
                , , );
        try {
            if (pid == 0) {
                // in child
                ();
                ();
                (serverPipeFd);
                serverPipeFd = null;
                return handleChildProc(parsedArgs, descriptors, childPipeFd,
                        );
            } else {
                // In the parent. A pid < 0 indicates a failure and will be handled in
                // handleParentProc.
                (childPipeFd);
                childPipeFd = null;
                handleParentProc(pid, descriptors, serverPipeFd);
                return null;
            }
        } finally {
            (childPipeFd);
            (serverPipeFd);
        }
    }
 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) {
        ();
        // 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) {
            (targetSdkVersion);
            (true, runtimeFlags);
            // Note that this event ends at the end of handleChildProc,
            (Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
        }
        ();
        return pid;
    }

The aboveZygoteIn this process, the class mainly does the following work:

  • Calling dalvikZygoteHooksofpreFrokPreprocessing mainly involves stopping 4 Daemon subthreads, HeapTaskDaemon, ReferenceQueueDaemon, FinalizerDaemon, FinalizerWatchdogDaemon, and waiting for all subthreads to end, and finally completing the initialization of the gc heap.
  • Callcom_android_internal_os_zygote.cppIn-housenativeForkAndSpecializeThe main work is to fork a child process through the linux mechanism, as well as some resource processing of the process, selinux permission processing, and some processing of the JAVA heap thread.
  • CallZygoteHooksofpostForkCommonMethod, start 4 Daemon threads of Zygote.

ExecutionforkAndSpecializeAfter the method, the code will continue to executeZygoteConnectionIn-househandleChildProcThis method.

private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
           FileDescriptor pipeFd, boolean isZygote) {
      ...
       if ( != null) {
           (,
                   , ,
                   (),
                   pipeFd, );
           // Should not get here.
           throw new IllegalStateException(" unexpectedly returned");
       } else {
           if (!isZygote) {
           //Enter this branch               return (,
                       , null /* classLoader */);
           } else {
               return (,
                       , null /* classLoader */);
           }
       }
   }
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {
         ...
         //Initialize the running environment        ();
        //Start binder thread pool        ();
        //Program entry function        return (targetSdkVersion, argv, classLoader);
    }

This method

protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {
        // If the application calls (), terminate the process
        // immediately without running any shutdown hooks.  It is not possible to
        // shutdown an Android application gracefully.  Among other things, the
        // Android runtime shutdown hooks close the Binder driver, which can cause
        // leftover running threads to crash before the process actually exits.
        nativeSetExitWithoutCleanup(true);
        // We want to be fairly aggressive about heap utilization, to avoid
        // holding on to a lot of memory that isn't needed.
        ().setTargetHeapUtilization(0.75f);
        ().setTargetSdkVersion(targetSdkVersion);
        final Arguments args = new Arguments(argv);
        // The end of of the RuntimeInit event (see #zygoteInit).
        (Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // Remaining arguments are passed to the start class's static main
        return findStaticMain(, , classLoader);
    }
 protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;
        try {
            cl = (className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    "Missing class when invoking static main " + className,
                    ex);
        }
        Method m;
        try {
            m = ("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }
        int modifiers = ();
        if (! ((modifiers) && (modifiers))) {
            throw new RuntimeException(
                    "Main method is not public and static on " + className);
        }
        /*
         * This throw gets caught in (), which responds
         * by invoking the exception's run() method. This arrangement
         * clears up all the stack frames that were required in setting
         * up the process.
         */
        return new MethodAndArgsCaller(m, argv);
    }

Here is obtained by reflectionclassNameThe main method in the variable and passed to the MethodAndArgsCaller to construct a Runnable. Just execute this Runnable and the execution will beginclassNamemain method in variables.

classNameWhat exactly is the value of a variable?

Remember what we emphasized in the previous articleentryPointIs this variable? Readers who don’t remember please go back and check it out.It is the target class we want to execute.

ActivityThreadThe call of the main method indicates that the application has completed the process creation and initialization process.ApplicationandActivitycreation and startup process. In the next article, we will continue to explore the rest.

Finally, let’s use a chart to summarize the above process:

[]
(currentUserId, "systemReady");
   |
[]
startHomeOnDisplay()		     //Get the intent of the first page of the lancher, its category is Intent.CATEGORY_HOME   |
[]
startHomeActivity()
   |
[]
execute()			     //Start to enter the activity startup processstartActivity()
startActivityUnchecked()
   |
[]
resumeFocusedStacksTopActivities()   //Start the top-level activity of the target ActivityStack   |
[]
resumeTopActivityInnerLocked()      //Pause the previous activity and load the theme background of manifest   |
[]
startSpecificActivityLocked()       //Check whether the process has been started and start a new process through the handler message mechanism   |
[]
startProcessLocked()
   |
[]
startProcessLocked()               //Specifies that the program starts the entry class after the process is created successfullystartProcess()
   |
[]
start()
   |
[]
start()
startViaZygote()		   //Initialize the startup parameters of Zygote and connect to the ZygoteServer serverattemptZygoteSendArgsAndGetResult()
   |
[]
processOneCommand()
   |		|
   |  []
   |  forkAndSpecialize()
   |        |
   |  []
   |	  preFrok()		   //Stop 4 child threads, wait for all child threads to end, and finally complete the initialization of the gc heap   |		|
   |  [com_android_internal_os_zygote.cpp]	
   |  nativeForkAndSpecialize()       //Fork child process   |        |
   |  []
   |  postForkCommon()                //Start 4 Daemon threads of Zygote   |
[]
handleChildProc()
   |
[]
zygoteInit()				//Initialize the running environment and start the binder thread pool   |
[]
applicationInit()
   |
[]
main()

The above is the detailed content of Android10 App launch analysis process to create source code analysis. For more information about Android10 App launch process creation, please follow my other related articles!