Loading core/java/android/os/Process.java +2 −2 Original line number Diff line number Diff line Loading @@ -530,7 +530,7 @@ public class Process { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, packagesForUid, visibleVols, zygoteArgs); packagesForUid, visibleVols, /*useBlastulaPool=*/ true, zygoteArgs); } /** @hide */ Loading @@ -551,7 +551,7 @@ public class Process { return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, packagesForUid, visibleVols, zygoteArgs); packagesForUid, visibleVols, /*useBlastulaPool=*/ false, zygoteArgs); } /** Loading core/java/android/os/ZygoteProcess.java +165 −48 Original line number Diff line number Diff line Loading @@ -73,6 +73,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 @@ -87,6 +97,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 @@ -94,12 +113,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 @@ -111,6 +140,7 @@ public class ZygoteProcess { */ public static class ZygoteState { final LocalSocketAddress mZygoteSocketAddress; final LocalSocketAddress mBlastulaSocketAddress; private final LocalSocket mZygoteSessionSocket; Loading @@ -122,11 +152,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 @@ -134,14 +166,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 @@ -154,7 +189,7 @@ public class ZygoteProcess { zygoteOutputWriter = new BufferedWriter( new OutputStreamWriter(zygoteSessionSocket.getOutputStream()), 256); Zygote.SOCKET_BUFFER_SIZE); } catch (IOException ex) { try { zygoteSessionSocket.close(); Loading @@ -163,11 +198,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 @@ -269,12 +311,13 @@ public class ZygoteProcess { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, boolean useBlastulaPool, @Nullable String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false, packageName, packagesForUid, visibleVols, zygoteArgs); packageName, packagesForUid, visibleVols, useBlastulaPool, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); Loading Loading @@ -322,14 +365,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 @@ -344,37 +385,108 @@ 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", "--preload-app", "--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 @@ -416,6 +528,7 @@ public class ZygoteProcess { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, boolean useBlastulaPool, @Nullable String[] extraArgs) throws ZygoteStartFailedEx { ArrayList<String> argsForZygote = new ArrayList<String>(); Loading Loading @@ -522,7 +635,9 @@ public class ZygoteProcess { } synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, argsForZygote); } } Loading Loading @@ -686,7 +801,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 @@ -703,7 +818,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 @@ -820,7 +936,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 @@ -884,7 +1000,8 @@ public class ZygoteProcess { gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, abi, instructionSet, null /* appDataDir */, null /* invokeWith */, true /* startChildZygote */, null /* packageName */, null /* packagesForUid */, null /* visibleVolumes */, extraArgs); null /* packagesForUid */, null /* visibleVolumes */, 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 +19 −8 Original line number Diff line number Diff line Loading @@ -117,7 +117,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 @@ -187,6 +193,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 Loading @@ -234,14 +245,14 @@ public final class Zygote { 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, String packageName, String[] packagesForUid, String[] visibleVolIds) { String packageName, String[] packagesForUID, String[] visibleVolIDs) { VM_HOOKS.preFork(); // Resets nice priority for zygote process. resetNicePriority(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName, packagesForUid, visibleVolIds); packagesForUID, visibleVolIDs); // Enable tracing as soon as possible for the child process. if (pid == 0) { Trace.setTracingEnabled(true, runtimeFlags); Loading @@ -256,7 +267,7 @@ public final class Zygote { private static native int nativeForkAndSpecialize(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, String packageName, String[] packagesForUid, String[] visibleVolIds); String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs); /** * Specialize a Blastula instance. The current VM must have been started Loading @@ -281,12 +292,12 @@ public final class Zygote { */ public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds) { boolean startChildZygote, String instructionSet, String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs) { nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir, packageName, packagesForUid, visibleVolIds); packageName, packagesForUID, visibleVolIDs); // Enable tracing as soon as possible for the child process. Trace.setTracingEnabled(true, runtimeFlags); Loading @@ -306,7 +317,7 @@ public final class Zygote { private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds); String[] packagesForUID, String[] visibleVolIDs); /** * Called to do any initialization before starting an application. Loading core/java/com/android/internal/os/ZygoteConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -242,7 +242,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 +2 −2 Original line number Diff line number Diff line Loading @@ -530,7 +530,7 @@ public class Process { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, packagesForUid, visibleVols, zygoteArgs); packagesForUid, visibleVols, /*useBlastulaPool=*/ true, zygoteArgs); } /** @hide */ Loading @@ -551,7 +551,7 @@ public class Process { return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, packagesForUid, visibleVols, zygoteArgs); packagesForUid, visibleVols, /*useBlastulaPool=*/ false, zygoteArgs); } /** Loading
core/java/android/os/ZygoteProcess.java +165 −48 Original line number Diff line number Diff line Loading @@ -73,6 +73,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 @@ -87,6 +97,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 @@ -94,12 +113,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 @@ -111,6 +140,7 @@ public class ZygoteProcess { */ public static class ZygoteState { final LocalSocketAddress mZygoteSocketAddress; final LocalSocketAddress mBlastulaSocketAddress; private final LocalSocket mZygoteSessionSocket; Loading @@ -122,11 +152,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 @@ -134,14 +166,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 @@ -154,7 +189,7 @@ public class ZygoteProcess { zygoteOutputWriter = new BufferedWriter( new OutputStreamWriter(zygoteSessionSocket.getOutputStream()), 256); Zygote.SOCKET_BUFFER_SIZE); } catch (IOException ex) { try { zygoteSessionSocket.close(); Loading @@ -163,11 +198,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 @@ -269,12 +311,13 @@ public class ZygoteProcess { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, boolean useBlastulaPool, @Nullable String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false, packageName, packagesForUid, visibleVols, zygoteArgs); packageName, packagesForUid, visibleVols, useBlastulaPool, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); Loading Loading @@ -322,14 +365,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 @@ -344,37 +385,108 @@ 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", "--preload-app", "--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 @@ -416,6 +528,7 @@ public class ZygoteProcess { @Nullable String packageName, @Nullable String[] packagesForUid, @Nullable String[] visibleVols, boolean useBlastulaPool, @Nullable String[] extraArgs) throws ZygoteStartFailedEx { ArrayList<String> argsForZygote = new ArrayList<String>(); Loading Loading @@ -522,7 +635,9 @@ public class ZygoteProcess { } synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, argsForZygote); } } Loading Loading @@ -686,7 +801,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 @@ -703,7 +818,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 @@ -820,7 +936,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 @@ -884,7 +1000,8 @@ public class ZygoteProcess { gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, abi, instructionSet, null /* appDataDir */, null /* invokeWith */, true /* startChildZygote */, null /* packageName */, null /* packagesForUid */, null /* visibleVolumes */, extraArgs); null /* packagesForUid */, null /* visibleVolumes */, 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 +19 −8 Original line number Diff line number Diff line Loading @@ -117,7 +117,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 @@ -187,6 +193,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 Loading @@ -234,14 +245,14 @@ public final class Zygote { 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, String packageName, String[] packagesForUid, String[] visibleVolIds) { String packageName, String[] packagesForUID, String[] visibleVolIDs) { VM_HOOKS.preFork(); // Resets nice priority for zygote process. resetNicePriority(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName, packagesForUid, visibleVolIds); packagesForUID, visibleVolIDs); // Enable tracing as soon as possible for the child process. if (pid == 0) { Trace.setTracingEnabled(true, runtimeFlags); Loading @@ -256,7 +267,7 @@ public final class Zygote { private static native int nativeForkAndSpecialize(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, String packageName, String[] packagesForUid, String[] visibleVolIds); String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs); /** * Specialize a Blastula instance. The current VM must have been started Loading @@ -281,12 +292,12 @@ public final class Zygote { */ public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds) { boolean startChildZygote, String instructionSet, String appDataDir, String packageName, String[] packagesForUID, String[] visibleVolIDs) { nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir, packageName, packagesForUid, visibleVolIds); packageName, packagesForUID, visibleVolIDs); // Enable tracing as soon as possible for the child process. Trace.setTracingEnabled(true, runtimeFlags); Loading @@ -306,7 +317,7 @@ public final class Zygote { private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds); String[] packagesForUID, String[] visibleVolIDs); /** * Called to do any initialization before starting an application. Loading
core/java/com/android/internal/os/ZygoteConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -242,7 +242,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