Loading core/java/android/os/ZygoteProcess.java +66 −26 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.provider.DeviceConfig; import android.util.Log; import android.util.Slog; Loading Loading @@ -63,16 +64,6 @@ import java.util.UUID; */ public class ZygoteProcess { /** * @hide for internal use only. */ public static final String ZYGOTE_SOCKET_NAME = "zygote"; /** * @hide for internal use only. */ public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary"; /** * @hide for internal use only. */ Loading @@ -89,17 +80,12 @@ public class ZygoteProcess { /** * @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"; private static final String LOG_TAG = "ZygoteProcess"; /** * @hide for internal use only * The default value for enabling the blastula pool. */ private static final String LOG_TAG = "ZygoteProcess"; private static final String BLASTULA_POOL_ENABLED_DEFAULT = "false"; /** * The name of the socket used to communicate with the primary zygote. Loading @@ -110,6 +96,7 @@ 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. */ Loading @@ -122,17 +109,21 @@ public class ZygoteProcess { public ZygoteProcess() { mZygoteSocketAddress = new LocalSocketAddress(ZYGOTE_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mZygoteSecondarySocketAddress = new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME, new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSocketAddress = new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME, new LocalSocketAddress(Zygote.BLASTULA_POOL_PRIMARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSecondarySocketAddress = new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME, new LocalSocketAddress(Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); // TODO (chriswailes): Uncomment when the blastula pool can be enabled. // fetchBlastulaPoolEnabledProp(); } public ZygoteProcess(LocalSocketAddress primarySocketAddress, Loading Loading @@ -271,6 +262,15 @@ public class ZygoteProcess { */ private ZygoteState secondaryZygoteState; /** * 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. */ private boolean mBlastulaPoolEnabled = false; /** * Start a new process. * Loading Loading @@ -327,6 +327,14 @@ public class ZygoteProcess { @Nullable String sandboxId, boolean useBlastulaPool, @Nullable String[] zygoteArgs) { if (fetchBlastulaPoolEnabledProp()) { // TODO (chriswailes): Send the appropriate command to the zygotes Log.i(LOG_TAG, "Blastula pool enabled property set to: " + mBlastulaPoolEnabled); // This can't be enabled yet, but we do want to test this code path. mBlastulaPoolEnabled = false; } try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, Loading Loading @@ -407,7 +415,7 @@ public class ZygoteProcess { Process.ProcessStartResult result = new Process.ProcessStartResult(); // TODO (chriswailes): Move branch body into separate function. if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) { if (useBlastulaPool && isValidBlastulaCommand(args)) { LocalSocket blastulaSessionSocket = null; try { Loading Loading @@ -620,6 +628,7 @@ public class ZygoteProcess { final StringBuilder sb = new StringBuilder(); sb.append("--packages-for-uid="); // TODO (chriswailes): Replace with String.join for (int i = 0; i < packagesForUid.length; ++i) { if (i != 0) { sb.append(','); Loading Loading @@ -656,11 +665,42 @@ public class ZygoteProcess { synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, useBlastulaPool && mBlastulaPoolEnabled, argsForZygote); } } private boolean fetchBlastulaPoolEnabledProp() { boolean origVal = mBlastulaPoolEnabled; final String propertyString = Zygote.getSystemProperty( DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED, BLASTULA_POOL_ENABLED_DEFAULT); if (!propertyString.isEmpty()) { mBlastulaPoolEnabled = Zygote.getSystemPropertyBoolean( DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED, Boolean.parseBoolean(BLASTULA_POOL_ENABLED_DEFAULT)); } return origVal != mBlastulaPoolEnabled; } private long mLastPropCheckTimestamp = 0; private boolean fetchBlastulaPoolEnabledPropWithMinInterval() { final long currentTimestamp = SystemClock.elapsedRealtime(); if (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL) { mLastPropCheckTimestamp = currentTimestamp; return fetchBlastulaPoolEnabledProp(); } return false; } /** * Closes the connections to the zygote, if they exist. */ Loading Loading @@ -940,7 +980,7 @@ public class ZygoteProcess { /** * Try connecting to the Zygote over and over again until we hit a time-out. * @param socketName The name of the socket to connect to. * @param zygoteSocketName The name of the socket to connect to. */ public static void waitForConnectionToZygote(String zygoteSocketName) { final LocalSocketAddress zygoteSocketAddress = Loading @@ -950,7 +990,7 @@ public class ZygoteProcess { /** * Try connecting to the Zygote over and over again until we hit a time-out. * @param address The name of the socket to connect to. * @param zygoteSocketAddress The name of the socket to connect to. */ public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) { int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS; Loading core/java/android/provider/DeviceConfig.java +32 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,38 @@ public final class DeviceConfig { @SystemApi public interface RuntimeNative { String NAMESPACE = "runtime_native"; /** * Zygote flags. See {@link com.internal.os.Zygote}. */ /** * If {@code true}, enables the blastula pool feature. * * @hide for internal use only */ String BLASTULA_POOL_ENABLED = "blastula_pool_enabled"; /** * The maximum number of processes to keep in the blastula pool. * * @hide for internal use only */ String BLASTULA_POOL_SIZE_MAX = "blastula_pool_size_max"; /** * The minimum number of processes to keep in the blastula pool. * * @hide for internal use only */ String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_max"; /** * The threshold used to determine if the pool should be refilled. * * @hide for internal use only */ String BLASTULA_POOL_REFILL_THRESHOLD = "blastula_refill_threshold"; } /** Loading core/java/com/android/internal/os/Zygote.java +57 −110 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.os.IVold; import android.os.Process; import android.os.SystemProperties; import android.os.Trace; import android.provider.DeviceConfig; import android.system.ErrnoException; import android.system.Os; import android.util.Log; Loading Loading @@ -128,21 +129,6 @@ 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. * * 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; /** * File descriptor used for communication between the signal handler and the ZygoteServer poll * loop. * */ protected static FileDescriptor sBlastulaPoolEventFD; /** * An extraArg passed when a zygote process is forking a child-zygote, specifying a name * in the abstract socket namespace. This socket name is what the new child zygote Loading Loading @@ -174,43 +160,39 @@ public final class Zygote { private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; /** * The maximum value that the sBlastulaPoolMax variable may take. This value * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp. * The duration to wait before re-checking Zygote related system properties. * * Five minutes in milliseconds. */ static final int BLASTULA_POOL_MAX_LIMIT = 10; public static final long PROPERTY_CHECK_INTERVAL = 300000; /** * The minimum value that the sBlastulaPoolMin variable may take. * @hide for internal use only */ static final int BLASTULA_POOL_MIN_LIMIT = 1; public static final int SOCKET_BUFFER_SIZE = 256; /** a prototype instance for a future List.toArray() */ protected static final int[][] INT_ARRAY_2D = new int[0][0]; /** * The runtime-adjustable maximum Blastula pool size. * @hide for internal use only. */ static int sBlastulaPoolMax = BLASTULA_POOL_MAX_LIMIT; public static final String PRIMARY_SOCKET_NAME = "zygote"; /** * The runtime-adjustable minimum Blastula pool size. * @hide for internal use only. */ static int sBlastulaPoolMin = BLASTULA_POOL_MIN_LIMIT; public static final String SECONDARY_SOCKET_NAME = "zygote_secondary"; /** * The runtime-adjustable value used to determine when to re-fill the * blastula pool. The pool will be re-filled when * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold. * @hide for internal use only */ // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax. static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2); public static final String BLASTULA_POOL_PRIMARY_SOCKET_NAME = "blastula_pool"; /** * @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() */ protected static final int[][] INT_ARRAY_2D = new int[0][0]; public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary"; private Zygote() {} Loading Loading @@ -428,70 +410,47 @@ public final class Zygote { protected static native void nativeGetSocketFDs(boolean isPrimary); /** * Initialize the blastula pool and fill it with the desired number of * processes. * Returns the raw string value of a system property. * * Note that Device Config is not available without an application so SystemProperties is used * instead. * * TODO (chriswailes): Cache the system property location in native code and then write a JNI * function to fetch it. */ protected static Runnable initBlastulaPool() { if (BLASTULA_POOL_ENABLED) { sBlastulaPoolEventFD = getBlastulaPoolEventFD(); return fillBlastulaPool(null); } else { return null; } public static String getSystemProperty(String propertyName, String defaultValue) { return SystemProperties.get( String.join(".", "persist.device_config", DeviceConfig.RuntimeNative.NAMESPACE, propertyName), defaultValue); } /** * Checks to see if the current policy says that pool should be refilled, and spawns new * blastulas if necessary. * Returns the value of a system property converted to a boolean using specific logic. * * NOTE: This function doesn't need to be guarded with BLASTULA_POOL_ENABLED because it is * only called from contexts that are only valid if the pool is enabled. * Note that Device Config is not available without an application so SystemProperties is used * instead. * * @param sessionSocketRawFDs Anonymous session sockets that are currently open * @return In the Zygote process this function will always return null; in blastula processes * this function will return a Runnable object representing the new application that is * passed up from blastulaMain. * @see SystemProperties.getBoolean * * TODO (chriswailes): Cache the system property location in native code and then write a JNI * function to fetch it. */ protected static Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); int blastulaPoolCount = getBlastulaPoolCount(); int numBlastulasToSpawn = sBlastulaPoolMax - blastulaPoolCount; if (blastulaPoolCount < sBlastulaPoolMin || numBlastulasToSpawn >= sBlastulaPoolRefillThreshold) { // Disable some VM functionality and reset some system values // before forking. ZygoteHooks.preFork(); resetNicePriority(); while (blastulaPoolCount++ < sBlastulaPoolMax) { Runnable caller = forkBlastula(sessionSocketRawFDs); if (caller != null) { return caller; } } // Re-enable runtime services for the Zygote. Blastula services // are re-enabled in specializeBlastula. ZygoteHooks.postForkCommon(); Log.i("zygote", "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return null; public static boolean getSystemPropertyBoolean(String propertyName, Boolean defaultValue) { return SystemProperties.getBoolean( String.join(".", "persist.device_config", DeviceConfig.RuntimeNative.NAMESPACE, propertyName), defaultValue); } /** * @return Number of blastulas currently in the pool */ private static int getBlastulaPoolCount() { static int getBlastulaPoolCount() { return nativeGetBlastulaPoolCount(); } Loading @@ -501,7 +460,7 @@ public final class Zygote { * @return The event FD used for communication between the signal handler and the ZygoteServer * poll loop */ private static FileDescriptor getBlastulaPoolEventFD() { static FileDescriptor getBlastulaPoolEventFD() { FileDescriptor fd = new FileDescriptor(); fd.setInt$(nativeGetBlastulaPoolEventFD()); Loading @@ -518,7 +477,8 @@ public final class Zygote { * this function will return a Runnable object representing the new application that is * passed up from blastulaMain. */ private static Runnable forkBlastula(int[] sessionSocketRawFDs) { static Runnable forkBlastula(LocalServerSocket blastulaPoolSocket, int[] sessionSocketRawFDs) { FileDescriptor[] pipeFDs = null; try { Loading @@ -532,7 +492,7 @@ public final class Zygote { if (pid == 0) { IoUtils.closeQuietly(pipeFDs[0]); return blastulaMain(pipeFDs[1]); return blastulaMain(blastulaPoolSocket, pipeFDs[1]); } else { // The read-end of the pipe will be closed by the native code. // See removeBlastulaTableEntry(); Loading @@ -553,7 +513,8 @@ public final class Zygote { * of the ZygoteServer. * @return A runnable oject representing the new application. */ static Runnable blastulaMain(FileDescriptor writePipe) { private static Runnable blastulaMain(LocalServerSocket blastulaPoolSocket, FileDescriptor writePipe) { final int pid = Process.myPid(); LocalSocket sessionSocket = null; Loading @@ -563,7 +524,7 @@ public final class Zygote { while (true) { try { sessionSocket = sBlastulaPoolSocket.accept(); sessionSocket = blastulaPoolSocket.accept(); BufferedReader blastulaReader = new BufferedReader(new InputStreamReader(sessionSocket.getInputStream())); Loading Loading @@ -611,7 +572,7 @@ public final class Zygote { System.exit(-1); } finally { IoUtils.closeQuietly(sessionSocket); IoUtils.closeQuietly(sBlastulaPoolSocket); IoUtils.closeQuietly(blastulaPoolSocket); } try { Loading Loading @@ -660,7 +621,7 @@ public final class Zygote { * exception if an invalid arugment is encountered. * @param args The arguments to test */ static void validateBlastulaCommand(ZygoteArguments args) { private static void validateBlastulaCommand(ZygoteArguments args) { if (args.mAbiListQuery) { throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--query-abi-list"); } else if (args.mPidQuery) { Loading Loading @@ -850,20 +811,6 @@ public final class Zygote { return args; } /** * Creates a managed object representing the Blastula pool socket that has * already been initialized and bound by init. * * TODO (chriswailes): Move the name selection logic into this function. * * @throws RuntimeException when open fails */ static void createBlastulaSocket(String socketName) { if (BLASTULA_POOL_ENABLED && sBlastulaPoolSocket == null) { sBlastulaPoolSocket = createManagedSocketFromInitSocket(socketName); } } /** * Creates a managed LocalServerSocket object using a file descriptor * created by an init.rc script. The init scripts that specify the Loading core/java/com/android/internal/os/ZygoteInit.java +18 −28 Original line number Diff line number Diff line Loading @@ -755,7 +755,7 @@ public class ZygoteInit { } public static void main(String argv[]) { ZygoteServer zygoteServer = new ZygoteServer(); ZygoteServer zygoteServer = null; // Mark zygote start. This ensures that thread creation will throw // an error. Loading Loading @@ -783,7 +783,7 @@ public class ZygoteInit { RuntimeInit.enableDdms(); boolean startSystemServer = false; String socketName = "zygote"; String zygoteSocketName = "zygote"; String abiList = null; boolean enableLazyPreload = false; for (int i = 1; i < argv.length; i++) { Loading @@ -794,26 +794,19 @@ public class ZygoteInit { } else if (argv[i].startsWith(ABI_LIST_ARG)) { abiList = argv[i].substring(ABI_LIST_ARG.length()); } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { socketName = argv[i].substring(SOCKET_NAME_ARG.length()); zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); } } final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME); if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } // 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)); Zygote.getSocketFDs(isPrimaryZygote); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. Loading Loading @@ -846,8 +839,10 @@ public class ZygoteInit { ZygoteHooks.stopZygoteNoThreadCreation(); zygoteServer = new ZygoteServer(isPrimaryZygote); if (startSystemServer) { Runnable r = forkSystemServer(abiList, socketName, zygoteServer); Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r != null} in the // child (system_server) process. Loading @@ -857,24 +852,19 @@ 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; } finally { if (zygoteServer != null) { zygoteServer.closeServerSocket(); } } // We're in the child process and have exited the select loop. Proceed to execute the // command. Loading @@ -894,8 +884,8 @@ public class ZygoteInit { } private static void waitForSecondaryZygote(String socketName) { String otherZygoteName = ZygoteProcess.ZYGOTE_SOCKET_NAME.equals(socketName) ? ZygoteProcess.ZYGOTE_SECONDARY_SOCKET_NAME : ZygoteProcess.ZYGOTE_SOCKET_NAME; String otherZygoteName = Zygote.PRIMARY_SOCKET_NAME.equals(socketName) ? Zygote.SECONDARY_SOCKET_NAME : Zygote.PRIMARY_SOCKET_NAME; ZygoteProcess.waitForConnectionToZygote(otherZygoteName); } Loading core/java/com/android/internal/os/ZygoteServer.java +189 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/os/ZygoteProcess.java +66 −26 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.provider.DeviceConfig; import android.util.Log; import android.util.Slog; Loading Loading @@ -63,16 +64,6 @@ import java.util.UUID; */ public class ZygoteProcess { /** * @hide for internal use only. */ public static final String ZYGOTE_SOCKET_NAME = "zygote"; /** * @hide for internal use only. */ public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary"; /** * @hide for internal use only. */ Loading @@ -89,17 +80,12 @@ public class ZygoteProcess { /** * @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"; private static final String LOG_TAG = "ZygoteProcess"; /** * @hide for internal use only * The default value for enabling the blastula pool. */ private static final String LOG_TAG = "ZygoteProcess"; private static final String BLASTULA_POOL_ENABLED_DEFAULT = "false"; /** * The name of the socket used to communicate with the primary zygote. Loading @@ -110,6 +96,7 @@ 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. */ Loading @@ -122,17 +109,21 @@ public class ZygoteProcess { public ZygoteProcess() { mZygoteSocketAddress = new LocalSocketAddress(ZYGOTE_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mZygoteSecondarySocketAddress = new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME, new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSocketAddress = new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME, new LocalSocketAddress(Zygote.BLASTULA_POOL_PRIMARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSecondarySocketAddress = new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME, new LocalSocketAddress(Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); // TODO (chriswailes): Uncomment when the blastula pool can be enabled. // fetchBlastulaPoolEnabledProp(); } public ZygoteProcess(LocalSocketAddress primarySocketAddress, Loading Loading @@ -271,6 +262,15 @@ public class ZygoteProcess { */ private ZygoteState secondaryZygoteState; /** * 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. */ private boolean mBlastulaPoolEnabled = false; /** * Start a new process. * Loading Loading @@ -327,6 +327,14 @@ public class ZygoteProcess { @Nullable String sandboxId, boolean useBlastulaPool, @Nullable String[] zygoteArgs) { if (fetchBlastulaPoolEnabledProp()) { // TODO (chriswailes): Send the appropriate command to the zygotes Log.i(LOG_TAG, "Blastula pool enabled property set to: " + mBlastulaPoolEnabled); // This can't be enabled yet, but we do want to test this code path. mBlastulaPoolEnabled = false; } try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, Loading Loading @@ -407,7 +415,7 @@ public class ZygoteProcess { Process.ProcessStartResult result = new Process.ProcessStartResult(); // TODO (chriswailes): Move branch body into separate function. if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) { if (useBlastulaPool && isValidBlastulaCommand(args)) { LocalSocket blastulaSessionSocket = null; try { Loading Loading @@ -620,6 +628,7 @@ public class ZygoteProcess { final StringBuilder sb = new StringBuilder(); sb.append("--packages-for-uid="); // TODO (chriswailes): Replace with String.join for (int i = 0; i < packagesForUid.length; ++i) { if (i != 0) { sb.append(','); Loading Loading @@ -656,11 +665,42 @@ public class ZygoteProcess { synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, useBlastulaPool && mBlastulaPoolEnabled, argsForZygote); } } private boolean fetchBlastulaPoolEnabledProp() { boolean origVal = mBlastulaPoolEnabled; final String propertyString = Zygote.getSystemProperty( DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED, BLASTULA_POOL_ENABLED_DEFAULT); if (!propertyString.isEmpty()) { mBlastulaPoolEnabled = Zygote.getSystemPropertyBoolean( DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED, Boolean.parseBoolean(BLASTULA_POOL_ENABLED_DEFAULT)); } return origVal != mBlastulaPoolEnabled; } private long mLastPropCheckTimestamp = 0; private boolean fetchBlastulaPoolEnabledPropWithMinInterval() { final long currentTimestamp = SystemClock.elapsedRealtime(); if (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL) { mLastPropCheckTimestamp = currentTimestamp; return fetchBlastulaPoolEnabledProp(); } return false; } /** * Closes the connections to the zygote, if they exist. */ Loading Loading @@ -940,7 +980,7 @@ public class ZygoteProcess { /** * Try connecting to the Zygote over and over again until we hit a time-out. * @param socketName The name of the socket to connect to. * @param zygoteSocketName The name of the socket to connect to. */ public static void waitForConnectionToZygote(String zygoteSocketName) { final LocalSocketAddress zygoteSocketAddress = Loading @@ -950,7 +990,7 @@ public class ZygoteProcess { /** * Try connecting to the Zygote over and over again until we hit a time-out. * @param address The name of the socket to connect to. * @param zygoteSocketAddress The name of the socket to connect to. */ public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) { int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS; Loading
core/java/android/provider/DeviceConfig.java +32 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,38 @@ public final class DeviceConfig { @SystemApi public interface RuntimeNative { String NAMESPACE = "runtime_native"; /** * Zygote flags. See {@link com.internal.os.Zygote}. */ /** * If {@code true}, enables the blastula pool feature. * * @hide for internal use only */ String BLASTULA_POOL_ENABLED = "blastula_pool_enabled"; /** * The maximum number of processes to keep in the blastula pool. * * @hide for internal use only */ String BLASTULA_POOL_SIZE_MAX = "blastula_pool_size_max"; /** * The minimum number of processes to keep in the blastula pool. * * @hide for internal use only */ String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_max"; /** * The threshold used to determine if the pool should be refilled. * * @hide for internal use only */ String BLASTULA_POOL_REFILL_THRESHOLD = "blastula_refill_threshold"; } /** Loading
core/java/com/android/internal/os/Zygote.java +57 −110 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.os.IVold; import android.os.Process; import android.os.SystemProperties; import android.os.Trace; import android.provider.DeviceConfig; import android.system.ErrnoException; import android.system.Os; import android.util.Log; Loading Loading @@ -128,21 +129,6 @@ 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. * * 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; /** * File descriptor used for communication between the signal handler and the ZygoteServer poll * loop. * */ protected static FileDescriptor sBlastulaPoolEventFD; /** * An extraArg passed when a zygote process is forking a child-zygote, specifying a name * in the abstract socket namespace. This socket name is what the new child zygote Loading Loading @@ -174,43 +160,39 @@ public final class Zygote { private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; /** * The maximum value that the sBlastulaPoolMax variable may take. This value * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp. * The duration to wait before re-checking Zygote related system properties. * * Five minutes in milliseconds. */ static final int BLASTULA_POOL_MAX_LIMIT = 10; public static final long PROPERTY_CHECK_INTERVAL = 300000; /** * The minimum value that the sBlastulaPoolMin variable may take. * @hide for internal use only */ static final int BLASTULA_POOL_MIN_LIMIT = 1; public static final int SOCKET_BUFFER_SIZE = 256; /** a prototype instance for a future List.toArray() */ protected static final int[][] INT_ARRAY_2D = new int[0][0]; /** * The runtime-adjustable maximum Blastula pool size. * @hide for internal use only. */ static int sBlastulaPoolMax = BLASTULA_POOL_MAX_LIMIT; public static final String PRIMARY_SOCKET_NAME = "zygote"; /** * The runtime-adjustable minimum Blastula pool size. * @hide for internal use only. */ static int sBlastulaPoolMin = BLASTULA_POOL_MIN_LIMIT; public static final String SECONDARY_SOCKET_NAME = "zygote_secondary"; /** * The runtime-adjustable value used to determine when to re-fill the * blastula pool. The pool will be re-filled when * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold. * @hide for internal use only */ // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax. static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2); public static final String BLASTULA_POOL_PRIMARY_SOCKET_NAME = "blastula_pool"; /** * @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() */ protected static final int[][] INT_ARRAY_2D = new int[0][0]; public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary"; private Zygote() {} Loading Loading @@ -428,70 +410,47 @@ public final class Zygote { protected static native void nativeGetSocketFDs(boolean isPrimary); /** * Initialize the blastula pool and fill it with the desired number of * processes. * Returns the raw string value of a system property. * * Note that Device Config is not available without an application so SystemProperties is used * instead. * * TODO (chriswailes): Cache the system property location in native code and then write a JNI * function to fetch it. */ protected static Runnable initBlastulaPool() { if (BLASTULA_POOL_ENABLED) { sBlastulaPoolEventFD = getBlastulaPoolEventFD(); return fillBlastulaPool(null); } else { return null; } public static String getSystemProperty(String propertyName, String defaultValue) { return SystemProperties.get( String.join(".", "persist.device_config", DeviceConfig.RuntimeNative.NAMESPACE, propertyName), defaultValue); } /** * Checks to see if the current policy says that pool should be refilled, and spawns new * blastulas if necessary. * Returns the value of a system property converted to a boolean using specific logic. * * NOTE: This function doesn't need to be guarded with BLASTULA_POOL_ENABLED because it is * only called from contexts that are only valid if the pool is enabled. * Note that Device Config is not available without an application so SystemProperties is used * instead. * * @param sessionSocketRawFDs Anonymous session sockets that are currently open * @return In the Zygote process this function will always return null; in blastula processes * this function will return a Runnable object representing the new application that is * passed up from blastulaMain. * @see SystemProperties.getBoolean * * TODO (chriswailes): Cache the system property location in native code and then write a JNI * function to fetch it. */ protected static Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); int blastulaPoolCount = getBlastulaPoolCount(); int numBlastulasToSpawn = sBlastulaPoolMax - blastulaPoolCount; if (blastulaPoolCount < sBlastulaPoolMin || numBlastulasToSpawn >= sBlastulaPoolRefillThreshold) { // Disable some VM functionality and reset some system values // before forking. ZygoteHooks.preFork(); resetNicePriority(); while (blastulaPoolCount++ < sBlastulaPoolMax) { Runnable caller = forkBlastula(sessionSocketRawFDs); if (caller != null) { return caller; } } // Re-enable runtime services for the Zygote. Blastula services // are re-enabled in specializeBlastula. ZygoteHooks.postForkCommon(); Log.i("zygote", "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return null; public static boolean getSystemPropertyBoolean(String propertyName, Boolean defaultValue) { return SystemProperties.getBoolean( String.join(".", "persist.device_config", DeviceConfig.RuntimeNative.NAMESPACE, propertyName), defaultValue); } /** * @return Number of blastulas currently in the pool */ private static int getBlastulaPoolCount() { static int getBlastulaPoolCount() { return nativeGetBlastulaPoolCount(); } Loading @@ -501,7 +460,7 @@ public final class Zygote { * @return The event FD used for communication between the signal handler and the ZygoteServer * poll loop */ private static FileDescriptor getBlastulaPoolEventFD() { static FileDescriptor getBlastulaPoolEventFD() { FileDescriptor fd = new FileDescriptor(); fd.setInt$(nativeGetBlastulaPoolEventFD()); Loading @@ -518,7 +477,8 @@ public final class Zygote { * this function will return a Runnable object representing the new application that is * passed up from blastulaMain. */ private static Runnable forkBlastula(int[] sessionSocketRawFDs) { static Runnable forkBlastula(LocalServerSocket blastulaPoolSocket, int[] sessionSocketRawFDs) { FileDescriptor[] pipeFDs = null; try { Loading @@ -532,7 +492,7 @@ public final class Zygote { if (pid == 0) { IoUtils.closeQuietly(pipeFDs[0]); return blastulaMain(pipeFDs[1]); return blastulaMain(blastulaPoolSocket, pipeFDs[1]); } else { // The read-end of the pipe will be closed by the native code. // See removeBlastulaTableEntry(); Loading @@ -553,7 +513,8 @@ public final class Zygote { * of the ZygoteServer. * @return A runnable oject representing the new application. */ static Runnable blastulaMain(FileDescriptor writePipe) { private static Runnable blastulaMain(LocalServerSocket blastulaPoolSocket, FileDescriptor writePipe) { final int pid = Process.myPid(); LocalSocket sessionSocket = null; Loading @@ -563,7 +524,7 @@ public final class Zygote { while (true) { try { sessionSocket = sBlastulaPoolSocket.accept(); sessionSocket = blastulaPoolSocket.accept(); BufferedReader blastulaReader = new BufferedReader(new InputStreamReader(sessionSocket.getInputStream())); Loading Loading @@ -611,7 +572,7 @@ public final class Zygote { System.exit(-1); } finally { IoUtils.closeQuietly(sessionSocket); IoUtils.closeQuietly(sBlastulaPoolSocket); IoUtils.closeQuietly(blastulaPoolSocket); } try { Loading Loading @@ -660,7 +621,7 @@ public final class Zygote { * exception if an invalid arugment is encountered. * @param args The arguments to test */ static void validateBlastulaCommand(ZygoteArguments args) { private static void validateBlastulaCommand(ZygoteArguments args) { if (args.mAbiListQuery) { throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--query-abi-list"); } else if (args.mPidQuery) { Loading Loading @@ -850,20 +811,6 @@ public final class Zygote { return args; } /** * Creates a managed object representing the Blastula pool socket that has * already been initialized and bound by init. * * TODO (chriswailes): Move the name selection logic into this function. * * @throws RuntimeException when open fails */ static void createBlastulaSocket(String socketName) { if (BLASTULA_POOL_ENABLED && sBlastulaPoolSocket == null) { sBlastulaPoolSocket = createManagedSocketFromInitSocket(socketName); } } /** * Creates a managed LocalServerSocket object using a file descriptor * created by an init.rc script. The init scripts that specify the Loading
core/java/com/android/internal/os/ZygoteInit.java +18 −28 Original line number Diff line number Diff line Loading @@ -755,7 +755,7 @@ public class ZygoteInit { } public static void main(String argv[]) { ZygoteServer zygoteServer = new ZygoteServer(); ZygoteServer zygoteServer = null; // Mark zygote start. This ensures that thread creation will throw // an error. Loading Loading @@ -783,7 +783,7 @@ public class ZygoteInit { RuntimeInit.enableDdms(); boolean startSystemServer = false; String socketName = "zygote"; String zygoteSocketName = "zygote"; String abiList = null; boolean enableLazyPreload = false; for (int i = 1; i < argv.length; i++) { Loading @@ -794,26 +794,19 @@ public class ZygoteInit { } else if (argv[i].startsWith(ABI_LIST_ARG)) { abiList = argv[i].substring(ABI_LIST_ARG.length()); } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { socketName = argv[i].substring(SOCKET_NAME_ARG.length()); zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); } } final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME); if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } // 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)); Zygote.getSocketFDs(isPrimaryZygote); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. Loading Loading @@ -846,8 +839,10 @@ public class ZygoteInit { ZygoteHooks.stopZygoteNoThreadCreation(); zygoteServer = new ZygoteServer(isPrimaryZygote); if (startSystemServer) { Runnable r = forkSystemServer(abiList, socketName, zygoteServer); Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r != null} in the // child (system_server) process. Loading @@ -857,24 +852,19 @@ 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; } finally { if (zygoteServer != null) { zygoteServer.closeServerSocket(); } } // We're in the child process and have exited the select loop. Proceed to execute the // command. Loading @@ -894,8 +884,8 @@ public class ZygoteInit { } private static void waitForSecondaryZygote(String socketName) { String otherZygoteName = ZygoteProcess.ZYGOTE_SOCKET_NAME.equals(socketName) ? ZygoteProcess.ZYGOTE_SECONDARY_SOCKET_NAME : ZygoteProcess.ZYGOTE_SOCKET_NAME; String otherZygoteName = Zygote.PRIMARY_SOCKET_NAME.equals(socketName) ? Zygote.SECONDARY_SOCKET_NAME : Zygote.PRIMARY_SOCKET_NAME; ZygoteProcess.waitForConnectionToZygote(otherZygoteName); } Loading
core/java/com/android/internal/os/ZygoteServer.java +189 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes