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

Commit 391bc944 authored by Christian Wailes's avatar Christian Wailes Committed by Gerrit Code Review
Browse files

Merge changes from topic "manual-zygote-snap"

* changes:
  Fixed an issue Zygote initialization.
  Improved timekeeping logic for USAP Pool refill mechanism.
  Resets the refill action/timing info after a pool fill event.
  Make the USAP Pool refill delay configurable.
  Re-adds a trace  point that was accidentally removed.
  Moved some thread priority changes.
  Adjust Java Language thread priority in new processes.
  Adjusts the USAP pool refill mechanism.
  Change USAP name to application name as soon as possible.
parents 3b98950a ce55f80e
Loading
Loading
Loading
Loading
+68 −40
Original line number Diff line number Diff line
@@ -187,6 +187,11 @@ public final class Zygote {
     */
    public static final int SOCKET_BUFFER_SIZE = 256;

    /**
     * @hide for internal use only
     */
    private static final int PRIORITY_MAX = -20;

    /** a prototype instance for a future List.toArray() */
    protected static final int[][] INT_ARRAY_2D = new int[0][0];

@@ -251,8 +256,7 @@ public final class Zygote {
            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
            int targetSdkVersion) {
        ZygoteHooks.preFork();
        // Resets nice priority for zygote process.
        resetNicePriority();

        int pid = nativeForkAndSpecialize(
                uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                fdsToIgnore, startChildZygote, instructionSet, appDataDir);
@@ -264,6 +268,10 @@ public final class Zygote {
            // Note that this event ends at the end of handleChildProc,
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
        }

        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        ZygoteHooks.postForkCommon();
        return pid;
    }
@@ -306,6 +314,9 @@ public final class Zygote {
        // Note that this event ends at the end of handleChildProc.
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");

        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        /*
         * This is called here (instead of after the fork but before the specialize) to maintain
         * consistancy with the code paths for forkAndSpecialize.
@@ -350,15 +361,19 @@ public final class Zygote {
    public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
        ZygoteHooks.preFork();
        // Resets nice priority for zygote process.
        resetNicePriority();

        int pid = nativeForkSystemServer(
                uid, gid, gids, runtimeFlags, rlimits,
                permittedCapabilities, effectiveCapabilities);

        // Enable tracing as soon as we enter the system_server.
        if (pid == 0) {
            Trace.setTracingEnabled(true, runtimeFlags);
        }

        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        ZygoteHooks.postForkCommon();
        return pid;
    }
@@ -476,13 +491,16 @@ public final class Zygote {
    /**
     * Fork a new unspecialized app process from the zygote
     *
     * @param usapPoolSocket  The server socket the USAP will call accept on
     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
     * @param isPriorityFork  Value controlling the process priority level until accept is called
     * @return In the Zygote process this function will always return null; in unspecialized app
     *         processes this function will return a Runnable object representing the new
     *         application that is passed up from usapMain.
     */
    static Runnable forkUsap(LocalServerSocket usapPoolSocket,
                             int[] sessionSocketRawFDs) {
                             int[] sessionSocketRawFDs,
                             boolean isPriorityFork) {
        FileDescriptor[] pipeFDs = null;

        try {
@@ -492,7 +510,8 @@ public final class Zygote {
        }

        int pid =
                nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), sessionSocketRawFDs);
                nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(),
                               sessionSocketRawFDs, isPriorityFork);

        if (pid == 0) {
            IoUtils.closeQuietly(pipeFDs[0]);
@@ -507,7 +526,8 @@ public final class Zygote {

    private static native int nativeForkUsap(int readPipeFD,
                                             int writePipeFD,
                                                 int[] sessionSocketRawFDs);
                                             int[] sessionSocketRawFDs,
                                             boolean isPriorityFork);

    /**
     * This function is used by unspecialized app processes to wait for specialization requests from
@@ -527,6 +547,11 @@ public final class Zygote {
        Credentials peerCredentials = null;
        ZygoteArguments args = null;

        // Change the priority to max before calling accept so we can respond to new specialization
        // requests as quickly as possible.  This will be reverted to the default priority in the
        // native specialization code.
        boostUsapPriority();

        while (true) {
            try {
                sessionSocket = usapPoolSocket.accept();
@@ -568,6 +593,7 @@ public final class Zygote {
        try {
            // SIGTERM is blocked on loop exit.  This prevents a USAP that is specializing from
            // being killed during a pool flush.
            setAppProcessName(args, "USAP");

            applyUidSecurityPolicy(args, peerCredentials);
            applyDebuggerSystemProperty(args);
@@ -634,11 +660,6 @@ public final class Zygote {

            disableExecuteOnly(args.mTargetSdkVersion);

            if (args.mNiceName != null) {
                Process.setArgV0(args.mNiceName);
            }

            // End of the postFork event.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

            return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
@@ -663,6 +684,22 @@ public final class Zygote {

    private static native void nativeUnblockSigTerm();

    private static void boostUsapPriority() {
        nativeBoostUsapPriority();
    }

    private static native void nativeBoostUsapPriority();

    static void setAppProcessName(ZygoteArguments args, String loggingTag) {
        if (args.mNiceName != null) {
            Process.setArgV0(args.mNiceName);
        } else if (args.mPackageName != null) {
            Process.setArgV0(args.mPackageName);
        } else {
            Log.w(loggingTag, "Unable to set package name.");
        }
    }

    private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: ";

    /**
@@ -902,15 +939,6 @@ public final class Zygote {
        ZygoteHooks.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
    }

    /**
     * Resets the calling thread priority to the default value (Thread.NORM_PRIORITY
     * or nice value 0). This updates both the priority value in java.lang.Thread and
     * the nice value (setpriority).
     */
    static void resetNicePriority() {
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
    }

    /**
     * Executes "/system/bin/sh -c <command>" using the exec() system call.
     * This method throws a runtime exception if exec() failed, otherwise, this
+2 −4
Original line number Diff line number Diff line
@@ -346,7 +346,7 @@ class ZygoteConnection {
            if (zygoteServer.isUsapPoolEnabled()) {
                Runnable fpResult =
                        zygoteServer.fillUsapPool(
                                new int[]{mSocket.getFileDescriptor().getInt$()});
                                new int[]{mSocket.getFileDescriptor().getInt$()}, false);

                if (fpResult != null) {
                    zygoteServer.setForkChild();
@@ -485,9 +485,7 @@ class ZygoteConnection {

        closeSocket();

        if (parsedArgs.mNiceName != null) {
            Process.setArgV0(parsedArgs.mNiceName);
        }
        Zygote.setAppProcessName(parsedArgs, TAG);

        // End of the postFork event.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+12 −2
Original line number Diff line number Diff line
@@ -825,6 +825,18 @@ public class ZygoteInit {
        return result;
    }

    /**
     * This is the entry point for a Zygote process.  It creates the Zygote server, loads resources,
     * and handles other tasks related to preparing the process for forking into applications.
     *
     * This process is started with a nice value of -20 (highest priority).  All paths that flow
     * into new processes are required to either set the priority to the default value or terminate
     * before executing any non-system code.  The native side of this occurs in SpecializeCommon,
     * while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess,
     * ZygoteConnection.handleChildProc, and Zygote.usapMain.
     *
     * @param argv  Command line arguments used to specify the Zygote's configuration.
     */
    @UnsupportedAppUsage
    public static void main(String argv[]) {
        ZygoteServer zygoteServer = null;
@@ -888,8 +900,6 @@ public class ZygoteInit {
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                        SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
            }

            // Do an initial gc to clean up after startup
+236 −123
Original line number Diff line number Diff line
@@ -66,6 +66,12 @@ class ZygoteServer {
    /** The default value used for the USAP_POOL_SIZE_MIN device property */
    private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1";

    /** The default value used for the USAP_REFILL_DELAY_MS device property */
    private static final String USAP_POOL_REFILL_DELAY_MS_DEFAULT = "3000";

    /** The "not a timestamp" value for the refill delay timestamp mechanism. */
    private static final int INVALID_TIMESTAMP = -1;

    /**
     * Indicates if this Zygote server can support a unspecialized app process pool.  Currently this
     * should only be true for the primary and secondary Zygotes, and not the App Zygotes or the
@@ -131,6 +137,24 @@ class ZygoteServer {
     */
    private int mUsapPoolRefillThreshold = 0;

    /**
     * Number of milliseconds to delay before refilling the pool if it hasn't reached its
     * minimum value.
     */
    private int mUsapPoolRefillDelayMs = -1;

    /**
     * If and when we should refill the USAP pool.
     */
    private UsapPoolRefillAction mUsapPoolRefillAction;
    private long mUsapPoolRefillTriggerTimestamp;

    private enum UsapPoolRefillAction {
        DELAYED,
        IMMEDIATE,
        NONE
    }

    ZygoteServer() {
        mUsapPoolEventFD = null;
        mZygoteSocket = null;
@@ -160,9 +184,8 @@ class ZygoteServer {
                            Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
        }

        fetchUsapPoolPolicyProps();

        mUsapPoolSupported = true;
        fetchUsapPoolPolicyProps();
    }

    void setForkChild() {
@@ -267,6 +290,13 @@ class ZygoteServer {
                        mUsapPoolSizeMax);
            }

            final String usapPoolRefillDelayMsPropString = Zygote.getConfigurationProperty(
                    ZygoteConfig.USAP_POOL_REFILL_DELAY_MS, USAP_POOL_REFILL_DELAY_MS_DEFAULT);

            if (!usapPoolRefillDelayMsPropString.isEmpty()) {
                mUsapPoolRefillDelayMs = Integer.parseInt(usapPoolRefillDelayMsPropString);
            }

            // Sanity check
            if (mUsapPoolSizeMin >= mUsapPoolSizeMax) {
                Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size."
@@ -293,9 +323,16 @@ class ZygoteServer {
        }
    }

    private void fetchUsapPoolPolicyPropsIfUnfetched() {
        if (mIsFirstPropertyCheck) {
            mIsFirstPropertyCheck = false;
            fetchUsapPoolPolicyProps();
        }
    }

    /**
     * Checks to see if the current policy says that pool should be refilled, and spawns new USAPs
     * if necessary.
     * Refill the USAP Pool to the appropriate level, determined by whether this is a priority
     * refill event or not.
     *
     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
     * @return In the Zygote process this function will always return null; in unspecialized app
@@ -303,25 +340,36 @@ class ZygoteServer {
     *         application that is passed up from usapMain.
     */

    Runnable fillUsapPool(int[] sessionSocketRawFDs) {
    Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillUsapPool");

        // Ensure that the pool properties have been fetched.
        fetchUsapPoolPolicyPropsWithMinInterval();
        fetchUsapPoolPolicyPropsIfUnfetched();

        int usapPoolCount = Zygote.getUsapPoolCount();
        int numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;
        int numUsapsToSpawn;

        if (isPriorityRefill) {
            // Refill to min
            numUsapsToSpawn = mUsapPoolSizeMin - usapPoolCount;

        if (usapPoolCount < mUsapPoolSizeMin
                || numUsapsToSpawn >= mUsapPoolRefillThreshold) {
            Log.i("zygote",
                    "Priority USAP Pool refill. New USAPs: " + numUsapsToSpawn);
        } else {
            // Refill up to max
            numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;

            Log.i("zygote",
                    "Delayed USAP Pool refill. New USAPs: " + numUsapsToSpawn);
        }

        // Disable some VM functionality and reset some system values
        // before forking.
        ZygoteHooks.preFork();
            Zygote.resetNicePriority();

            while (usapPoolCount++ < mUsapPoolSizeMax) {
                Runnable caller = Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs);
        while (--numUsapsToSpawn >= 0) {
            Runnable caller =
                    Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs, isPriorityRefill);

            if (caller != null) {
                return caller;
@@ -332,9 +380,7 @@ class ZygoteServer {
        // are re-enabled in specializeAppProcess.
        ZygoteHooks.postForkCommon();

            Log.i("zygote",
                    "Filled the USAP pool. New USAPs: " + numUsapsToSpawn);
        }
        resetUsapRefillState();

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

@@ -358,13 +404,18 @@ class ZygoteServer {
        mUsapPoolEnabled = newStatus;

        if (newStatus) {
            return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
            return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }, false);
        } else {
            Zygote.emptyUsapPool();
            return null;
        }
    }

    void resetUsapRefillState() {
        mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
        mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
    }

    /**
     * Runs the zygote process's select loop. Accepts new connections as
     * they happen, and reads commands from connections one spawn-request's
@@ -377,8 +428,11 @@ class ZygoteServer {
        socketFDs.add(mZygoteSocket.getFileDescriptor());
        peers.add(null);

        mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;

        while (true) {
            fetchUsapPoolPolicyPropsWithMinInterval();
            mUsapPoolRefillAction = UsapPoolRefillAction.NONE;

            int[] usapPipeFDs = null;
            StructPollfd[] pollFDs;
@@ -430,12 +484,46 @@ class ZygoteServer {
                }
            }

            int pollTimeoutMs;

            if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
                pollTimeoutMs = -1;
            } else {
                long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;

                if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
                    // Normalize the poll timeout value when the time between one poll event and the
                    // next pushes us over the delay value.  This prevents poll receiving a 0
                    // timeout value, which would result in it returning immediately.
                    pollTimeoutMs = -1;

                } else if (elapsedTimeMs <= 0) {
                    // This can occur if the clock used by currentTimeMillis is reset, which is
                    // possible because it is not guaranteed to be monotonic.  Because we can't tell
                    // how far back the clock was set the best way to recover is to simply re-start
                    // the respawn delay countdown.
                    pollTimeoutMs = mUsapPoolRefillDelayMs;

                } else {
                    pollTimeoutMs = (int) (mUsapPoolRefillDelayMs - elapsedTimeMs);
                }
            }

            int pollReturnValue;
            try {
                Os.poll(pollFDs, -1);
                pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }

            if (pollReturnValue == 0) {
                // The poll timeout has been exceeded.  This only occurs when we have finished the
                // USAP pool refill delay period.

                mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;

            } else {
                boolean usapPoolFDRead = false;

                while (--pollIndex >= 0) {
@@ -459,8 +547,8 @@ class ZygoteServer {

                            // TODO (chriswailes): Is this extra check necessary?
                            if (mIsForkChild) {
                            // We're in the child. We should always have a command to run at this
                            // stage if processOneCommand hasn't called "exec".
                                // We're in the child. We should always have a command to run at
                                // this stage if processOneCommand hasn't called "exec".
                                if (command == null) {
                                    throw new IllegalStateException("command == null");
                                }
@@ -474,7 +562,8 @@ class ZygoteServer {

                                // We don't know whether the remote side of the socket was closed or
                                // not until we attempt to read from it from processOneCommand. This
                            // shows up as a regular POLLIN event in our regular processing loop.
                                // shows up as a regular POLLIN event in our regular processing
                                // loop.
                                if (connection.isClosedByPeer()) {
                                    connection.closeSocket();
                                    peers.remove(pollIndex);
@@ -483,10 +572,10 @@ class ZygoteServer {
                            }
                        } catch (Exception e) {
                            if (!mIsForkChild) {
                            // We're in the server so any exception here is one that has taken place
                            // pre-fork while processing commands or reading / writing from the
                            // control socket. Make a loud noise about any such exceptions so that
                            // we know exactly what failed and why.
                                // We're in the server so any exception here is one that has taken
                                // place pre-fork while processing commands or reading / writing
                                // from the control socket. Make a loud noise about any such
                                // exceptions so that we know exactly what failed and why.

                                Slog.e(TAG, "Exception executing zygote command: ", e);

@@ -499,28 +588,33 @@ class ZygoteServer {
                                socketFDs.remove(pollIndex);
                            } else {
                                // We're in the child so any exception caught here has happened post
                            // fork and before we execute ActivityThread.main (or any other main()
                            // method). Log the details of the exception and bring down the process.
                                // fork and before we execute ActivityThread.main (or any other
                                // main() method). Log the details of the exception and bring down
                                // the process.
                                Log.e(TAG, "Caught post-fork exception in child process.", e);
                                throw e;
                            }
                        } finally {
                            // Reset the child flag, in the event that the child process is a child-
                        // zygote. The flag will not be consulted this loop pass after the Runnable
                        // is returned.
                            // zygote. The flag will not be consulted this loop pass after the
                            // Runnable is returned.
                            mIsForkChild = false;
                        }

                    } else {
                        // Either the USAP pool event FD or a USAP reporting pipe.

                        // If this is the event FD the payload will be the number of USAPs removed.
                        // If this is a reporting pipe FD the payload will be the PID of the USAP
                    // that was just specialized.
                    long messagePayload = -1;
                        // that was just specialized.  The `continue` statements below ensure that
                        // the messagePayload will always be valid if we complete the try block
                        // without an exception.
                        long messagePayload;

                        try {
                            byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
                        int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
                            int readBytes =
                                    Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);

                            if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
                                DataInputStream inputStream =
@@ -552,18 +646,37 @@ class ZygoteServer {
                    }
                }

            // Check to see if the USAP pool needs to be refilled.
                if (usapPoolFDRead) {
                    int usapPoolCount = Zygote.getUsapPoolCount();

                    if (usapPoolCount < mUsapPoolSizeMin) {
                        // Immediate refill
                        mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
                    } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {
                        // Delayed refill
                        mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
                    }
                }
            }

            if (mUsapPoolRefillAction != UsapPoolRefillAction.NONE) {
                int[] sessionSocketRawFDs =
                        socketFDs.subList(1, socketFDs.size())
                                .stream()
                                .mapToInt(FileDescriptor::getInt$)
                                .toArray();

                final Runnable command = fillUsapPool(sessionSocketRawFDs);
                final boolean isPriorityRefill =
                        mUsapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;

                final Runnable command =
                        fillUsapPool(sessionSocketRawFDs, isPriorityRefill);

                if (command != null) {
                    return command;
                } else if (isPriorityRefill) {
                    // Schedule a delayed refill to finish refilling the pool.
                    mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
                }
            }
        }
+36 −7

File changed.

Preview size limit exceeded, changes collapsed.