Re learn Android Framework layer application process creation process

Posted by Push Eject on Wed, 22 Dec 2021 15:25:52 +0100

After the Android system is started, the first Android application Launcher will be started. Then start other applications under user operation. The two processes are roughly the same. This paper mainly analyzes the startup process of launching Launcher.

Android system source code version: 9.0 0_ r3

The overall flow chart is as follows:

Zygote startup process

After the Android Kernel is started, start the first process init, which will resolve init. * RC file starts the process zygote.

/system/core/rootdir/init.zygote32_64.rc

1 service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
2    class main
3    priority -20
4    user root
5    group root readproc reserved_disk
6    socket zygote stream 660 root system
7    onrestart write /sys/android_power/request_state wake
8    onrestart write /sys/power/state on
9    onrestart restart audioserver
10    onrestart restart cameraserver
11    onrestart restart media
12    onrestart restart netd
13    onrestart restart wificond
14    writepid /dev/cpuset/foreground/tasks
15
16 service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
17    class main
18    priority -20
19    user root
20    group root readproc reserved_disk
21    socket zygote_secondary stream 660 root system
22    onrestart restart zygote
23    writepid /dev/cpuset/foreground/tasks

init started app_process32 or app_ The process64 and -- zygote parameters indicate that the zygote process is started. App here_ process32,app_process64 represents a 32-bit zygote process and a 64 bit zygote process. It is used for compatible application startup with a 32-bit so library. In fact, we can use app in Android environment_ Success * runs a java bytecode file in Dalvik format, just like running bytecode in java on a desktop.
After zygote is started, it will complete the creation of ART virtual machine and run com android. internal. os. ZygoteInit. The key contents are as follows

/frameworks/base/cmds/app_process/app_main.cpp

349    if (zygote) {
350        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
351    } else if (className) {
352        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
353    } else {
354        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
355        app_usage();
356        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
357    }

On COM android. internal. os. Zygoteinit will complete the loading of key Java class files of the Framework in the Android environment. Create a local socket, manage it with ZygoteServer, and create a sub process system through fork call_ Server process. Turn on the listening function of the local socket ZygoteServer, that is, run the runSelectLoop method to listen for new sockets or read-write events.

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

750    public static void main(String argv[]) {
751        ZygoteServer zygoteServer = new ZygoteServer();
752
            //..... Delete irrelevant codes
832            if (startSystemServer) {
833                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
834
835                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
836                // child (system_server) process.
837                if (r != null) {
838                    r.run();
839                    return;
840                }
841            }
842
843            Log.i(TAG, "Accepting command socket connections");
844
845            // The select loop returns early in the child process after a fork and
846            // loops forever in the zygote.
847            caller = zygoteServer.runSelectLoop(abiList);
848        } catch (Throwable ex) {
849            Log.e(TAG, "System zygote died with exception", ex);
850            throw ex;
851        } finally {
852            zygoteServer.closeServerSocket();
853        }
854
855        // We're in the child process and have exited the select loop. Proceed to execute the
856        // command.
857        if (caller != null) {
858            caller.run();
859        }
860    }

Here, the local socket, namely UDS(Unix Domain Socket), is actually a way of inter process communication. For example, in Nginx and MySQL, UDS is used for inter process communication. Android has more interprocess communication than Linux, and Binder has more interprocess communication. This article will not explain Binder in detail. Just know that Binder is actually used for communication between two different processes. It is not fundamentally different from pipeline, semaphore and UDS.

system_server process startup process

system_ The server process will call com android. server. Systemserver and systemnserver will start various important services. The code is as follows

/frameworks/base/services/java/com/android/server/SystemServer.java

 // Start services.
427        try {
428            traceBeginAndSlog("StartServices");
429            startBootstrapServices();
430            startCoreServices();
431            startOtherServices();
432            SystemServerInitThreadPool.shutdown();
433        } catch (Throwable ex) {
434            Slog.e("System", "******************************************");
435            Slog.e("System", "************ Failure starting system services", ex);
436            throw ex;
437        } finally {
438            traceEnd();
439        }

The services started include ActivityManagerService, WindowManagerService, PackageManagerService, InputManagerService, Bluetooth managerservice and other service classes.
PackageManagerService will resolve the installed applications on the mobile phone, such as Apk installation location, Manifest information, resource file, class file path, etc.

system_ After server is launched, it calls the systemReady method of AMS, sends the Intent that starts Launcher, PMS will find the installation path of Launcher, and call startProcess of AMS. After a series of calls, startprocess finally uses UDS to communicate with zygote process and send corresponding commands to let zygote create child processes. AMS puts the returned child process pid and the associated app information into the mPidsSelfLocked member variable. The code is as follows

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

4530    private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
4531            long expectedStartSeq, boolean procAttached) {
4532       //Omit a lot of code
4598        synchronized (mPidsSelfLocked) {
4599            this.mPidsSelfLocked.put(pid, app);
4600            if (!procAttached) {
4601                Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
4602                msg.obj = app;
4603                mHandler.sendMessageDelayed(msg, usingWrapper
4604                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
4605            }
4606        }
4607        checkTime(app.startTime, "startProcess: done updating pids map");
4608        return true;
4609    }

Application process startup process

Here, when the local socket of ZygoteServer listens to a message, it will fork a new sub process, which inherits the resources of zygote process, such as the created ART virtual machine, which can speed up the creation and startup of applications.
The key codes are as follows

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

123    Runnable processOneCommand(ZygoteServer zygoteServer) {
124        String args[];
125        Arguments parsedArgs = null;
126        FileDescriptor[] descriptors;
127
128        try {
129            args = readArgumentList();
130            descriptors = mSocket.getAncillaryFileDescriptors();
131        } catch (IOException ex) {
132            throw new IllegalStateException("IOException on command socket", ex);
133        }
134
   //Delete irrelevant code
146        parsedArgs = new Arguments(args);
147
148        
231
232        fd = null;
233
234        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
235                parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
236                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
237                parsedArgs.instructionSet, parsedArgs.appDataDir);
            //Delete irrelevant code
238
240            if (pid == 0) {
241                // in child
242                zygoteServer.setForkChild();
243
244                zygoteServer.closeServerSocket();
245                IoUtils.closeQuietly(serverPipeFd);
246                serverPipeFd = null;
247
248                return handleChildProc(parsedArgs, descriptors, childPipeFd,
249                        parsedArgs.startChildZygote);
250            } else {
251                // In the parent. A pid < 0 indicates a failure and will be handled in
252                // handleParentProc.
253                IoUtils.closeQuietly(childPipeFd);
254                childPipeFd = null;
255                handleParentProc(pid, descriptors, serverPipeFd);
256                return null;
257            }
258     
262    }

Other processes will be resolved in the child process The parameter sent by (system_server) runs the specified class. Usually, this class is ActivityThread, and this sub process is our newly started application process. At present, this process does not know where to load the byte code and resource file corresponding to the application. After the ActivityThread is started, it will carry out inter process communication (Binder) with system_server and call AMS related functions. The code is as follows:

6478    private void attach(boolean system, long startSeq) {
6479        sCurrentActivityThread = this;
6480        mSystemThread = system;
6481        if (!system) {
6482            ViewRootImpl.addFirstDrawHandler(new Runnable() {
6483                @Override
6484                public void run() {
6485                    ensureJitEnabled();
6486                }
6487            });
6488            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
6489                                                    UserHandle.myUserId());
6490            RuntimeInit.setApplicationObject(mAppThread.asBinder());
6491            final IActivityManager mgr = ActivityManager.getService();
6492            try {
6493                mgr.attachApplication(mAppThread, startSeq);
6494            } catch (RemoteException ex) {
6495                throw ex.rethrowFromSystemServer();
6496            }

At line 6493, the ActivityThread calls Mgr attachApplication, because Binder mechanism will call attachApplication method of AMS.

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

    @Override
7932    public final void attachApplication(IApplicationThread thread, long startSeq) {
7933        synchronized (this) {
7934            int callingPid = Binder.getCallingPid();
7935            final int callingUid = Binder.getCallingUid();
7936            final long origId = Binder.clearCallingIdentity();
7937            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
7938            Binder.restoreCallingIdentity(origId);
7939        }
7940    }

The attachApplication method will obtain the pid of the starting process, find the information of the program to be started in mPidsSelfLocked through pid, send the information of the program to be started to the application process through Binder inter process communication (i.e. iaapplicationthread related methods), and the application process will load the corresponding classes.

After the application process is loaded, inform AMS that AMS will control the application process to start the Activity. Then AMS issues a command, the application process executes, and inform AMS after execution. The application process is restricted by AMS like a string puppet. (the current plug-in technology is to Hook off the corresponding interface and deceive AMS to complete the loading of the four components in the plug-in, that is, it is often said to deceive the top and deceive the bottom)

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

  private final boolean attachApplicationLocked(IApplicationThread thread,
7573            int pid, int callingUid, long startSeq) {
7574
7575        // Find the application record that is being attached...  either via
7576        // the pid if we are running in multiple processes, or just pull the
7577        // next app record if we are emulating process with anonymous threads.
7578        ProcessRecord app;
7579        long startTime = SystemClock.uptimeMillis();
7580        if (pid != MY_PID && pid >= 0) {
7581            synchronized (mPidsSelfLocked) {
7582                app = mPidsSelfLocked.get(pid);
7583            }
7584        } else {
7585            app = null;
7586        }
7587
7588        // It's possible that process called attachApplication before we got a chance to
7589        // update the internal state.
7590        if (app == null && startSeq > 0) {
7591            final ProcessRecord pending = mPendingStarts.get(startSeq);
7592            if (pending != null && pending.startUid == callingUid
7593                    && handleProcessStartedLocked(pending, pid, pending.usingWrapper,
7594                            startSeq, true)) {
7595                app = pending;
7596            }
7597        }
7598
7599        if (app == null) {
7600            Slog.w(TAG, "No pending application record for pid " + pid
7601                    + " (IApplicationThread " + thread + "); dropping process");
7602            EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
7603            if (pid > 0 && pid != MY_PID) {
7604                killProcessQuiet(pid);
7605                //TODO: killProcessGroup(app.info.uid, pid);
7606            } else {
7607                try {
7608                    thread.scheduleExit();
7609                } catch (Exception e) {
7610                    // Ignore exceptions.
7611                }
7612            }
7613            return false;
7614        }

Topics: Android