Loading core/java/android/os/Process.java +4 −2 Original line number Diff line number Diff line Loading @@ -498,7 +498,8 @@ public class Process { String[] zygoteArgs) { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, zygoteArgs); abi, instructionSet, appDataDir, invokeWith, /*useBlastulaPool=*/ true, zygoteArgs); } /** @hide */ Loading @@ -515,7 +516,8 @@ public class Process { String[] zygoteArgs) { return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, zygoteArgs); abi, instructionSet, appDataDir, invokeWith, /*useBlastulaPool=*/ false, zygoteArgs); } /** Loading core/java/android/os/ZygoteProcess.java +164 −48 Original line number Diff line number Diff line Loading @@ -69,6 +69,16 @@ public class ZygoteProcess { */ public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary"; /** * @hide for internal use only */ public static final String BLASTULA_POOL_SOCKET_NAME = "blastula_pool"; /** * @hide for internal use only */ public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary"; /** * @hide for internal use only */ Loading @@ -83,6 +93,15 @@ public class ZygoteProcess { * The name of the secondary (alternate ABI) zygote socket. */ private final LocalSocketAddress mZygoteSecondarySocketAddress; /** * The name of the socket used to communicate with the primary blastula pool. */ private final LocalSocketAddress mBlastulaPoolSocketAddress; /** * The name of the socket used to communicate with the secondary (alternate ABI) blastula pool. */ private final LocalSocketAddress mBlastulaPoolSecondarySocketAddress; public ZygoteProcess() { mZygoteSocketAddress = Loading @@ -90,12 +109,22 @@ public class ZygoteProcess { mZygoteSecondarySocketAddress = new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSocketAddress = new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSecondarySocketAddress = new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); } public ZygoteProcess(LocalSocketAddress primarySocketAddress, LocalSocketAddress secondarySocketAddress) { mZygoteSocketAddress = primarySocketAddress; mZygoteSecondarySocketAddress = secondarySocketAddress; mBlastulaPoolSocketAddress = null; mBlastulaPoolSecondarySocketAddress = null; } public LocalSocketAddress getPrimarySocketAddress() { Loading @@ -107,6 +136,7 @@ public class ZygoteProcess { */ public static class ZygoteState { final LocalSocketAddress mZygoteSocketAddress; final LocalSocketAddress mBlastulaSocketAddress; private final LocalSocket mZygoteSessionSocket; Loading @@ -118,11 +148,13 @@ public class ZygoteProcess { private boolean mClosed; private ZygoteState(LocalSocketAddress zygoteSocketAddress, LocalSocketAddress blastulaSocketAddress, LocalSocket zygoteSessionSocket, DataInputStream zygoteInputStream, BufferedWriter zygoteOutputWriter, List<String> abiList) { this.mZygoteSocketAddress = zygoteSocketAddress; this.mBlastulaSocketAddress = blastulaSocketAddress; this.mZygoteSessionSocket = zygoteSessionSocket; this.mZygoteInputStream = zygoteInputStream; this.mZygoteOutputWriter = zygoteOutputWriter; Loading @@ -130,14 +162,17 @@ public class ZygoteProcess { } /** * Create a new ZygoteState object by connecting to the given Zygote socket. * Create a new ZygoteState object by connecting to the given Zygote socket and saving the * given blastula socket address. * * @param zygoteSocketAddress Zygote socket to connect to * @param blastulaSocketAddress Blastula socket address to save for later * @return A new ZygoteState object containing a session socket for the given Zygote socket * address * @throws IOException */ public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress) public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress, LocalSocketAddress blastulaSocketAddress) throws IOException { DataInputStream zygoteInputStream = null; Loading @@ -150,7 +185,7 @@ public class ZygoteProcess { zygoteOutputWriter = new BufferedWriter( new OutputStreamWriter(zygoteSessionSocket.getOutputStream()), 256); Zygote.SOCKET_BUFFER_SIZE); } catch (IOException ex) { try { zygoteSessionSocket.close(); Loading @@ -159,11 +194,18 @@ public class ZygoteProcess { throw ex; } return new ZygoteState(zygoteSocketAddress, return new ZygoteState(zygoteSocketAddress, blastulaSocketAddress, zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter, getAbiList(zygoteOutputWriter, zygoteInputStream)); } LocalSocket getBlastulaSessionSocket() throws IOException { final LocalSocket blastulaSessionSocket = new LocalSocket(); blastulaSessionSocket.connect(this.mBlastulaSocketAddress); return blastulaSessionSocket; } boolean matches(String abi) { return mABIList.contains(abi); } Loading Loading @@ -259,12 +301,14 @@ public class ZygoteProcess { String instructionSet, String appDataDir, String invokeWith, boolean useBlastulaPool, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false, zygoteArgs); /*startChildZygote=*/false, useBlastulaPool, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); Loading Loading @@ -312,14 +356,12 @@ public class ZygoteProcess { */ @GuardedBy("mLock") private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList<String> args) ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args) throws ZygoteStartFailedEx { try { // Throw early if any of the arguments are malformed. This means we can // avoid writing a partial response to the zygote. int sz = args.size(); for (int i = 0; i < sz; i++) { if (args.get(i).indexOf('\n') >= 0) { for (String arg : args) { if (arg.indexOf('\n') >= 0) { throw new ZygoteStartFailedEx("embedded newlines not allowed"); } } Loading @@ -334,37 +376,107 @@ public class ZygoteProcess { * the child or -1 on failure, followed by boolean to * indicate whether a wrapper process was used. */ final BufferedWriter writer = zygoteState.mZygoteOutputWriter; final DataInputStream inputStream = zygoteState.mZygoteInputStream; String msgStr = Integer.toString(args.size()) + "\n" + String.join("\n", args) + "\n"; writer.write(Integer.toString(args.size())); writer.newLine(); // Should there be a timeout on this? Process.ProcessStartResult result = new Process.ProcessStartResult(); for (int i = 0; i < sz; i++) { String arg = args.get(i); writer.write(arg); writer.newLine(); // TODO (chriswailes): Move branch body into separate function. if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) { LocalSocket blastulaSessionSocket = null; try { blastulaSessionSocket = zygoteState.getBlastulaSessionSocket(); final BufferedWriter blastulaWriter = new BufferedWriter( new OutputStreamWriter(blastulaSessionSocket.getOutputStream()), Zygote.SOCKET_BUFFER_SIZE); final DataInputStream blastulaReader = new DataInputStream(blastulaSessionSocket.getInputStream()); blastulaWriter.write(msgStr); blastulaWriter.flush(); result.pid = blastulaReader.readInt(); // Blastulas can't be used to spawn processes that need wrappers. result.usingWrapper = false; if (result.pid < 0) { throw new ZygoteStartFailedEx("Blastula specialization failed"); } writer.flush(); return result; } catch (IOException ex) { // If there was an IOException using the blastula pool we will log the error and // attempt to start the process through the Zygote. Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - " + ex.toString()); } finally { try { blastulaSessionSocket.close(); } catch (IOException ex) { Log.e(LOG_TAG, "Failed to close blastula session socket: " + ex.getMessage()); } } } // Should there be a timeout on this? Process.ProcessStartResult result = new Process.ProcessStartResult(); try { final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter; final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream; zygoteWriter.write(msgStr); zygoteWriter.flush(); // Always read the entire result from the input stream to avoid leaving // bytes in the stream for future process starts to accidentally stumble // upon. result.pid = inputStream.readInt(); result.usingWrapper = inputStream.readBoolean(); result.pid = zygoteInputStream.readInt(); result.usingWrapper = zygoteInputStream.readBoolean(); } catch (IOException ex) { zygoteState.close(); Log.e(LOG_TAG, "IO Exception while communicating with Zygote - " + ex.toString()); throw new ZygoteStartFailedEx(ex); } if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } return result; } catch (IOException ex) { zygoteState.close(); throw new ZygoteStartFailedEx(ex); } /** * Flags that may not be passed to a blastula. */ private static final String[] INVALID_BLASTULA_FLAGS = { "--query-abi-list", "--get-pid", "--preload-default", "--preload-package", "--start-child-zygote", "--set-api-blacklist-exemptions", "--hidden-api-log-sampling-rate", "--invoke-with" }; /** * Tests a command list to see if it is valid to send to a blastula. * @param args Zygote/Blastula command arguments * @return True if the command can be passed to a blastula; false otherwise */ private static boolean isValidBlastulaCommand(ArrayList<String> args) { for (String flag : args) { for (String badFlag : INVALID_BLASTULA_FLAGS) { if (flag.startsWith(badFlag)) { return false; } } } return true; } /** Loading Loading @@ -400,6 +512,7 @@ public class ZygoteProcess { String appDataDir, String invokeWith, boolean startChildZygote, boolean useBlastulaPool, String[] extraArgs) throws ZygoteStartFailedEx { ArrayList<String> argsForZygote = new ArrayList<String>(); Loading Loading @@ -469,7 +582,9 @@ public class ZygoteProcess { } synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, argsForZygote); } } Loading Loading @@ -633,7 +748,7 @@ public class ZygoteProcess { if (primaryZygoteState == null || primaryZygoteState.isClosed()) { try { primaryZygoteState = ZygoteState.connect(mZygoteSocketAddress); ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress); } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); } Loading @@ -650,7 +765,8 @@ public class ZygoteProcess { if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { try { secondaryZygoteState = ZygoteState.connect(mZygoteSecondarySocketAddress); ZygoteState.connect(mZygoteSecondarySocketAddress, mBlastulaPoolSecondarySocketAddress); } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); } Loading Loading @@ -737,7 +853,7 @@ public class ZygoteProcess { for (int n = 20; n >= 0; n--) { try { final ZygoteState zs = ZygoteState.connect(zygoteSocketAddress); ZygoteState.connect(zygoteSocketAddress, null); zs.close(); return; } catch (IOException ioe) { Loading Loading @@ -778,7 +894,7 @@ public class ZygoteProcess { result = startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, abi, instructionSet, null /* appDataDir */, null /* invokeWith */, true /* startChildZygote */, extraArgs); true /* startChildZygote */, false /* useBlastulaPool */, extraArgs); } catch (ZygoteStartFailedEx ex) { throw new RuntimeException("Starting child-zygote through Zygote failed", ex); } Loading core/java/com/android/internal/os/Zygote.java +12 −1 Original line number Diff line number Diff line Loading @@ -105,7 +105,13 @@ public final class Zygote { /** Number of bytes sent to the Zygote over blastula pipes or the pool event FD */ public static final int BLASTULA_MANAGEMENT_MESSAGE_BYTES = 8; /** If the blastula pool should be created and used to start applications */ /** * If the blastula pool should be created and used to start applications. * * Setting this value to false will disable the creation, maintenance, and use of the blastula * pool. When the blastula pool is disabled the application lifecycle will be identical to * previous versions of Android. */ public static final boolean BLASTULA_POOL_ENABLED = false; /** Loading Loading @@ -155,6 +161,11 @@ public final class Zygote { // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax. static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2); /** * @hide for internal use only */ public static final int SOCKET_BUFFER_SIZE = 256; private static LocalServerSocket sBlastulaPoolSocket = null; /** a prototype instance for a future List.toArray() */ Loading core/java/com/android/internal/os/ZygoteConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -224,7 +224,7 @@ class ZygoteConnection { fdsToClose[0] = fd.getInt$(); } fd = zygoteServer.getServerSocketFileDescriptor(); fd = zygoteServer.getZygoteSocketFileDescriptor(); if (fd != null) { fdsToClose[1] = fd.getInt$(); Loading core/java/com/android/internal/os/ZygoteInit.java +24 −7 Original line number Diff line number Diff line Loading @@ -269,7 +269,7 @@ public class ZygoteInit { try { BufferedReader br = new BufferedReader(new InputStreamReader(is), 256); new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE); int count = 0; String line; Loading Loading @@ -750,7 +750,7 @@ public class ZygoteInit { throw new RuntimeException("Failed to setpgid(0,0)", ex); } final Runnable caller; Runnable caller; try { // Report Zygote start time to tron unless it is a runtime restart if (!"1".equals(SystemProperties.get("sys.boot_completed"))) { Loading Loading @@ -786,7 +786,17 @@ public class ZygoteInit { throw new RuntimeException("No ABI list supplied."); } zygoteServer.registerServerSocketFromEnv(socketName); // TODO (chriswailes): Wrap these three calls in a helper function? final String blastulaSocketName = socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME) ? ZygoteProcess.BLASTULA_POOL_SOCKET_NAME : ZygoteProcess.BLASTULA_POOL_SECONDARY_SOCKET_NAME; zygoteServer.createZygoteSocket(socketName); Zygote.createBlastulaSocket(blastulaSocketName); Zygote.getSocketFDs(socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME)); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. if (!enableLazyPreload) { Loading Loading @@ -829,11 +839,18 @@ public class ZygoteInit { } } // If the return value is null then this is the zygote process // returning to the normal control flow. If it returns a Runnable // object then this is a blastula that has finished specializing. caller = Zygote.initBlastulaPool(); if (caller == null) { Log.i(TAG, "Accepting command socket connections"); // 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); throw ex; Loading Loading
core/java/android/os/Process.java +4 −2 Original line number Diff line number Diff line Loading @@ -498,7 +498,8 @@ public class Process { String[] zygoteArgs) { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, zygoteArgs); abi, instructionSet, appDataDir, invokeWith, /*useBlastulaPool=*/ true, zygoteArgs); } /** @hide */ Loading @@ -515,7 +516,8 @@ public class Process { String[] zygoteArgs) { return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, zygoteArgs); abi, instructionSet, appDataDir, invokeWith, /*useBlastulaPool=*/ false, zygoteArgs); } /** Loading
core/java/android/os/ZygoteProcess.java +164 −48 Original line number Diff line number Diff line Loading @@ -69,6 +69,16 @@ public class ZygoteProcess { */ public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary"; /** * @hide for internal use only */ public static final String BLASTULA_POOL_SOCKET_NAME = "blastula_pool"; /** * @hide for internal use only */ public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary"; /** * @hide for internal use only */ Loading @@ -83,6 +93,15 @@ public class ZygoteProcess { * The name of the secondary (alternate ABI) zygote socket. */ private final LocalSocketAddress mZygoteSecondarySocketAddress; /** * The name of the socket used to communicate with the primary blastula pool. */ private final LocalSocketAddress mBlastulaPoolSocketAddress; /** * The name of the socket used to communicate with the secondary (alternate ABI) blastula pool. */ private final LocalSocketAddress mBlastulaPoolSecondarySocketAddress; public ZygoteProcess() { mZygoteSocketAddress = Loading @@ -90,12 +109,22 @@ public class ZygoteProcess { mZygoteSecondarySocketAddress = new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSocketAddress = new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSecondarySocketAddress = new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); } public ZygoteProcess(LocalSocketAddress primarySocketAddress, LocalSocketAddress secondarySocketAddress) { mZygoteSocketAddress = primarySocketAddress; mZygoteSecondarySocketAddress = secondarySocketAddress; mBlastulaPoolSocketAddress = null; mBlastulaPoolSecondarySocketAddress = null; } public LocalSocketAddress getPrimarySocketAddress() { Loading @@ -107,6 +136,7 @@ public class ZygoteProcess { */ public static class ZygoteState { final LocalSocketAddress mZygoteSocketAddress; final LocalSocketAddress mBlastulaSocketAddress; private final LocalSocket mZygoteSessionSocket; Loading @@ -118,11 +148,13 @@ public class ZygoteProcess { private boolean mClosed; private ZygoteState(LocalSocketAddress zygoteSocketAddress, LocalSocketAddress blastulaSocketAddress, LocalSocket zygoteSessionSocket, DataInputStream zygoteInputStream, BufferedWriter zygoteOutputWriter, List<String> abiList) { this.mZygoteSocketAddress = zygoteSocketAddress; this.mBlastulaSocketAddress = blastulaSocketAddress; this.mZygoteSessionSocket = zygoteSessionSocket; this.mZygoteInputStream = zygoteInputStream; this.mZygoteOutputWriter = zygoteOutputWriter; Loading @@ -130,14 +162,17 @@ public class ZygoteProcess { } /** * Create a new ZygoteState object by connecting to the given Zygote socket. * Create a new ZygoteState object by connecting to the given Zygote socket and saving the * given blastula socket address. * * @param zygoteSocketAddress Zygote socket to connect to * @param blastulaSocketAddress Blastula socket address to save for later * @return A new ZygoteState object containing a session socket for the given Zygote socket * address * @throws IOException */ public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress) public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress, LocalSocketAddress blastulaSocketAddress) throws IOException { DataInputStream zygoteInputStream = null; Loading @@ -150,7 +185,7 @@ public class ZygoteProcess { zygoteOutputWriter = new BufferedWriter( new OutputStreamWriter(zygoteSessionSocket.getOutputStream()), 256); Zygote.SOCKET_BUFFER_SIZE); } catch (IOException ex) { try { zygoteSessionSocket.close(); Loading @@ -159,11 +194,18 @@ public class ZygoteProcess { throw ex; } return new ZygoteState(zygoteSocketAddress, return new ZygoteState(zygoteSocketAddress, blastulaSocketAddress, zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter, getAbiList(zygoteOutputWriter, zygoteInputStream)); } LocalSocket getBlastulaSessionSocket() throws IOException { final LocalSocket blastulaSessionSocket = new LocalSocket(); blastulaSessionSocket.connect(this.mBlastulaSocketAddress); return blastulaSessionSocket; } boolean matches(String abi) { return mABIList.contains(abi); } Loading Loading @@ -259,12 +301,14 @@ public class ZygoteProcess { String instructionSet, String appDataDir, String invokeWith, boolean useBlastulaPool, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false, zygoteArgs); /*startChildZygote=*/false, useBlastulaPool, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); Loading Loading @@ -312,14 +356,12 @@ public class ZygoteProcess { */ @GuardedBy("mLock") private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList<String> args) ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args) throws ZygoteStartFailedEx { try { // Throw early if any of the arguments are malformed. This means we can // avoid writing a partial response to the zygote. int sz = args.size(); for (int i = 0; i < sz; i++) { if (args.get(i).indexOf('\n') >= 0) { for (String arg : args) { if (arg.indexOf('\n') >= 0) { throw new ZygoteStartFailedEx("embedded newlines not allowed"); } } Loading @@ -334,37 +376,107 @@ public class ZygoteProcess { * the child or -1 on failure, followed by boolean to * indicate whether a wrapper process was used. */ final BufferedWriter writer = zygoteState.mZygoteOutputWriter; final DataInputStream inputStream = zygoteState.mZygoteInputStream; String msgStr = Integer.toString(args.size()) + "\n" + String.join("\n", args) + "\n"; writer.write(Integer.toString(args.size())); writer.newLine(); // Should there be a timeout on this? Process.ProcessStartResult result = new Process.ProcessStartResult(); for (int i = 0; i < sz; i++) { String arg = args.get(i); writer.write(arg); writer.newLine(); // TODO (chriswailes): Move branch body into separate function. if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) { LocalSocket blastulaSessionSocket = null; try { blastulaSessionSocket = zygoteState.getBlastulaSessionSocket(); final BufferedWriter blastulaWriter = new BufferedWriter( new OutputStreamWriter(blastulaSessionSocket.getOutputStream()), Zygote.SOCKET_BUFFER_SIZE); final DataInputStream blastulaReader = new DataInputStream(blastulaSessionSocket.getInputStream()); blastulaWriter.write(msgStr); blastulaWriter.flush(); result.pid = blastulaReader.readInt(); // Blastulas can't be used to spawn processes that need wrappers. result.usingWrapper = false; if (result.pid < 0) { throw new ZygoteStartFailedEx("Blastula specialization failed"); } writer.flush(); return result; } catch (IOException ex) { // If there was an IOException using the blastula pool we will log the error and // attempt to start the process through the Zygote. Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - " + ex.toString()); } finally { try { blastulaSessionSocket.close(); } catch (IOException ex) { Log.e(LOG_TAG, "Failed to close blastula session socket: " + ex.getMessage()); } } } // Should there be a timeout on this? Process.ProcessStartResult result = new Process.ProcessStartResult(); try { final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter; final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream; zygoteWriter.write(msgStr); zygoteWriter.flush(); // Always read the entire result from the input stream to avoid leaving // bytes in the stream for future process starts to accidentally stumble // upon. result.pid = inputStream.readInt(); result.usingWrapper = inputStream.readBoolean(); result.pid = zygoteInputStream.readInt(); result.usingWrapper = zygoteInputStream.readBoolean(); } catch (IOException ex) { zygoteState.close(); Log.e(LOG_TAG, "IO Exception while communicating with Zygote - " + ex.toString()); throw new ZygoteStartFailedEx(ex); } if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } return result; } catch (IOException ex) { zygoteState.close(); throw new ZygoteStartFailedEx(ex); } /** * Flags that may not be passed to a blastula. */ private static final String[] INVALID_BLASTULA_FLAGS = { "--query-abi-list", "--get-pid", "--preload-default", "--preload-package", "--start-child-zygote", "--set-api-blacklist-exemptions", "--hidden-api-log-sampling-rate", "--invoke-with" }; /** * Tests a command list to see if it is valid to send to a blastula. * @param args Zygote/Blastula command arguments * @return True if the command can be passed to a blastula; false otherwise */ private static boolean isValidBlastulaCommand(ArrayList<String> args) { for (String flag : args) { for (String badFlag : INVALID_BLASTULA_FLAGS) { if (flag.startsWith(badFlag)) { return false; } } } return true; } /** Loading Loading @@ -400,6 +512,7 @@ public class ZygoteProcess { String appDataDir, String invokeWith, boolean startChildZygote, boolean useBlastulaPool, String[] extraArgs) throws ZygoteStartFailedEx { ArrayList<String> argsForZygote = new ArrayList<String>(); Loading Loading @@ -469,7 +582,9 @@ public class ZygoteProcess { } synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, argsForZygote); } } Loading Loading @@ -633,7 +748,7 @@ public class ZygoteProcess { if (primaryZygoteState == null || primaryZygoteState.isClosed()) { try { primaryZygoteState = ZygoteState.connect(mZygoteSocketAddress); ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress); } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); } Loading @@ -650,7 +765,8 @@ public class ZygoteProcess { if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { try { secondaryZygoteState = ZygoteState.connect(mZygoteSecondarySocketAddress); ZygoteState.connect(mZygoteSecondarySocketAddress, mBlastulaPoolSecondarySocketAddress); } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); } Loading Loading @@ -737,7 +853,7 @@ public class ZygoteProcess { for (int n = 20; n >= 0; n--) { try { final ZygoteState zs = ZygoteState.connect(zygoteSocketAddress); ZygoteState.connect(zygoteSocketAddress, null); zs.close(); return; } catch (IOException ioe) { Loading Loading @@ -778,7 +894,7 @@ public class ZygoteProcess { result = startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, abi, instructionSet, null /* appDataDir */, null /* invokeWith */, true /* startChildZygote */, extraArgs); true /* startChildZygote */, false /* useBlastulaPool */, extraArgs); } catch (ZygoteStartFailedEx ex) { throw new RuntimeException("Starting child-zygote through Zygote failed", ex); } Loading
core/java/com/android/internal/os/Zygote.java +12 −1 Original line number Diff line number Diff line Loading @@ -105,7 +105,13 @@ public final class Zygote { /** Number of bytes sent to the Zygote over blastula pipes or the pool event FD */ public static final int BLASTULA_MANAGEMENT_MESSAGE_BYTES = 8; /** If the blastula pool should be created and used to start applications */ /** * If the blastula pool should be created and used to start applications. * * Setting this value to false will disable the creation, maintenance, and use of the blastula * pool. When the blastula pool is disabled the application lifecycle will be identical to * previous versions of Android. */ public static final boolean BLASTULA_POOL_ENABLED = false; /** Loading Loading @@ -155,6 +161,11 @@ public final class Zygote { // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax. static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2); /** * @hide for internal use only */ public static final int SOCKET_BUFFER_SIZE = 256; private static LocalServerSocket sBlastulaPoolSocket = null; /** a prototype instance for a future List.toArray() */ Loading
core/java/com/android/internal/os/ZygoteConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -224,7 +224,7 @@ class ZygoteConnection { fdsToClose[0] = fd.getInt$(); } fd = zygoteServer.getServerSocketFileDescriptor(); fd = zygoteServer.getZygoteSocketFileDescriptor(); if (fd != null) { fdsToClose[1] = fd.getInt$(); Loading
core/java/com/android/internal/os/ZygoteInit.java +24 −7 Original line number Diff line number Diff line Loading @@ -269,7 +269,7 @@ public class ZygoteInit { try { BufferedReader br = new BufferedReader(new InputStreamReader(is), 256); new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE); int count = 0; String line; Loading Loading @@ -750,7 +750,7 @@ public class ZygoteInit { throw new RuntimeException("Failed to setpgid(0,0)", ex); } final Runnable caller; Runnable caller; try { // Report Zygote start time to tron unless it is a runtime restart if (!"1".equals(SystemProperties.get("sys.boot_completed"))) { Loading Loading @@ -786,7 +786,17 @@ public class ZygoteInit { throw new RuntimeException("No ABI list supplied."); } zygoteServer.registerServerSocketFromEnv(socketName); // TODO (chriswailes): Wrap these three calls in a helper function? final String blastulaSocketName = socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME) ? ZygoteProcess.BLASTULA_POOL_SOCKET_NAME : ZygoteProcess.BLASTULA_POOL_SECONDARY_SOCKET_NAME; zygoteServer.createZygoteSocket(socketName); Zygote.createBlastulaSocket(blastulaSocketName); Zygote.getSocketFDs(socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME)); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. if (!enableLazyPreload) { Loading Loading @@ -829,11 +839,18 @@ public class ZygoteInit { } } // If the return value is null then this is the zygote process // returning to the normal control flow. If it returns a Runnable // object then this is a blastula that has finished specializing. caller = Zygote.initBlastulaPool(); if (caller == null) { Log.i(TAG, "Accepting command socket connections"); // 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); throw ex; Loading