diff options
| author | 2017-09-15 01:09:04 +0000 | |
|---|---|---|
| committer | 2017-09-15 01:09:04 +0000 | |
| commit | dd3f6046b3ba6d030328e8e8a8c0131a0a4558b3 (patch) | |
| tree | 54bfbb67ec6145eb15e2269af938e8e7e2fa476e | |
| parent | add03c674257bee994dda96b00b528e23324fa1c (diff) | |
| parent | 66d7275d393209bea48711df633b023d987652bb (diff) | |
Merge changes Id931d441,I83faf974 am: 89c94f67b4
am: 66d7275d39
Change-Id: Ic0d6dba2b5560b335b3be8e27a88c01c960ec0aa
| -rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 55 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/WebViewZygoteInit.java | 24 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/WrapperInit.java | 58 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/Zygote.java | 35 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteConnection.java | 245 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 54 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteServer.java | 70 |
7 files changed, 278 insertions, 263 deletions
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index f4be1289eb70..66475e445efa 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -31,6 +31,7 @@ import android.util.Slog; import com.android.internal.logging.AndroidConfig; import com.android.server.NetworkManagementSocketTagger; import dalvik.system.VMRuntime; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.TimeZone; @@ -228,8 +229,8 @@ public class RuntimeInit { * @param argv Argument vector for main() * @param classLoader the classLoader to load {@className} with */ - private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) - throws Zygote.MethodAndArgsCaller { + private static Runnable findStaticMain(String className, String[] argv, + ClassLoader classLoader) { Class<?> cl; try { @@ -263,7 +264,7 @@ public class RuntimeInit { * clears up all the stack frames that were required in setting * up the process. */ - throw new Zygote.MethodAndArgsCaller(m, argv); + return new MethodAndArgsCaller(m, argv); } public static final void main(String[] argv) { @@ -286,8 +287,8 @@ public class RuntimeInit { if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); } - protected static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) - throws Zygote.MethodAndArgsCaller { + protected static Runnable applicationInit(int targetSdkVersion, String[] argv, + ClassLoader classLoader) { // If the application calls System.exit(), terminate the process // immediately without running any shutdown hooks. It is not possible to // shutdown an Android application gracefully. Among other things, the @@ -300,20 +301,13 @@ public class RuntimeInit { VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); - final Arguments args; - try { - args = new Arguments(argv); - } catch (IllegalArgumentException ex) { - Slog.e(TAG, ex.getMessage()); - // let the process exit - return; - } + final Arguments args = new Arguments(argv); // The end of of the RuntimeInit event (see #zygoteInit). Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // Remaining arguments are passed to the start class's static main - invokeStaticMain(args.startClass, args.startArgs, classLoader); + return findStaticMain(args.startClass, args.startArgs, classLoader); } /** @@ -422,4 +416,37 @@ public class RuntimeInit { System.arraycopy(args, curArg, startArgs, 0, startArgs.length); } } + + /** + * Helper class which holds a method and arguments and can call them. This is used as part of + * a trampoline to get rid of the initial process setup stack frames. + */ + 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() { + 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 new RuntimeException(ex); + } + } + } } diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index 58e4a3ed8deb..7f46a0c1abde 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -69,8 +69,7 @@ class WebViewZygoteInit { } @Override - protected boolean handlePreloadPackage(String packagePath, String libsPath, - String cacheKey) { + protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) { Log.i(TAG, "Beginning package preload"); // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that // our children will reuse the same classloader instead of creating their own. @@ -106,12 +105,10 @@ class WebViewZygoteInit { DataOutputStream socketOut = getSocketOutputStream(); socketOut.writeInt(preloadSucceeded ? 1 : 0); } catch (IOException ioe) { - Log.e(TAG, "Error writing to command socket", ioe); - return true; + throw new IllegalStateException("Error writing to command socket", ioe); } Log.i(TAG, "Package preload done"); - return false; } } @@ -125,16 +122,23 @@ class WebViewZygoteInit { throw new RuntimeException("Failed to setpgid(0,0)", ex); } + final Runnable caller; try { sServer.registerServerSocket("webview_zygote"); - sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS)); - sServer.closeServerSocket(); - } catch (Zygote.MethodAndArgsCaller caller) { - caller.run(); + // The select loop returns early in the child process after a fork and + // loops forever in the zygote. + caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS)); } catch (RuntimeException e) { Log.e(TAG, "Fatal exception:", e); + throw e; + } finally { + sServer.closeServerSocket(); } - System.exit(0); + // We're in the child process and have exited the select loop. Proceed to execute the + // command. + if (caller != null) { + caller.run(); + } } } diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 608bc9ff33d3..89328b21ccf5 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -25,7 +25,6 @@ import android.system.StructCapUserData; import android.system.StructCapUserHeader; import android.util.BootTimingsTraceLog; import android.util.Slog; -import com.android.internal.os.Zygote.MethodAndArgsCaller; import dalvik.system.VMRuntime; import java.io.DataOutputStream; import java.io.FileDescriptor; @@ -61,37 +60,35 @@ public class WrapperInit { * @param args The command-line arguments. */ public static void main(String[] args) { - try { - // Parse our mandatory arguments. - int fdNum = Integer.parseInt(args[0], 10); - int targetSdkVersion = Integer.parseInt(args[1], 10); + // Parse our mandatory arguments. + int fdNum = Integer.parseInt(args[0], 10); + int targetSdkVersion = Integer.parseInt(args[1], 10); - // Tell the Zygote what our actual PID is (since it only knows about the - // wrapper that it directly forked). - if (fdNum != 0) { - try { - FileDescriptor fd = new FileDescriptor(); - fd.setInt$(fdNum); - DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); - os.writeInt(Process.myPid()); - os.close(); - IoUtils.closeQuietly(fd); - } catch (IOException ex) { - Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); - } + // Tell the Zygote what our actual PID is (since it only knows about the + // wrapper that it directly forked). + if (fdNum != 0) { + try { + FileDescriptor fd = new FileDescriptor(); + fd.setInt$(fdNum); + DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); + os.writeInt(Process.myPid()); + os.close(); + IoUtils.closeQuietly(fd); + } catch (IOException ex) { + Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); } + } - // Mimic system Zygote preloading. - ZygoteInit.preload(new BootTimingsTraceLog("WrapperInitTiming", - Trace.TRACE_TAG_DALVIK)); + // Mimic system Zygote preloading. + ZygoteInit.preload(new BootTimingsTraceLog("WrapperInitTiming", + Trace.TRACE_TAG_DALVIK)); - // Launch the application. - String[] runtimeArgs = new String[args.length - 2]; - System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length); - WrapperInit.wrapperInit(targetSdkVersion, runtimeArgs); - } catch (Zygote.MethodAndArgsCaller caller) { - caller.run(); - } + // Launch the application. + String[] runtimeArgs = new String[args.length - 2]; + System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length); + Runnable r = wrapperInit(targetSdkVersion, runtimeArgs); + + r.run(); } /** @@ -142,8 +139,7 @@ public class WrapperInit { * @param targetSdkVersion target SDK version * @param argv arg strings */ - private static void wrapperInit(int targetSdkVersion, String[] argv) - throws Zygote.MethodAndArgsCaller { + private static Runnable wrapperInit(int targetSdkVersion, String[] argv) { if (RuntimeInit.DEBUG) { Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from wrapper"); } @@ -165,7 +161,7 @@ public class WrapperInit { // Perform the same initialization that would happen after the Zygote forks. Zygote.nativePreApplicationInit(); - RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); + return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); } /** diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 4668d5c32695..d6117688a5f5 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -221,39 +221,4 @@ public final class Zygote { command.append(" '").append(arg.replace("'", "'\\''")).append("'"); } } - - /** - * Helper exception class which holds a method and arguments and - * can call them. This is used as part of a trampoline to get rid of - * the initial process setup stack frames. - */ - public static class MethodAndArgsCaller extends Exception - 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() { - 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 new RuntimeException(ex); - } - } - } } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index d459bc31beba..0bb7326a682b 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -30,7 +30,6 @@ import android.net.Credentials; import android.net.LocalSocket; import android.os.FactoryTest; import android.os.Process; -import android.os.SELinux; import android.os.SystemProperties; import android.os.Trace; import android.system.ErrnoException; @@ -42,14 +41,13 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.EOFException; import java.io.FileDescriptor; -import java.io.FileOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import libcore.io.IoUtils; /** @@ -73,6 +71,7 @@ class ZygoteConnection { private final BufferedReader mSocketReader; private final Credentials peer; private final String abiList; + private boolean isEof; /** * Constructs instance from connected socket. @@ -99,6 +98,8 @@ class ZygoteConnection { Log.e(TAG, "Cannot read peer credentials", ex); throw ex; } + + isEof = false; } /** @@ -111,21 +112,14 @@ class ZygoteConnection { } /** - * 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. + * Reads one start command from the command socket. If successful, a child is forked and a + * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child + * process. {@code null} is always returned in the parent process (the zygote). * - * @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 + * If the client closes the socket, an {@code EOF} condition is set, which callers can test + * for by calling {@code ZygoteConnection.isClosedByPeer}. */ - boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller { - + Runnable processOneCommand(ZygoteServer zygoteServer) { String args[]; Arguments parsedArgs = null; FileDescriptor[] descriptors; @@ -134,130 +128,120 @@ class ZygoteConnection { args = readArgumentList(); descriptors = mSocket.getAncillaryFileDescriptors(); } catch (IOException ex) { - Log.w(TAG, "IOException on command socket " + ex.getMessage()); - closeSocket(); - return true; + throw new IllegalStateException("IOException on command socket", ex); } + // readArgumentList returns null only when it has reached EOF with no available + // data to read. This will only happen when the remote socket has disconnected. 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])); + isEof = true; + return null; } int pid = -1; FileDescriptor childPipeFd = null; FileDescriptor serverPipeFd = null; - try { - parsedArgs = new Arguments(args); + parsedArgs = new Arguments(args); - if (parsedArgs.abiListQuery) { - return handleAbiListQuery(); - } + if (parsedArgs.abiListQuery) { + handleAbiListQuery(); + return null; + } - if (parsedArgs.preloadDefault) { - return handlePreload(); - } + if (parsedArgs.preloadDefault) { + handlePreload(); + return null; + } - if (parsedArgs.preloadPackage != null) { - return handlePreloadPackage(parsedArgs.preloadPackage, - parsedArgs.preloadPackageLibs, parsedArgs.preloadPackageCacheKey); - } + if (parsedArgs.preloadPackage != null) { + handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs, + parsedArgs.preloadPackageCacheKey); + return null; + } - 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)); - } + 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); + applyUidSecurityPolicy(parsedArgs, peer); + applyInvokeWithSecurityPolicy(parsedArgs, peer); - applyDebuggerSystemProperty(parsedArgs); - applyInvokeWithSystemProperty(parsedArgs); + applyDebuggerSystemProperty(parsedArgs); + applyInvokeWithSystemProperty(parsedArgs); - int[][] rlimits = null; + int[][] rlimits = null; - if (parsedArgs.rlimits != null) { - rlimits = parsedArgs.rlimits.toArray(intArray2d); - } + if (parsedArgs.rlimits != null) { + rlimits = parsedArgs.rlimits.toArray(intArray2d); + } - int[] fdsToIgnore = null; + int[] fdsToIgnore = null; - if (parsedArgs.invokeWith != null) { + if (parsedArgs.invokeWith != null) { + try { 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$() }; + fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()}; + } catch (ErrnoException errnoEx) { + throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx); } + } - /** - * 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. - */ + /** + * 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 }; + int [] fdsToClose = { -1, -1 }; - FileDescriptor fd = mSocket.getFileDescriptor(); + FileDescriptor fd = mSocket.getFileDescriptor(); - if (fd != null) { - fdsToClose[0] = fd.getInt$(); - } + if (fd != null) { + fdsToClose[0] = fd.getInt$(); + } - fd = zygoteServer.getServerSocketFileDescriptor(); + fd = zygoteServer.getServerSocketFileDescriptor(); - if (fd != null) { - fdsToClose[1] = fd.getInt$(); - } + if (fd != null) { + fdsToClose[1] = fd.getInt$(); + } - fd = null; + fd = null; - pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, - parsedArgs.runtimeFlags, 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); - } + pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, + parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, + parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet, + parsedArgs.appDataDir); try { if (pid == 0) { // in child + zygoteServer.setForkChild(); + 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; + return handleChildProc(parsedArgs, descriptors, childPipeFd); } else { - // in parent...pid of < 0 means failure + // In the parent. A pid < 0 indicates a failure and will be handled in + // handleParentProc. IoUtils.closeQuietly(childPipeFd); childPipeFd = null; - return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); + handleParentProc(pid, descriptors, serverPipeFd); + return null; } } finally { IoUtils.closeQuietly(childPipeFd); @@ -265,15 +249,13 @@ class ZygoteConnection { } } - private boolean handleAbiListQuery() { + private void handleAbiListQuery() { try { final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII); mSocketOutStream.writeInt(abiListBytes.length); mSocketOutStream.write(abiListBytes); - return false; } catch (IOException ioe) { - Log.e(TAG, "Error writing to command socket", ioe); - return true; + throw new IllegalStateException("Error writing to command socket", ioe); } } @@ -283,7 +265,7 @@ class ZygoteConnection { * if no preload was initiated. The latter implies that the zygote is not configured to load * resources lazy or that the zygote has already handled a previous request to handlePreload. */ - private boolean handlePreload() { + private void handlePreload() { try { if (isPreloadComplete()) { mSocketOutStream.writeInt(1); @@ -291,11 +273,8 @@ class ZygoteConnection { preload(); mSocketOutStream.writeInt(0); } - - return false; } catch (IOException ioe) { - Log.e(TAG, "Error writing to command socket", ioe); - return true; + throw new IllegalStateException("Error writing to command socket", ioe); } } @@ -311,7 +290,7 @@ class ZygoteConnection { return mSocketOutStream; } - protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) { + protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) { throw new RuntimeException("Zyogte does not support package preloading"); } @@ -327,6 +306,10 @@ class ZygoteConnection { } } + boolean isClosedByPeer() { + return isEof; + } + /** * Handles argument parsing for args related to the zygote spawner. * @@ -757,15 +740,9 @@ class ZygoteConnection { * @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 { + private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, + FileDescriptor pipeFd) { /** * 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 @@ -782,7 +759,6 @@ class ZygoteConnection { for (FileDescriptor fd: descriptors) { IoUtils.closeQuietly(fd); } - newStderr = System.err; } catch (ErrnoException ex) { Log.e(TAG, "Error reopening stdio", ex); } @@ -799,9 +775,12 @@ class ZygoteConnection { parsedArgs.niceName, parsedArgs.targetSdkVersion, VMRuntime.getCurrentInstructionSet(), pipeFd, parsedArgs.remainingArgs); + + // Should not get here. + throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned"); } else { - ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, - parsedArgs.remainingArgs, null /* classLoader */); + return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, + null /* classLoader */); } } @@ -813,13 +792,8 @@ class ZygoteConnection { * @param descriptors null-ok; file descriptors for child's new stdio if * specified. * @param pipeFd null-ok; pipe for communication with child. - * @param parsedArgs non-null; zygote args - * @return true for "exit command loop" and false for "continue command - * loop" */ - private boolean handleParentProc(int pid, - FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) { - + private void handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd) { if (pid > 0) { setChildPgid(pid); } @@ -911,11 +885,8 @@ class ZygoteConnection { mSocketOutStream.writeInt(pid); mSocketOutStream.writeBoolean(usingWrapper); } catch (IOException ex) { - Log.e(TAG, "Error writing to command socket", ex); - return true; + throw new IllegalStateException("Error writing to command socket", ex); } - - return false; } private void setChildPgid(int pid) { @@ -931,20 +902,4 @@ class ZygoteConnection { + "normal if peer is not in our session"); } } - - /** - * Logs an error message and prints it to the specified stream, if - * provided - * - * @param newStderr null-ok; a standard error stream - * @param message non-null; error message - * @param ex null-ok an exception - */ - private static void logAndPrintError (PrintStream newStderr, - String message, Throwable ex) { - Log.e(TAG, message, ex); - if (newStderr != null) { - newStderr.println(message + (ex == null ? "" : ex)); - } - } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ce6dcf1fb985..6d8b81170e21 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -24,7 +24,6 @@ import android.content.res.TypedArray; import android.icu.impl.CacheValue; import android.icu.text.DecimalFormatSymbols; import android.icu.util.ULocale; -import android.net.LocalServerSocket; import android.opengl.EGL14; import android.os.Build; import android.os.IInstalld; @@ -452,10 +451,7 @@ public class ZygoteInit { /** * Finish remaining work for the newly forked system server process. */ - private static void handleSystemServerProcess( - ZygoteConnection.Arguments parsedArgs) - throws Zygote.MethodAndArgsCaller { - + private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) { // set umask to 0077 so new files and directories will default to owner-only permissions. Os.umask(S_IRWXG | S_IRWXO); @@ -501,6 +497,8 @@ public class ZygoteInit { WrapperInit.execApplication(parsedArgs.invokeWith, parsedArgs.niceName, parsedArgs.targetSdkVersion, VMRuntime.getCurrentInstructionSet(), null, args); + + throw new IllegalStateException("Unexpected return from WrapperInit.execApplication"); } else { ClassLoader cl = null; if (systemServerClasspath != null) { @@ -512,7 +510,7 @@ public class ZygoteInit { /* * Pass the remaining arguments to SystemServer. */ - ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl); + return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl); } /* should never reach here */ @@ -594,10 +592,13 @@ public class ZygoteInit { } /** - * Prepare the arguments and fork for the system server process. + * Prepare the arguments and forks for the system server process. + * + * Returns an {@code Runnable} that provides an entrypoint into system_server code in the + * child process, and {@code null} in the parent. */ - private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) - throws Zygote.MethodAndArgsCaller, RuntimeException { + private static Runnable forkSystemServer(String abiList, String socketName, + ZygoteServer zygoteServer) { long capabilities = posixCapabilitiesAsBits( OsConstants.CAP_IPC_LOCK, OsConstants.CAP_KILL, @@ -662,10 +663,10 @@ public class ZygoteInit { } zygoteServer.closeServerSocket(); - handleSystemServerProcess(parsedArgs); + return handleSystemServerProcess(parsedArgs); } - return true; + return null; } /** @@ -696,6 +697,7 @@ public class ZygoteInit { throw new RuntimeException("Failed to setpgid(0,0)", ex); } + final Runnable caller; try { // Report Zygote start time to tron unless it is a runtime restart if (!"1".equals(SystemProperties.get("sys.boot_completed"))) { @@ -765,19 +767,32 @@ public class ZygoteInit { ZygoteHooks.stopZygoteNoThreadCreation(); if (startSystemServer) { - startSystemServer(abiList, socketName, zygoteServer); + Runnable r = forkSystemServer(abiList, socketName, zygoteServer); + + // {@code r == null} in the parent (zygote) process, and {@code r != null} in the + // child (system_server) process. + if (r != null) { + r.run(); + return; + } } Log.i(TAG, "Accepting command socket connections"); - zygoteServer.runSelectLoop(abiList); - zygoteServer.closeServerSocket(); - } catch (Zygote.MethodAndArgsCaller caller) { - caller.run(); + // The select loop returns early in the child process after a fork and + // loops forever in the zygote. + caller = zygoteServer.runSelectLoop(abiList); } catch (Throwable ex) { Log.e(TAG, "System zygote died with exception", ex); - zygoteServer.closeServerSocket(); 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(); } } @@ -821,8 +836,7 @@ public class ZygoteInit { * @param targetSdkVersion target SDK version * @param argv arg strings */ - public static final void zygoteInit(int targetSdkVersion, String[] argv, - ClassLoader classLoader) throws Zygote.MethodAndArgsCaller { + public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) { if (RuntimeInit.DEBUG) { Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote"); } @@ -832,7 +846,7 @@ public class ZygoteInit { RuntimeInit.commonInit(); ZygoteInit.nativeZygoteInit(); - RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); + return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); } private static final native void nativeZygoteInit(); diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 126d9e7db2ff..8baa15a058de 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -25,6 +25,7 @@ import android.system.ErrnoException; import android.system.StructPollfd; import android.util.Log; +import android.util.Slog; import java.io.IOException; import java.io.FileDescriptor; import java.util.ArrayList; @@ -45,9 +46,18 @@ class ZygoteServer { private LocalServerSocket mServerSocket; + /** + * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}. + */ + private boolean mIsForkChild; + ZygoteServer() { } + void setForkChild() { + mIsForkChild = true; + } + /** * Registers a server socket for zygote command connections * @@ -129,11 +139,8 @@ class ZygoteServer { * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. - * - * @throws Zygote.MethodAndArgsCaller in a child process when a main() - * should be executed. */ - void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller { + Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); @@ -156,15 +163,62 @@ class ZygoteServer { 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); + try { + ZygoteConnection connection = peers.get(i); + final Runnable command = connection.processOneCommand(this); + + if (mIsForkChild) { + // We're in the child. We should always have a command to run at this + // stage if processOneCommand hasn't called "exec". + if (command == null) { + throw new IllegalStateException("command == null"); + } + + return command; + } else { + // We're in the server - we should never have any commands to run. + if (command != null) { + throw new IllegalStateException("command != null"); + } + + // We don't know whether the remote side of the socket was closed or + // not until we attempt to read from it from processOneCommand. This shows up as + // a regular POLLIN event in our regular processing loop. + if (connection.isClosedByPeer()) { + connection.closeSocket(); + peers.remove(i); + fds.remove(i); + } + } + } catch (Exception e) { + if (!mIsForkChild) { + // We're in the server so any exception here is one that has taken place + // pre-fork while processing commands or reading / writing from the + // control socket. Make a loud noise about any such exceptions so that + // we know exactly what failed and why. + + Slog.e(TAG, "Exception executing zygote command: ", e); + + // Make sure the socket is closed so that the other end knows immediately + // that something has gone wrong and doesn't time out waiting for a + // response. + ZygoteConnection conn = peers.remove(i); + conn.closeSocket(); + + fds.remove(i); + } else { + // We're in the child so any exception caught here has happened post + // fork and before we execute ActivityThread.main (or any other main() + // method). Log the details of the exception and bring down the process. + Log.e(TAG, "Caught post-fork exception in child process.", e); + throw e; + } } } } |