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

Commit b649fda0 authored by Christian Wailes's avatar Christian Wailes Committed by android-build-merger
Browse files

Merge "Enables the use of the blastula pool."

am: 33a09948

Change-Id: Ibe2aa45a1df74674b82127f0c8588694c39461cf
parents 8a2facfa 33a09948
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -498,7 +498,8 @@ public class Process {
                                  String[] zygoteArgs) {
        return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
                    abi, instructionSet, appDataDir, invokeWith,
                    /*useBlastulaPool=*/ true, zygoteArgs);
    }

    /** @hide */
@@ -515,7 +516,8 @@ public class Process {
                                  String[] zygoteArgs) {
        return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
                    abi, instructionSet, appDataDir, invokeWith,
                    /*useBlastulaPool=*/ false, zygoteArgs);
    }

    /**
+164 −48
Original line number Diff line number Diff line
@@ -69,6 +69,16 @@ public class ZygoteProcess {
     */
    public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary";

    /**
     * @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
     */
@@ -83,6 +93,15 @@ 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.
     */
    private final LocalSocketAddress mBlastulaPoolSocketAddress;

    /**
     * The name of the socket used to communicate with the secondary (alternate ABI) blastula pool.
     */
    private final LocalSocketAddress mBlastulaPoolSecondarySocketAddress;

    public ZygoteProcess() {
        mZygoteSocketAddress =
@@ -90,12 +109,22 @@ public class ZygoteProcess {
        mZygoteSecondarySocketAddress =
                new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);

        mBlastulaPoolSocketAddress =
                new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);
        mBlastulaPoolSecondarySocketAddress =
                new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);
    }

    public ZygoteProcess(LocalSocketAddress primarySocketAddress,
                         LocalSocketAddress secondarySocketAddress) {
        mZygoteSocketAddress = primarySocketAddress;
        mZygoteSecondarySocketAddress = secondarySocketAddress;

        mBlastulaPoolSocketAddress = null;
        mBlastulaPoolSecondarySocketAddress = null;
    }

    public LocalSocketAddress getPrimarySocketAddress() {
@@ -107,6 +136,7 @@ public class ZygoteProcess {
     */
    public static class ZygoteState {
        final LocalSocketAddress mZygoteSocketAddress;
        final LocalSocketAddress mBlastulaSocketAddress;

        private final LocalSocket mZygoteSessionSocket;

@@ -118,11 +148,13 @@ public class ZygoteProcess {
        private boolean mClosed;

        private ZygoteState(LocalSocketAddress zygoteSocketAddress,
                            LocalSocketAddress blastulaSocketAddress,
                            LocalSocket zygoteSessionSocket,
                            DataInputStream zygoteInputStream,
                            BufferedWriter zygoteOutputWriter,
                            List<String> abiList) {
            this.mZygoteSocketAddress = zygoteSocketAddress;
            this.mBlastulaSocketAddress = blastulaSocketAddress;
            this.mZygoteSessionSocket = zygoteSessionSocket;
            this.mZygoteInputStream = zygoteInputStream;
            this.mZygoteOutputWriter = zygoteOutputWriter;
@@ -130,14 +162,17 @@ public class ZygoteProcess {
        }

        /**
         * Create a new ZygoteState object by connecting to the given Zygote socket.
         * Create a new ZygoteState object by connecting to the given Zygote socket and saving the
         * given blastula socket address.
         *
         * @param zygoteSocketAddress  Zygote socket to connect to
         * @param blastulaSocketAddress  Blastula socket address to save for later
         * @return  A new ZygoteState object containing a session socket for the given Zygote socket
         * address
         * @throws IOException
         */
        public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress)
        public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress,
                                          LocalSocketAddress blastulaSocketAddress)
                throws IOException {

            DataInputStream zygoteInputStream = null;
@@ -150,7 +185,7 @@ public class ZygoteProcess {
                zygoteOutputWriter =
                        new BufferedWriter(
                                new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
                                256);
                                Zygote.SOCKET_BUFFER_SIZE);
            } catch (IOException ex) {
                try {
                    zygoteSessionSocket.close();
@@ -159,11 +194,18 @@ public class ZygoteProcess {
                throw ex;
            }

            return new ZygoteState(zygoteSocketAddress,
            return new ZygoteState(zygoteSocketAddress, blastulaSocketAddress,
                                   zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
                                   getAbiList(zygoteOutputWriter, zygoteInputStream));
        }

        LocalSocket getBlastulaSessionSocket() throws IOException {
            final LocalSocket blastulaSessionSocket = new LocalSocket();
            blastulaSessionSocket.connect(this.mBlastulaSocketAddress);

            return blastulaSessionSocket;
        }

        boolean matches(String abi) {
            return mABIList.contains(abi);
        }
@@ -259,12 +301,14 @@ public class ZygoteProcess {
                                                  String instructionSet,
                                                  String appDataDir,
                                                  String invokeWith,
                                                  boolean useBlastulaPool,
                                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith,
                    /*startChildZygote=*/false, zygoteArgs);
                    /*startChildZygote=*/false,
                    useBlastulaPool, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
@@ -312,14 +356,12 @@ public class ZygoteProcess {
     */
    @GuardedBy("mLock")
    private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, ArrayList<String> args)
            ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args)
            throws ZygoteStartFailedEx {
        try {
        // Throw early if any of the arguments are malformed. This means we can
        // avoid writing a partial response to the zygote.
            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                if (args.get(i).indexOf('\n') >= 0) {
        for (String arg : args) {
            if (arg.indexOf('\n') >= 0) {
                throw new ZygoteStartFailedEx("embedded newlines not allowed");
            }
        }
@@ -334,37 +376,107 @@ public class ZygoteProcess {
         * the child or -1 on failure, followed by boolean to
         * indicate whether a wrapper process was used.
         */
            final BufferedWriter writer = zygoteState.mZygoteOutputWriter;
            final DataInputStream inputStream = zygoteState.mZygoteInputStream;
        String msgStr = Integer.toString(args.size()) + "\n"
                        + String.join("\n", args) + "\n";

            writer.write(Integer.toString(args.size()));
            writer.newLine();
        // Should there be a timeout on this?
        Process.ProcessStartResult result = new Process.ProcessStartResult();

            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                writer.write(arg);
                writer.newLine();
        // TODO (chriswailes): Move branch body into separate function.
        if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) {
            LocalSocket blastulaSessionSocket = null;

            try {
                blastulaSessionSocket = zygoteState.getBlastulaSessionSocket();

                final BufferedWriter blastulaWriter =
                        new BufferedWriter(
                                new OutputStreamWriter(blastulaSessionSocket.getOutputStream()),
                                Zygote.SOCKET_BUFFER_SIZE);
                final DataInputStream blastulaReader =
                        new DataInputStream(blastulaSessionSocket.getInputStream());

                blastulaWriter.write(msgStr);
                blastulaWriter.flush();

                result.pid = blastulaReader.readInt();
                // Blastulas can't be used to spawn processes that need wrappers.
                result.usingWrapper = false;

                if (result.pid < 0) {
                    throw new ZygoteStartFailedEx("Blastula specialization failed");
                }

            writer.flush();
                return result;
            } catch (IOException ex) {
                // If there was an IOException using the blastula pool we will log the error and
                // attempt to start the process through the Zygote.
                Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - "
                               + ex.toString());
            } finally {
                try {
                    blastulaSessionSocket.close();
                } catch (IOException ex) {
                    Log.e(LOG_TAG, "Failed to close blastula session socket: " + ex.getMessage());
                }
            }
        }

            // Should there be a timeout on this?
            Process.ProcessStartResult result = new Process.ProcessStartResult();
        try {
            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

            zygoteWriter.write(msgStr);
            zygoteWriter.flush();

            // Always read the entire result from the input stream to avoid leaving
            // bytes in the stream for future process starts to accidentally stumble
            // upon.
            result.pid = inputStream.readInt();
            result.usingWrapper = inputStream.readBoolean();
            result.pid = zygoteInputStream.readInt();
            result.usingWrapper = zygoteInputStream.readBoolean();
        } catch (IOException ex) {
            zygoteState.close();
            Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
                    + ex.toString());
            throw new ZygoteStartFailedEx(ex);
        }

        if (result.pid < 0) {
            throw new ZygoteStartFailedEx("fork() failed");
        }

        return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
    }

    /**
     * Flags that may not be passed to a blastula.
     */
    private static final String[] INVALID_BLASTULA_FLAGS = {
        "--query-abi-list",
        "--get-pid",
        "--preload-default",
        "--preload-package",
        "--start-child-zygote",
        "--set-api-blacklist-exemptions",
        "--hidden-api-log-sampling-rate",
        "--invoke-with"
    };

    /**
     * Tests a command list to see if it is valid to send to a blastula.
     * @param args  Zygote/Blastula command arguments
     * @return  True if the command can be passed to a blastula; false otherwise
     */
    private static boolean isValidBlastulaCommand(ArrayList<String> args) {
        for (String flag : args) {
            for (String badFlag : INVALID_BLASTULA_FLAGS) {
                if (flag.startsWith(badFlag)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
@@ -400,6 +512,7 @@ public class ZygoteProcess {
                                                      String appDataDir,
                                                      String invokeWith,
                                                      boolean startChildZygote,
                                                      boolean useBlastulaPool,
                                                      String[] extraArgs)
                                                      throws ZygoteStartFailedEx {
        ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -469,7 +582,9 @@ public class ZygoteProcess {
        }

        synchronized(mLock) {
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                              useBlastulaPool,
                                              argsForZygote);
        }
    }

@@ -633,7 +748,7 @@ public class ZygoteProcess {
        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
            try {
                primaryZygoteState =
                    ZygoteState.connect(mZygoteSocketAddress);
                    ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
            }
@@ -650,7 +765,8 @@ public class ZygoteProcess {
        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
            try {
                secondaryZygoteState =
                    ZygoteState.connect(mZygoteSecondarySocketAddress);
                    ZygoteState.connect(mZygoteSecondarySocketAddress,
                                        mBlastulaPoolSecondarySocketAddress);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
            }
@@ -737,7 +853,7 @@ public class ZygoteProcess {
        for (int n = 20; n >= 0; n--) {
            try {
                final ZygoteState zs =
                        ZygoteState.connect(zygoteSocketAddress);
                        ZygoteState.connect(zygoteSocketAddress, null);
                zs.close();
                return;
            } catch (IOException ioe) {
@@ -778,7 +894,7 @@ public class ZygoteProcess {
            result = startViaZygote(processClass, niceName, uid, gid,
                    gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
                    abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
                    true /* startChildZygote */, extraArgs);
                    true /* startChildZygote */, false /* useBlastulaPool */, extraArgs);
        } catch (ZygoteStartFailedEx ex) {
            throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
        }
+12 −1
Original line number Diff line number Diff line
@@ -105,7 +105,13 @@ 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 */
    /**
     * 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;

    /**
@@ -155,6 +161,11 @@ public final class Zygote {
    // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax.
    static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2);

    /**
     * @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() */
+1 −1
Original line number Diff line number Diff line
@@ -224,7 +224,7 @@ class ZygoteConnection {
            fdsToClose[0] = fd.getInt$();
        }

        fd = zygoteServer.getServerSocketFileDescriptor();
        fd = zygoteServer.getZygoteSocketFileDescriptor();

        if (fd != null) {
            fdsToClose[1] = fd.getInt$();
+24 −7
Original line number Diff line number Diff line
@@ -269,7 +269,7 @@ public class ZygoteInit {

        try {
            BufferedReader br =
                    new BufferedReader(new InputStreamReader(is), 256);
                    new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);

            int count = 0;
            String line;
@@ -750,7 +750,7 @@ public class ZygoteInit {
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
        }

        final Runnable caller;
        Runnable caller;
        try {
            // Report Zygote start time to tron unless it is a runtime restart
            if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
@@ -786,7 +786,17 @@ public class ZygoteInit {
                throw new RuntimeException("No ABI list supplied.");
            }

            zygoteServer.registerServerSocketFromEnv(socketName);
            // 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));

            // In some configurations, we avoid preloading resources and classes eagerly.
            // In such cases, we will preload things prior to our first fork.
            if (!enableLazyPreload) {
@@ -829,11 +839,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();

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