Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18f7673f authored by Christian Wailes's avatar Christian Wailes Committed by Android (Google) Code Review
Browse files

Merge "Add blastula pool system properties"

parents 975c6e2d 0bccbf79
Loading
Loading
Loading
Loading
+66 −26
Original line number Diff line number Diff line
@@ -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.
     */
@@ -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,
@@ -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;
+32 −0
Original line number Diff line number Diff line
@@ -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";
    }

    /**
+57 −110
Original line number Diff line number Diff line
@@ -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,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.
     * 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
+18 −28
Original line number Diff line number Diff line
@@ -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,24 +852,19 @@ public class ZygoteInit {
                }
            }

            // If the return value is null then this is the zygote process
            // returning to the normal control flow.  If it returns a Runnable
            // object then this is a blastula that has finished specializing.
            caller = Zygote.initBlastulaPool();

            if (caller == null) {
            Log.i(TAG, "Accepting command socket connections");

            // The select loop returns early in the child process after a fork and
            // loops forever in the zygote.
            caller = zygoteServer.runSelectLoop(abiList);
            }
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            if (zygoteServer != null) {
                zygoteServer.closeServerSocket();
            }
        }

        // We're in the child process and have exited the select loop. Proceed to execute the
        // command.
@@ -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);
    }

+189 −15

File changed.

Preview size limit exceeded, changes collapsed.

Loading