summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mathieu Chartier <mathieuc@google.com> 2019-01-30 15:56:17 -0800
committer Chris Wailes <chriswailes@google.com> 2019-02-15 17:07:37 -0800
commit0bccbf7984e482b66dd4f4f6561b25c6a65af37f (patch)
treea9724e22fbeb540eec7b661a400dfe81165c6c1d
parentc6c879768714916788530a0edb5ffa29a8352ed8 (diff)
Add blastula pool system properties
This patch adds the following properties to DeviceConfig: * BLASTULA_POOL_ENABLED * BLASTULA_POOL_SIZE_MAX * BLASTULA_POOL_SIZE_MIN * BLASTULA_POOL_REFILL_THERSHOLD The BLASTULA_POOL_ENABLED property is checked by ZygoteProcess but not currently used due to an existing bug (b/123409530). This will be enabled by go/ag/6162164. Until then the code path is tested via log inspection. The remaining properties are checked by ZygoteServer. Values are clamped to ensure that they cannot be set to values that are harmful to the device. Since device_config is not available in the Zygote the system properties interface is used instead. Bug: 123524494 Bug: 68253328 Test: adb shell device_config put runtime_native blastula_pool_enabled true Test: manually verify the property change is observed Change-Id: I2b675afd9fbc1cbb0e8bc1c491cfdbbb612d0d3b
-rw-r--r--core/java/android/os/ZygoteProcess.java92
-rw-r--r--core/java/android/provider/DeviceConfig.java32
-rw-r--r--core/java/com/android/internal/os/Zygote.java167
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java46
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java204
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp3
6 files changed, 365 insertions, 179 deletions
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index ee3d35427b29..1de811792e7d 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -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;
@@ -66,16 +67,6 @@ 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.
- */
public static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000;
/**
@@ -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.
@@ -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.
*/
@@ -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,
@@ -272,6 +263,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.
*
* <p>If processes are enabled, a new process is created and the
@@ -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,
@@ -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 {
@@ -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(',');
@@ -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.
*/
@@ -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 =
@@ -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;
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 7eb03007ee6a..f6a8388a7d40 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -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";
}
/**
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 40d78688cb4c..22884ac9e3fc 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -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;
@@ -129,21 +130,6 @@ public final class Zygote {
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
* should listen for connections on.
@@ -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() {}
@@ -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.
- */
- protected static Runnable initBlastulaPool() {
- if (BLASTULA_POOL_ENABLED) {
- sBlastulaPoolEventFD = getBlastulaPoolEventFD();
-
- return fillBlastulaPool(null);
- } else {
- return null;
- }
+ * 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.
+ */
+ 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.
- */
- 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;
+ * @see SystemProperties.getBoolean
+ *
+ * TODO (chriswailes): Cache the system property location in native code and then write a JNI
+ * function to fetch it.
+ */
+ 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();
}
@@ -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());
@@ -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 {
@@ -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();
@@ -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;
@@ -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()));
@@ -611,7 +572,7 @@ public final class Zygote {
System.exit(-1);
} finally {
IoUtils.closeQuietly(sessionSocket);
- IoUtils.closeQuietly(sBlastulaPoolSocket);
+ IoUtils.closeQuietly(blastulaPoolSocket);
}
try {
@@ -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) {
@@ -851,20 +812,6 @@ public final class Zygote {
}
/**
- * 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
* sockets name can be found in system/core/rootdir. The socket is bound
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index e132abd7e4cb..7cddf7588b28 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -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.
@@ -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++) {
@@ -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.
@@ -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.
@@ -857,23 +852,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();
+ Log.i(TAG, "Accepting command socket connections");
- 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);
- }
+ // 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 {
- zygoteServer.closeServerSocket();
+ if (zygoteServer != null) {
+ zygoteServer.closeServerSocket();
+ }
}
// We're in the child process and have exited the select loop. Proceed to execute the
@@ -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);
}
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index a78c095a8deb..24269efc3f26 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -20,12 +20,17 @@ import static android.system.OsConstants.POLLIN;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.provider.DeviceConfig;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructPollfd;
import android.util.Log;
import android.util.Slog;
+import dalvik.system.ZygoteHooks;
+
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileDescriptor;
@@ -38,7 +43,7 @@ import java.util.ArrayList;
* Provides functions to wait for commands on a UNIX domain socket, and fork
* off child processes that inherit the initial state of the VM.%
*
- * Please see {@link ZygoteConnection.Arguments} for documentation on the
+ * Please see {@link ZygoteArguments} for documentation on the
* client protocol.
*/
class ZygoteServer {
@@ -46,11 +51,48 @@ class ZygoteServer {
public static final String TAG = "ZygoteServer";
/**
+ * The maximim value that will be accepted from the BLASTULA_POOL_SIZE_MAX device property.
+ * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp.
+ */
+ private static final int BLASTULA_POOL_SIZE_MAX_LIMIT = 100;
+
+ /**
+ * The minimum value that will be accepted from the BLASTULA_POOL_SIZE_MIN device property.
+ */
+ private static final int BLASTULA_POOL_SIZE_MIN_LIMIT = 1;
+
+ /** The default value used for the BLASTULA_POOL_SIZE_MAX device property */
+ private static final String BLASTULA_POOL_SIZE_MAX_DEFAULT = "10";
+
+ /** The default value used for the BLASTULA_POOL_SIZE_MIN device property */
+ private static final String BLASTULA_POOL_SIZE_MIN_DEFAULT = "1";
+
+ /**
+ * 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;
+
+ /**
* Listening socket that accepts new server connections.
*/
private LocalServerSocket mZygoteSocket;
/**
+ * The name of the blastula socket to use if the blastula pool is enabled.
+ */
+ private LocalServerSocket mBlastulaPoolSocket;
+
+ /**
+ * File descriptor used for communication between the signal handler and the ZygoteServer poll
+ * loop.
+ * */
+ private FileDescriptor mBlastulaPoolEventFD;
+
+ /**
* Whether or not mZygoteSocket's underlying FD should be closed directly.
* If mZygoteSocket is created with an existing FD, closing the socket does
* not close the FD and it must be closed explicitly. If the socket is created
@@ -64,25 +106,55 @@ class ZygoteServer {
*/
private boolean mIsForkChild;
- ZygoteServer() { }
+ /**
+ * The runtime-adjustable maximum Blastula pool size.
+ */
+ private int mBlastulaPoolSizeMax = 0;
- void setForkChild() {
- mIsForkChild = true;
+ /**
+ * The runtime-adjustable minimum Blastula pool size.
+ */
+ private int mBlastulaPoolSizeMin = 0;
+
+ /**
+ * The runtime-adjustable value used to determine when to re-fill the
+ * blastula pool. The pool will be re-filled when
+ * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold.
+ */
+ private int mBlastulaPoolRefillThreshold = 0;
+
+ ZygoteServer() {
+ mBlastulaPoolEventFD = null;
+ mZygoteSocket = null;
+ mBlastulaPoolSocket = null;
}
/**
- * Creates a managed object representing the Zygote socket that has already
- * been initialized and bound by init.
- *
- * TODO (chriswailes): Move the name selection logic into this function.
+ * Initialize the Zygote server with the Zygote server socket, blastula pool server socket,
+ * and blastula pool event FD.
*
- * @throws RuntimeException when open fails
+ * @param isPrimaryZygote If this is the primary Zygote or not.
*/
- void createZygoteSocket(String socketName) {
- if (mZygoteSocket == null) {
- mZygoteSocket = Zygote.createManagedSocketFromInitSocket(socketName);
- mCloseSocketFd = true;
+ ZygoteServer(boolean isPrimaryZygote) {
+ mBlastulaPoolEventFD = Zygote.getBlastulaPoolEventFD();
+
+ if (isPrimaryZygote) {
+ mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
+ mBlastulaPoolSocket =
+ Zygote.createManagedSocketFromInitSocket(
+ Zygote.BLASTULA_POOL_PRIMARY_SOCKET_NAME);
+ } else {
+ mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
+ mBlastulaPoolSocket =
+ Zygote.createManagedSocketFromInitSocket(
+ Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME);
}
+
+ fetchBlastulaPoolPolicyProps();
+ }
+
+ void setForkChild() {
+ mIsForkChild = true;
}
/**
@@ -151,6 +223,104 @@ class ZygoteServer {
return mZygoteSocket.getFileDescriptor();
}
+ private void fetchBlastulaPoolPolicyProps() {
+ final String blastulaPoolSizeMaxPropString =
+ Zygote.getSystemProperty(
+ DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX,
+ BLASTULA_POOL_SIZE_MAX_DEFAULT);
+
+ if (!blastulaPoolSizeMaxPropString.isEmpty()) {
+ mBlastulaPoolSizeMax =
+ Integer.min(
+ Integer.parseInt(blastulaPoolSizeMaxPropString),
+ BLASTULA_POOL_SIZE_MAX_LIMIT);
+ }
+
+ final String blastulaPoolSizeMinPropString =
+ Zygote.getSystemProperty(
+ DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN,
+ BLASTULA_POOL_SIZE_MIN_DEFAULT);
+
+ if (!blastulaPoolSizeMinPropString.isEmpty()) {
+ mBlastulaPoolSizeMin =
+ Integer.max(
+ Integer.parseInt(blastulaPoolSizeMinPropString),
+ BLASTULA_POOL_SIZE_MIN_LIMIT);
+ }
+
+ final String blastulaPoolRefillThresholdPropString =
+ Zygote.getSystemProperty(
+ DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD,
+ Integer.toString(mBlastulaPoolSizeMax / 2));
+
+ if (!blastulaPoolRefillThresholdPropString.isEmpty()) {
+ mBlastulaPoolRefillThreshold =
+ Integer.min(
+ Integer.parseInt(blastulaPoolRefillThresholdPropString),
+ mBlastulaPoolSizeMax);
+ }
+
+ }
+
+ private long mLastPropCheckTimestamp = 0;
+
+ private void fetchBlastulaPoolPolicyPropsWithMinInterval() {
+ final long currentTimestamp = SystemClock.elapsedRealtime();
+
+ if (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL) {
+ fetchBlastulaPoolPolicyProps();
+ mLastPropCheckTimestamp = currentTimestamp;
+ }
+ }
+
+ /**
+ * Checks to see if the current policy says that pool should be refilled, and spawns new
+ * blastulas if necessary.
+ *
+ * @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.
+ */
+ private Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
+ Log.i(TAG, "FDHUNT - Marker 2 - fillBlastulaPool");
+
+ if (mBlastulaPoolEnabled) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
+
+ int blastulaPoolCount = Zygote.getBlastulaPoolCount();
+ int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount;
+
+ if (blastulaPoolCount < mBlastulaPoolSizeMin
+ || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) {
+
+ // Disable some VM functionality and reset some system values
+ // before forking.
+ ZygoteHooks.preFork();
+ Zygote.resetNicePriority();
+
+ while (blastulaPoolCount++ < mBlastulaPoolSizeMax) {
+ Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, 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;
+ }
+
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
@@ -164,6 +334,8 @@ class ZygoteServer {
peers.add(null);
while (true) {
+ fetchBlastulaPoolPolicyPropsWithMinInterval();
+
int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
// Space for all of the socket FDs, the Blastula Pool Event FD, and
@@ -181,7 +353,7 @@ class ZygoteServer {
final int blastulaPoolEventFDIndex = pollIndex;
pollFDs[pollIndex] = new StructPollfd();
- pollFDs[pollIndex].fd = Zygote.sBlastulaPoolEventFD;
+ pollFDs[pollIndex].fd = mBlastulaPoolEventFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
@@ -275,6 +447,8 @@ class ZygoteServer {
} else {
// Either the blastula pool event FD or a blastula reporting pipe.
+ Log.i(TAG, "FDHUNT - Marker 1 - runSelectLoop");
+
// If this is the event FD the payload will be the number of blastulas removed.
// If this is a reporting pipe FD the payload will be the PID of the blastula
// that was just specialized.
@@ -316,7 +490,7 @@ class ZygoteServer {
.mapToInt(fd -> fd.getInt$())
.toArray();
- final Runnable command = Zygote.fillBlastulaPool(sessionSocketRawFDs);
+ final Runnable command = fillBlastulaPool(sessionSocketRawFDs);
if (command != null) {
return command;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7b4e4ea43415..4649b5262723 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1357,6 +1357,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
SetSchedulerPolicy(fail_fn);
+ __android_log_close();
+ stats_log_close();
+
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;