Merge "Adjusts the USAP pool refill mechanism." into qt-dev
am: 40654b7696

Change-Id: I998f2b95043cfc26a8ff0e35d9462e48e2d6dd06
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 2736c6a..dbc15b3 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -172,6 +172,11 @@
      */
     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];
 
@@ -236,8 +241,7 @@
             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);
@@ -249,6 +253,7 @@
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
         }
+
         ZygoteHooks.postForkCommon();
         return pid;
     }
@@ -335,15 +340,16 @@
     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);
         }
+
         ZygoteHooks.postForkCommon();
         return pid;
     }
@@ -461,13 +467,16 @@
     /**
      * 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 {
@@ -477,7 +486,8 @@
         }
 
         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]);
@@ -491,8 +501,9 @@
     }
 
     private static native int nativeForkUsap(int readPipeFD,
-                                                 int writePipeFD,
-                                                 int[] sessionSocketRawFDs);
+                                             int writePipeFD,
+                                             int[] sessionSocketRawFDs,
+                                             boolean isPriorityFork);
 
     /**
      * This function is used by unspecialized app processes to wait for specialization requests from
@@ -515,6 +526,11 @@
         // Load resources
         ZygoteInit.nativePreloadGraphicsDriver();
 
+        // 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();
@@ -618,6 +634,12 @@
                                      null /* classLoader */);
     }
 
+    private static void boostUsapPriority() {
+        nativeBoostUsapPriority();
+    }
+
+    private static native void nativeBoostUsapPriority();
+
     private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: ";
 
     /**
@@ -858,15 +880,6 @@
     }
 
     /**
-     * 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
      * method never returns.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 785256e..1a8ba76 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -346,7 +346,7 @@
             if (zygoteServer.isUsapPoolEnabled()) {
                 Runnable fpResult =
                         zygoteServer.fillUsapPool(
-                                new int[]{mSocket.getFileDescriptor().getInt$()});
+                                new int[]{mSocket.getFileDescriptor().getInt$()}, false);
 
                 if (fpResult != null) {
                     zygoteServer.setForkChild();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f9e868f..c5a0af7 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -822,6 +822,9 @@
     public static void main(String argv[]) {
         ZygoteServer zygoteServer = null;
 
+        // Set the initial thread priority to the "normal" value.
+        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
+
         // Mark zygote start. This ensures that thread creation will throw
         // an error.
         ZygoteHooks.startZygoteNoThreadCreation();
@@ -881,8 +884,6 @@
                 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                         SystemClock.uptimeMillis());
                 bootTimingsTraceLog.traceEnd(); // ZygotePreload
-            } else {
-                Zygote.resetNicePriority();
             }
 
             // Do an initial gc to clean up after startup
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 5d1911b..7eed9b1 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -67,6 +67,15 @@
     private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1";
 
     /**
+     * Number of milliseconds to delay before refilling the pool if it hasn't reached its
+     * minimum value.
+     */
+    private static final int USAP_REFILL_DELAY_MS = 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
      * WebView Zygote.
@@ -131,6 +140,12 @@
      */
     private int mUsapPoolRefillThreshold = 0;
 
+    private enum UsapPoolRefillAction {
+        DELAYED,
+        IMMEDIATE,
+        NONE
+    }
+
     ZygoteServer() {
         mUsapPoolEventFD = null;
         mZygoteSocket = null;
@@ -293,9 +308,16 @@
         }
     }
 
+    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,39 +325,46 @@
      *         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 (usapPoolCount < mUsapPoolSizeMin
-                || numUsapsToSpawn >= mUsapPoolRefillThreshold) {
-
-            // Disable some VM functionality and reset some system values
-            // before forking.
-            ZygoteHooks.preFork();
-            Zygote.resetNicePriority();
-
-            while (usapPoolCount++ < mUsapPoolSizeMax) {
-                Runnable caller = Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs);
-
-                if (caller != null) {
-                    return caller;
-                }
-            }
-
-            // Re-enable runtime services for the Zygote.  Services for unspecialized app process
-            // are re-enabled in specializeAppProcess.
-            ZygoteHooks.postForkCommon();
+        if (isPriorityRefill) {
+            // Refill to min
+            numUsapsToSpawn = mUsapPoolSizeMin - usapPoolCount;
 
             Log.i("zygote",
-                    "Filled the USAP pool. New USAPs: " + numUsapsToSpawn);
+                    "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();
+
+        while (--numUsapsToSpawn >= 0) {
+            Runnable caller =
+                    Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs, isPriorityRefill);
+
+            if (caller != null) {
+                return caller;
+            }
+        }
+
+        // Re-enable runtime services for the Zygote.  Services for unspecialized app process
+        // are re-enabled in specializeAppProcess.
+        ZygoteHooks.postForkCommon();
+
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
         return null;
@@ -358,7 +387,7 @@
         mUsapPoolEnabled = newStatus;
 
         if (newStatus) {
-            return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
+            return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }, false);
         } else {
             Zygote.emptyUsapPool();
             return null;
@@ -377,6 +406,8 @@
         socketFDs.add(mZygoteSocket.getFileDescriptor());
         peers.add(null);
 
+        long usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+
         while (true) {
             fetchUsapPoolPolicyPropsWithMinInterval();
 
@@ -428,140 +459,192 @@
                 }
             }
 
+            int pollTimeoutMs;
+
+            if (usapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
+                pollTimeoutMs = -1;
+            } else {
+                int elapsedTimeMs =
+                        (int) (System.currentTimeMillis() - usapPoolRefillTriggerTimestamp);
+
+                if (elapsedTimeMs >= USAP_REFILL_DELAY_MS) {
+                    // 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 {
+                    pollTimeoutMs = USAP_REFILL_DELAY_MS - elapsedTimeMs;
+                }
+            }
+
+            int pollReturnValue;
             try {
-                Os.poll(pollFDs, -1);
+                pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
             } catch (ErrnoException ex) {
                 throw new RuntimeException("poll failed", ex);
             }
 
-            boolean usapPoolFDRead = false;
+            UsapPoolRefillAction usapPoolRefillAction = UsapPoolRefillAction.NONE;
+            if (pollReturnValue == 0) {
+                // The poll timeout has been exceeded.  This only occurs when we have finished the
+                // USAP pool refill delay period.
 
-            while (--pollIndex >= 0) {
-                if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
-                    continue;
-                }
+                usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+                usapPoolRefillAction = UsapPoolRefillAction.DELAYED;
 
-                if (pollIndex == 0) {
-                    // Zygote server socket
+            } else {
+                boolean usapPoolFDRead = false;
 
-                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
-                    peers.add(newPeer);
-                    socketFDs.add(newPeer.getFileDescriptor());
-
-                } else if (pollIndex < usapPoolEventFDIndex) {
-                    // Session socket accepted from the Zygote server socket
-
-                    try {
-                        ZygoteConnection connection = peers.get(pollIndex);
-                        final Runnable command = connection.processOneCommand(this);
-
-                        // 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".
-                            if (command == null) {
-                                throw new IllegalStateException("command == null");
-                            }
-
-                            return command;
-                        } else {
-                            // We're in the server - we should never have any commands to run.
-                            if (command != null) {
-                                throw new IllegalStateException("command != null");
-                            }
-
-                            // 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.
-                            if (connection.isClosedByPeer()) {
-                                connection.closeSocket();
-                                peers.remove(pollIndex);
-                                socketFDs.remove(pollIndex);
-                            }
-                        }
-                    } 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.
-
-                            Slog.e(TAG, "Exception executing zygote command: ", e);
-
-                            // Make sure the socket is closed so that the other end knows
-                            // immediately that something has gone wrong and doesn't time out
-                            // waiting for a response.
-                            ZygoteConnection conn = peers.remove(pollIndex);
-                            conn.closeSocket();
-
-                            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.
-                            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.
-                        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;
-
-                    try {
-                        byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
-                        int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
-
-                        if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
-                            DataInputStream inputStream =
-                                    new DataInputStream(new ByteArrayInputStream(buffer));
-
-                            messagePayload = inputStream.readLong();
-                        } else {
-                            Log.e(TAG, "Incomplete read from USAP management FD of size "
-                                    + readBytes);
-                            continue;
-                        }
-                    } catch (Exception ex) {
-                        if (pollIndex == usapPoolEventFDIndex) {
-                            Log.e(TAG, "Failed to read from USAP pool event FD: "
-                                    + ex.getMessage());
-                        } else {
-                            Log.e(TAG, "Failed to read from USAP reporting pipe: "
-                                    + ex.getMessage());
-                        }
-
+                while (--pollIndex >= 0) {
+                    if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                         continue;
                     }
 
-                    if (pollIndex > usapPoolEventFDIndex) {
-                        Zygote.removeUsapTableEntry((int) messagePayload);
-                    }
+                    if (pollIndex == 0) {
+                        // Zygote server socket
 
-                    usapPoolFDRead = true;
+                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
+                        peers.add(newPeer);
+                        socketFDs.add(newPeer.getFileDescriptor());
+
+                    } else if (pollIndex < usapPoolEventFDIndex) {
+                        // Session socket accepted from the Zygote server socket
+
+                        try {
+                            ZygoteConnection connection = peers.get(pollIndex);
+                            final Runnable command = connection.processOneCommand(this);
+
+                            // 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".
+                                if (command == null) {
+                                    throw new IllegalStateException("command == null");
+                                }
+
+                                return command;
+                            } else {
+                                // We're in the server - we should never have any commands to run.
+                                if (command != null) {
+                                    throw new IllegalStateException("command != null");
+                                }
+
+                                // 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.
+                                if (connection.isClosedByPeer()) {
+                                    connection.closeSocket();
+                                    peers.remove(pollIndex);
+                                    socketFDs.remove(pollIndex);
+                                }
+                            }
+                        } 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.
+
+                                Slog.e(TAG, "Exception executing zygote command: ", e);
+
+                                // Make sure the socket is closed so that the other end knows
+                                // immediately that something has gone wrong and doesn't time out
+                                // waiting for a response.
+                                ZygoteConnection conn = peers.remove(pollIndex);
+                                conn.closeSocket();
+
+                                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.
+                                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.
+                            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.  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);
+
+                            if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
+                                DataInputStream inputStream =
+                                        new DataInputStream(new ByteArrayInputStream(buffer));
+
+                                messagePayload = inputStream.readLong();
+                            } else {
+                                Log.e(TAG, "Incomplete read from USAP management FD of size "
+                                        + readBytes);
+                                continue;
+                            }
+                        } catch (Exception ex) {
+                            if (pollIndex == usapPoolEventFDIndex) {
+                                Log.e(TAG, "Failed to read from USAP pool event FD: "
+                                        + ex.getMessage());
+                            } else {
+                                Log.e(TAG, "Failed to read from USAP reporting pipe: "
+                                        + ex.getMessage());
+                            }
+
+                            continue;
+                        }
+
+                        if (pollIndex > usapPoolEventFDIndex) {
+                            Zygote.removeUsapTableEntry((int) messagePayload);
+                        }
+
+                        usapPoolFDRead = true;
+                    }
+                }
+
+                if (usapPoolFDRead) {
+                    int usapPoolCount = Zygote.getUsapPoolCount();
+
+                    if (usapPoolCount < mUsapPoolSizeMin) {
+                        // Immediate refill
+                        usapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
+                    } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {
+                        // Delayed refill
+                        usapPoolRefillTriggerTimestamp = System.currentTimeMillis();
+                    }
                 }
             }
 
-            // Check to see if the USAP pool needs to be refilled.
-            if (usapPoolFDRead) {
+            if (usapPoolRefillAction != UsapPoolRefillAction.NONE) {
                 int[] sessionSocketRawFDs =
                         socketFDs.subList(1, socketFDs.size())
                                 .stream()
                                 .mapToInt(fd -> fd.getInt$())
                                 .toArray();
 
-                final Runnable command = fillUsapPool(sessionSocketRawFDs);
+                final boolean isPriorityRefill =
+                        usapPoolRefillAction == 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.
+                    usapPoolRefillTriggerTimestamp = System.currentTimeMillis();
                 }
             }
         }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8ff1691..5267427 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -169,6 +169,15 @@
  */
 static constexpr int USAP_POOL_SIZE_MAX_LIMIT = 100;
 
+/** The numeric value for the maximum priority a process may possess. */
+static constexpr int PROCESS_PRIORITY_MAX = -20;
+
+/** The numeric value for the minimum priority a process may possess. */
+static constexpr int PROCESS_PRIORITY_MIN = 19;
+
+/** The numeric value for the normal priority a process should have. */
+static constexpr int PROCESS_PRIORITY_DEFAULT = 0;
+
 /**
  * A helper class containing accounting information for USAPs.
  */
@@ -893,7 +902,8 @@
 // Utility routine to fork a process from the zygote.
 static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
                         const std::vector<int>& fds_to_close,
-                        const std::vector<int>& fds_to_ignore) {
+                        const std::vector<int>& fds_to_ignore,
+                        bool is_priority_fork) {
   SetSignalHandlers();
 
   // Curry a failure function.
@@ -926,6 +936,12 @@
   pid_t pid = fork();
 
   if (pid == 0) {
+    if (is_priority_fork) {
+      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+    } else {
+      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
+    }
+
     // The child process.
     PreApplicationInit();
 
@@ -1123,6 +1139,9 @@
   env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
                             is_system_server, is_child_zygote, managed_instruction_set);
 
+  // Reset the process priority to the default value.
+  setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
+
   if (env->ExceptionCheck()) {
     fail_fn("Error calling post fork hooks.");
   }
@@ -1360,7 +1379,7 @@
       fds_to_ignore.push_back(gUsapPoolEventFD);
     }
 
-    pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore);
+    pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
 
     if (pid == 0) {
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
@@ -1387,7 +1406,8 @@
 
   pid_t pid = ForkCommon(env, true,
                          fds_to_close,
-                         fds_to_ignore);
+                         fds_to_ignore,
+                         true);
   if (pid == 0) {
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                        permitted_capabilities, effective_capabilities,
@@ -1429,13 +1449,15 @@
  * zygote in managed code.
  * @param managed_session_socket_fds  A list of anonymous session sockets that must be ignored by
  * the FD hygiene code and automatically "closed" in the new USAP.
+ * @param is_priority_fork  Controls the nice level assigned to the newly created process
  * @return
  */
 static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env,
                                                           jclass,
                                                           jint read_pipe_fd,
                                                           jint write_pipe_fd,
-                                                          jintArray managed_session_socket_fds) {
+                                                          jintArray managed_session_socket_fds,
+                                                          jboolean is_priority_fork) {
   std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
                    fds_to_ignore(fds_to_close);
 
@@ -1457,7 +1479,8 @@
   fds_to_ignore.push_back(write_pipe_fd);
   fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end());
 
-  pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore);
+  pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore,
+                              is_priority_fork == JNI_TRUE);
 
   if (usap_pid != 0) {
     ++gUsapPoolCount;
@@ -1678,6 +1701,10 @@
   return dl_iterate_phdr(disable_execute_only, nullptr) == 0;
 }
 
+static void com_android_internal_os_Zygote_nativeBoostUsapPriority(JNIEnv* env, jclass) {
+  setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+}
+
 static const JNINativeMethod gMethods[] = {
     { "nativeForkAndSpecialize",
       "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
@@ -1690,7 +1717,7 @@
       (void *) com_android_internal_os_Zygote_nativePreApplicationInit },
     { "nativeInstallSeccompUidGidFilter", "(II)V",
       (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter },
-    { "nativeForkUsap", "(II[I)I",
+    { "nativeForkUsap", "(II[IZ)I",
       (void *) com_android_internal_os_Zygote_nativeForkUsap },
     { "nativeSpecializeAppProcess",
       "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
@@ -1708,7 +1735,9 @@
     { "nativeEmptyUsapPool", "()V",
       (void *) com_android_internal_os_Zygote_nativeEmptyUsapPool },
     { "nativeDisableExecuteOnly", "()Z",
-      (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly }
+      (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly },
+    { "nativeBoostUsapPriority", "()V",
+      (void* ) com_android_internal_os_Zygote_nativeBoostUsapPriority }
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {