Merge "Add blastula pool system properties"

This commit is contained in:
Christian Wailes
2019-02-19 18:15:48 +00:00
committed by Android (Google) Code Review
6 changed files with 373 additions and 187 deletions

View File

@@ -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;
@@ -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.
*/
@@ -86,21 +77,16 @@ public class ZygoteProcess {
*/
public static final int ZYGOTE_CONNECT_RETRY_DELAY_MS = 50;
/**
* @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
*/
private static final String LOG_TAG = "ZygoteProcess";
/**
* The default value for enabling the blastula pool.
*/
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,
@@ -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.
*
@@ -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;

View File

@@ -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";
}
/**

View File

@@ -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;
@@ -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
@@ -174,44 +160,40 @@ 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;
/**
* The minimum value that the sBlastulaPoolMin variable may take.
*/
static final int BLASTULA_POOL_MIN_LIMIT = 1;
/**
* The runtime-adjustable maximum Blastula pool size.
*/
static int sBlastulaPoolMax = BLASTULA_POOL_MAX_LIMIT;
/**
* The runtime-adjustable minimum Blastula pool size.
*/
static int sBlastulaPoolMin = BLASTULA_POOL_MIN_LIMIT;
/**
* The runtime-adjustable value used to determine when to re-fill the
* blastula pool. The pool will be re-filled when
* (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold.
*/
// TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax.
static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2);
public static final long PROPERTY_CHECK_INTERVAL = 300000;
/**
* @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];
/**
* @hide for internal use only.
*/
public static final String PRIMARY_SOCKET_NAME = "zygote";
/**
* @hide for internal use only.
*/
public static final String SECONDARY_SOCKET_NAME = "zygote_secondary";
/**
* @hide for internal use only
*/
public static final String BLASTULA_POOL_PRIMARY_SOCKET_NAME = "blastula_pool";
/**
* @hide for internal use only
*/
public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary";
private Zygote() {}
/** Called for some security initialization before any fork. */
@@ -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();
}
@@ -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) {
@@ -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

View File

@@ -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);
}

View File

@@ -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,18 +43,55 @@ 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 {
// TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
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
@@ -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.
* Initialize the Zygote server with the Zygote server socket, blastula pool server socket,
* and blastula pool event FD.
*
* TODO (chriswailes): Move the name selection logic into this function.
*
* @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;

View File

@@ -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;