Android 源码分析(七) Launcher 桌面程序启动分析
一.前言:
init进程 –> Zygote进程 –> SystemServer进程 –> Launcher桌面程序 -> 我们的App应用
init进程:linux的根进程,android系统是基于linux系统的,因此可以算作是整个android操作系统的第一个进程;
Zygote进程:android系统的根进程,主要作用:可以作用Zygote进程fork出SystemServer进程和各种应用进程;
SystemService进程:主要是在这个进程中启动系统的各项服务,比如ActivityManagerService,PackageManagerService,WindowManagerService服务等等;
Launcher桌面程序:就是我们平时看到的桌面程序,它其实也是一个android应用程序,只不过这个应用程序是系统默认第一个启动的应用程序.在Zygote进程里等待SystemService进程启动后,创建.
二. Launcher 桌面程序启动
Launcher程序就是我们平时看到的桌面程序,它其实也是一个android应用程序,只不过这个应用程序是系统默认第一个启动的应用程序。
首先了解下,Zygote进程是如何启动一个应用的。
//ZygoteServer.java void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); fds.add(mServerSocket.getFileDescriptor()); peers.add(null); while (true) { StructPollfd[] pollFds = new StructPollfd[fds.size()]; for (int i = 0; i < pollFds.length; ++i) { pollFds[i] = new StructPollfd(); pollFds[i].fd = fds.get(i); pollFds[i].events = (short) POLLIN; } try { Os.poll(pollFds, -1); } catch (ErrnoException ex) { throw new RuntimeException("poll failed", ex); } for (int i = pollFds.length - 1; i >= 0; --i) { if ((pollFds[i].revents & POLLIN) == 0) { continue; } if (i == 0) { ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { boolean done = peers.get(i).runOnce(this); if (done) { peers.remove(i); fds.remove(i); } } } } } }
/**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
return createNewConnection(mServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
//ZygoteConnection.java /** * Constructs instance from connected socket. * * @param socket non-null; connected socket * @param abiList non-null; a list of ABIs this zygote supports. * @throws IOException */ ZygoteConnection(LocalSocket socket, String abiList) throws IOException { mSocket = socket; this.abiList = abiList; mSocketOutStream = new DataOutputStream(socket.getOutputStream()); mSocketReader = new BufferedReader( new InputStreamReader(socket.getInputStream()), 256); mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS); try { peer = mSocket.getPeerCredentials(); } catch (IOException ex) { Log.e(TAG, "Cannot read peer credentials", ex); throw ex; } } /** * Reads one start command from the command socket. If successful, * a child is forked and a {@link Zygote.MethodAndArgsCaller} * exception is thrown in that child while in the parent process, * the method returns normally. On failure, the child is not * spawned and messages are printed to the log and stderr. Returns * a boolean status value indicating whether an end-of-file on the command * socket has been encountered. * * @return false if command socket should continue to be read from, or * true if an end-of-file has been encountered. * @throws Zygote.MethodAndArgsCaller trampoline to invoke main() * method in child process */ boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller { String args[]; Arguments parsedArgs = null; FileDescriptor[] descriptors; try { args = readArgumentList(); descriptors = mSocket.getAncillaryFileDescriptors(); } catch (IOException ex) { Log.w(TAG, "IOException on command socket " + ex.getMessage()); closeSocket(); return true; } if (args == null) { // EOF reached. closeSocket(); return true; } /** the stderr of the most recent request, if avail */ PrintStream newStderr = null; if (descriptors != null && descriptors.length >= 3) { newStderr = new PrintStream( new FileOutputStream(descriptors[2])); } int pid = -1; FileDescriptor childPipeFd = null; FileDescriptor serverPipeFd = null; try { parsedArgs = new Arguments(args); if (parsedArgs.abiListQuery) { return handleAbiListQuery(); } if (parsedArgs.preloadDefault) { return handlePreload(); } if (parsedArgs.preloadPackage != null) { return handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs, parsedArgs.preloadPackageCacheKey); } if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) { throw new ZygoteSecurityException("Client may not specify capabilities: " + "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) + ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities)); } applyUidSecurityPolicy(parsedArgs, peer); applyInvokeWithSecurityPolicy(parsedArgs, peer); applyDebuggerSystemProperty(parsedArgs); applyInvokeWithSystemProperty(parsedArgs); int[][] rlimits = null; if (parsedArgs.rlimits != null) { rlimits = parsedArgs.rlimits.toArray(intArray2d); } int[] fdsToIgnore = null; if (parsedArgs.invokeWith != null) { FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC); childPipeFd = pipeFds[1]; serverPipeFd = pipeFds[0]; Os.fcntlInt(childPipeFd, F_SETFD, 0); fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() }; } /** * In order to avoid leaking descriptors to the Zygote child, * the native code must close the two Zygote socket descriptors * in the child process before it switches from Zygote-root to * the UID and privileges of the application being launched. * * In order to avoid "bad file descriptor" errors when the * two LocalSocket objects are closed, the Posix file * descriptors are released via a dup2() call which closes * the socket and substitutes an open descriptor to /dev/null. */ int [] fdsToClose = { -1, -1 }; FileDescriptor fd = mSocket.getFileDescriptor(); if (fd != null) { fdsToClose[0] = fd.getInt$(); } fd = zygoteServer.getServerSocketFileDescriptor(); if (fd != null) { fdsToClose[1] = fd.getInt$(); } fd = null; pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet, parsedArgs.appDataDir); } catch (ErrnoException ex) { logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (IllegalArgumentException ex) { logAndPrintError(newStderr, "Invalid zygote arguments", ex); } catch (ZygoteSecurityException ex) { logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex); } try { if (pid == 0) { // in child zygoteServer.closeServerSocket(); IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); // should never get here, the child is expected to either // throw Zygote.MethodAndArgsCaller or exec(). return true; } else { // in parent...pid of < 0 means failure IoUtils.closeQuietly(childPipeFd); childPipeFd = null; return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); } } finally { IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); } }
//Zygote.java public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, String instructionSet, String appDataDir) { VM_HOOKS.preFork(); // Resets nice priority for zygote process. resetNicePriority(); int pid = nativeForkAndSpecialize( uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, instructionSet, appDataDir); // Enable tracing as soon as possible for the child process. if (pid == 0) { Trace.setTracingEnabled(true); // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); } VM_HOOKS.postForkCommon(); return pid; }
//ZygoteConnection.java /** * Handles post-fork setup of child proc, closing sockets as appropriate, * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller * if successful or returning if failed. * * @param parsedArgs non-null; zygote args * @param descriptors null-ok; new file descriptors for stdio if available. * @param pipeFd null-ok; pipe for communication back to Zygote. * @param newStderr null-ok; stream to use for stderr until stdio * is reopened. * * @throws Zygote.MethodAndArgsCaller on success to * trampoline to code that invokes static main. */ private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws Zygote.MethodAndArgsCaller { /** * By the time we get here, the native code has closed the two actual Zygote * socket connections, and substituted /dev/null in their place. The LocalSocket * objects still need to be closed properly. */ closeSocket(); if (descriptors != null) { try { Os.dup2(descriptors[0], STDIN_FILENO); Os.dup2(descriptors[1], STDOUT_FILENO); Os.dup2(descriptors[2], STDERR_FILENO); for (FileDescriptor fd: descriptors) { IoUtils.closeQuietly(fd); } newStderr = System.err; } catch (ErrnoException ex) { Log.e(TAG, "Error reopening stdio", ex); } } if (parsedArgs.niceName != null) { Process.setArgV0(parsedArgs.niceName); } // End of the postFork event. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); if (parsedArgs.invokeWith != null) { WrapperInit.execApplication(parsedArgs.invokeWith, parsedArgs.niceName, parsedArgs.targetSdkVersion, VMRuntime.getCurrentInstructionSet(), pipeFd, parsedArgs.remainingArgs); } else { ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, null /* classLoader */); } }
前一篇文章我们知道在SystemServer进程的启动过程中会调用其main静态方法,开始执行整个SystemServer的启动流程。在调用startOtherService方法中就会通过调用mActivityManagerService.systemReady()方法。
//SystemServer.java private void startOtherServices() { ...... // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state // where third party code can really run (but before it has actually // started launching the initial applications), for us to complete our // initialization. mActivityManagerService.systemReady() ...... }
//ActivityManagerService.java public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) { ...... startHomeActivityLocked(currentUserId, "systemReady"); ...... } boolean startHomeActivityLocked(int userId, String reason) { if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopAction == null) { // We are running in factory test mode, but unable to find // the factory test app, so just sit around displaying the // error message and don't try to start anything. return false; } Intent intent = getHomeIntent(); ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId); if (aInfo != null) { intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); // Don't do this if the home app is currently being // instrumented. aInfo = new ActivityInfo(aInfo); aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId); ProcessRecord app = getProcessRecordLocked(aInfo.processName, aInfo.applicationInfo.uid, true); if (app == null || app.instr == null) { intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid); // For ANR debugging to verify if the user activity is the one that actually // launched. final String myReason = reason + ":" + userId + ":" + resolvedUserId; mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason); } } else { Slog.wtf(TAG, "No home screen found for " + intent, new Throwable()); } return true; } Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } return intent; }
//ActivityStarter.java void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) { mSupervisor.moveHomeStackTaskToTop(reason); mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/, false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/, null /*container*/, null /*inTask*/, "startHomeActivity: " + reason); if (mSupervisor.inResumeTopActivity) { // 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. mSupervisor.scheduleResumeTopActivities(); } }
后面就进入了Activity的启动流程了。