Loading core/java/com/android/internal/os/Zygote.java +30 −17 Original line number Diff line number Diff line Loading @@ -172,6 +172,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]; Loading Loading @@ -236,8 +241,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); Loading @@ -249,6 +253,7 @@ public final class Zygote { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); } ZygoteHooks.postForkCommon(); return pid; } Loading Loading @@ -335,15 +340,16 @@ 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); } ZygoteHooks.postForkCommon(); return pid; } Loading Loading @@ -461,13 +467,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 { Loading @@ -477,7 +486,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]); Loading @@ -492,7 +502,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 Loading @@ -515,6 +526,11 @@ public final class Zygote { // 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(); Loading Loading @@ -626,6 +642,12 @@ public final class Zygote { } } private static void boostUsapPriority() { nativeBoostUsapPriority(); } private static native void nativeBoostUsapPriority(); private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: "; /** Loading Loading @@ -865,15 +887,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 Loading core/java/com/android/internal/os/ZygoteConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -330,7 +330,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(); Loading core/java/com/android/internal/os/ZygoteInit.java +3 −2 Original line number Diff line number Diff line Loading @@ -822,6 +822,9 @@ public class ZygoteInit { 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(); Loading Loading @@ -881,8 +884,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 Loading core/java/com/android/internal/os/ZygoteServer.java +206 −125 Original line number Diff line number Diff line Loading @@ -66,6 +66,15 @@ class ZygoteServer { /** The default value used for the USAP_POOL_SIZE_MIN device property */ 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 Loading Loading @@ -131,6 +140,12 @@ class ZygoteServer { */ private int mUsapPoolRefillThreshold = 0; private enum UsapPoolRefillAction { DELAYED, IMMEDIATE, NONE } ZygoteServer() { mUsapPoolEventFD = null; mZygoteSocket = null; Loading Loading @@ -293,9 +308,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 Loading @@ -303,25 +325,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; Loading @@ -332,10 +365,6 @@ class ZygoteServer { // are re-enabled in specializeAppProcess. ZygoteHooks.postForkCommon(); Log.i("zygote", "Filled the USAP pool. New USAPs: " + numUsapsToSpawn); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return null; Loading @@ -358,7 +387,7 @@ 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; Loading @@ -377,6 +406,8 @@ class ZygoteServer { socketFDs.add(mZygoteSocket.getFileDescriptor()); peers.add(null); long usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; while (true) { fetchUsapPoolPolicyPropsWithMinInterval(); Loading Loading @@ -430,12 +461,40 @@ class ZygoteServer { } } 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); } 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. usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; usapPoolRefillAction = UsapPoolRefillAction.DELAYED; } else { boolean usapPoolFDRead = false; while (--pollIndex >= 0) { Loading @@ -459,8 +518,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"); } Loading @@ -474,7 +533,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); Loading @@ -483,10 +543,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); Loading @@ -499,15 +559,16 @@ 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 { Loading @@ -516,13 +577,14 @@ class ZygoteServer { // 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. // 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 = Loading Loading @@ -554,18 +616,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 usapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE; } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) { // Delayed refill usapPoolRefillTriggerTimestamp = System.currentTimeMillis(); } } } if (usapPoolRefillAction != UsapPoolRefillAction.NONE) { int[] sessionSocketRawFDs = socketFDs.subList(1, socketFDs.size()) .stream() .mapToInt(FileDescriptor::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(); } } } Loading core/jni/com_android_internal_os_Zygote.cpp +36 −7 Original line number Diff line number Diff line Loading @@ -169,6 +169,15 @@ static int gUsapPoolEventFD = -1; */ 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. */ Loading Loading @@ -893,7 +902,8 @@ static void ClearUsapTable() { // 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. Loading Loading @@ -926,6 +936,12 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, 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(); Loading Loading @@ -1123,6 +1139,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, 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."); } Loading Loading @@ -1360,7 +1379,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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, Loading @@ -1387,7 +1406,8 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( 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, Loading Loading @@ -1429,13 +1449,15 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( * 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); Loading @@ -1457,7 +1479,8 @@ static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, 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; Loading Loading @@ -1678,6 +1701,10 @@ static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* 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", Loading @@ -1690,7 +1717,7 @@ static const JNINativeMethod gMethods[] = { (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", Loading @@ -1708,7 +1735,9 @@ static const JNINativeMethod gMethods[] = { { "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) { Loading Loading
core/java/com/android/internal/os/Zygote.java +30 −17 Original line number Diff line number Diff line Loading @@ -172,6 +172,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]; Loading Loading @@ -236,8 +241,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); Loading @@ -249,6 +253,7 @@ public final class Zygote { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); } ZygoteHooks.postForkCommon(); return pid; } Loading Loading @@ -335,15 +340,16 @@ 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); } ZygoteHooks.postForkCommon(); return pid; } Loading Loading @@ -461,13 +467,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 { Loading @@ -477,7 +486,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]); Loading @@ -492,7 +502,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 Loading @@ -515,6 +526,11 @@ public final class Zygote { // 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(); Loading Loading @@ -626,6 +642,12 @@ public final class Zygote { } } private static void boostUsapPriority() { nativeBoostUsapPriority(); } private static native void nativeBoostUsapPriority(); private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: "; /** Loading Loading @@ -865,15 +887,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 Loading
core/java/com/android/internal/os/ZygoteConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -330,7 +330,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(); Loading
core/java/com/android/internal/os/ZygoteInit.java +3 −2 Original line number Diff line number Diff line Loading @@ -822,6 +822,9 @@ public class ZygoteInit { 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(); Loading Loading @@ -881,8 +884,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 Loading
core/java/com/android/internal/os/ZygoteServer.java +206 −125 Original line number Diff line number Diff line Loading @@ -66,6 +66,15 @@ class ZygoteServer { /** The default value used for the USAP_POOL_SIZE_MIN device property */ 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 Loading Loading @@ -131,6 +140,12 @@ class ZygoteServer { */ private int mUsapPoolRefillThreshold = 0; private enum UsapPoolRefillAction { DELAYED, IMMEDIATE, NONE } ZygoteServer() { mUsapPoolEventFD = null; mZygoteSocket = null; Loading Loading @@ -293,9 +308,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 Loading @@ -303,25 +325,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; Loading @@ -332,10 +365,6 @@ class ZygoteServer { // are re-enabled in specializeAppProcess. ZygoteHooks.postForkCommon(); Log.i("zygote", "Filled the USAP pool. New USAPs: " + numUsapsToSpawn); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return null; Loading @@ -358,7 +387,7 @@ 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; Loading @@ -377,6 +406,8 @@ class ZygoteServer { socketFDs.add(mZygoteSocket.getFileDescriptor()); peers.add(null); long usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; while (true) { fetchUsapPoolPolicyPropsWithMinInterval(); Loading Loading @@ -430,12 +461,40 @@ class ZygoteServer { } } 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); } 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. usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; usapPoolRefillAction = UsapPoolRefillAction.DELAYED; } else { boolean usapPoolFDRead = false; while (--pollIndex >= 0) { Loading @@ -459,8 +518,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"); } Loading @@ -474,7 +533,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); Loading @@ -483,10 +543,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); Loading @@ -499,15 +559,16 @@ 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 { Loading @@ -516,13 +577,14 @@ class ZygoteServer { // 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. // 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 = Loading Loading @@ -554,18 +616,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 usapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE; } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) { // Delayed refill usapPoolRefillTriggerTimestamp = System.currentTimeMillis(); } } } if (usapPoolRefillAction != UsapPoolRefillAction.NONE) { int[] sessionSocketRawFDs = socketFDs.subList(1, socketFDs.size()) .stream() .mapToInt(FileDescriptor::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(); } } } Loading
core/jni/com_android_internal_os_Zygote.cpp +36 −7 Original line number Diff line number Diff line Loading @@ -169,6 +169,15 @@ static int gUsapPoolEventFD = -1; */ 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. */ Loading Loading @@ -893,7 +902,8 @@ static void ClearUsapTable() { // 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. Loading Loading @@ -926,6 +936,12 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, 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(); Loading Loading @@ -1123,6 +1139,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, 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."); } Loading Loading @@ -1360,7 +1379,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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, Loading @@ -1387,7 +1406,8 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( 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, Loading Loading @@ -1429,13 +1449,15 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( * 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); Loading @@ -1457,7 +1479,8 @@ static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, 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; Loading Loading @@ -1678,6 +1701,10 @@ static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* 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", Loading @@ -1690,7 +1717,7 @@ static const JNINativeMethod gMethods[] = { (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", Loading @@ -1708,7 +1735,9 @@ static const JNINativeMethod gMethods[] = { { "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) { Loading