Merge "Add blastula pool system properties"
This commit is contained in:
committed by
Android (Google) Code Review
commit
18f7673fcd
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user