diff options
| -rw-r--r-- | core/java/android/os/ZygoteProcess.java | 2 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/Zygote.java | 27 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteServer.java | 128 | ||||
| -rw-r--r-- | core/jni/com_android_internal_os_Zygote.cpp | 37 |
4 files changed, 137 insertions, 57 deletions
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index de378b0cd1bf..06fd314b47a1 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -444,7 +444,7 @@ public class ZygoteProcess { // If there was an IOException using the blastula pool we will log the error and // attempt to start the process through the Zygote. Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - " - + ex.toString()); + + ex.getMessage()); } finally { try { blastulaSessionSocket.close(); diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 0604ab2f7237..0dd8a1aba41c 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -427,6 +427,12 @@ public final class Zygote { defaultValue); } + protected static void emptyBlastulaPool() { + nativeEmptyBlastulaPool(); + } + + private static native void nativeEmptyBlastulaPool(); + /** * Returns the value of a system property converted to a boolean using specific logic. * @@ -520,7 +526,7 @@ public final class Zygote { LocalSocket sessionSocket = null; DataOutputStream blastulaOutputStream = null; Credentials peerCredentials = null; - String[] argStrings = null; + ZygoteArguments args = null; while (true) { try { @@ -533,25 +539,24 @@ public final class Zygote { peerCredentials = sessionSocket.getPeerCredentials(); - argStrings = readArgumentList(blastulaReader); + String[] argStrings = readArgumentList(blastulaReader); if (argStrings != null) { + args = new ZygoteArguments(argStrings); + + // TODO (chriswailes): Should this only be run for debug builds? + validateBlastulaCommand(args); break; } else { Log.e("Blastula", "Truncated command received."); IoUtils.closeQuietly(sessionSocket); } - } catch (IOException ioEx) { - Log.e("Blastula", "Failed to read command: " + ioEx.getMessage()); + } catch (Exception ex) { + Log.e("Blastula", ex.getMessage()); IoUtils.closeQuietly(sessionSocket); } } - ZygoteArguments args = new ZygoteArguments(argStrings); - - // TODO (chriswailes): Should this only be run for debug builds? - validateBlastulaCommand(args); - applyUidSecurityPolicy(args, peerCredentials); applyDebuggerSystemProperty(args); @@ -740,8 +745,8 @@ public final class Zygote { if (args.mInvokeWith != null && peerUid != 0 && (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) { - throw new ZygoteSecurityException("Peer is permitted to specify an" - + "explicit invoke-with wrapper command only for debuggable" + throw new ZygoteSecurityException("Peer is permitted to specify an " + + "explicit invoke-with wrapper command only for debuggable " + "applications."); } } diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 2c17540eb6c6..11c3ea25a1da 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -68,6 +68,15 @@ class ZygoteServer { private static final String BLASTULA_POOL_SIZE_MIN_DEFAULT = "1"; /** + * Indicates if this Zygote server can support a blastula pool. Currently this should only be + * true for the primary and secondary Zygotes, and not the App Zygotes or the WebView Zygote. + * + * TODO (chriswailes): Make this an explicit argument to the constructor + */ + + private final boolean mBlastulaPoolSupported; + + /** * If the blastula pool should be created and used to start applications. * * Setting this value to false will disable the creation, maintenance, and use of the blastula @@ -127,6 +136,8 @@ class ZygoteServer { mBlastulaPoolEventFD = null; mZygoteSocket = null; mBlastulaPoolSocket = null; + + mBlastulaPoolSupported = false; } /** @@ -151,12 +162,18 @@ class ZygoteServer { } fetchBlastulaPoolPolicyProps(); + + mBlastulaPoolSupported = true; } void setForkChild() { mIsForkChild = true; } + public boolean isBlastulaPoolEnabled() { + return mBlastulaPoolEnabled; + } + /** * Registers a server socket for zygote command connections. This opens the server socket * at the specified name in the abstract socket namespace. @@ -282,40 +299,39 @@ class ZygoteServer { * this function will return a Runnable object representing the new application that is * passed up from blastulaMain. */ - private Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { - if (mBlastulaPoolEnabled) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); - - int blastulaPoolCount = Zygote.getBlastulaPoolCount(); - int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount; - if (blastulaPoolCount < mBlastulaPoolSizeMin - || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) { + Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); - // Disable some VM functionality and reset some system values - // before forking. - ZygoteHooks.preFork(); - Zygote.resetNicePriority(); + int blastulaPoolCount = Zygote.getBlastulaPoolCount(); + int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount; - while (blastulaPoolCount++ < mBlastulaPoolSizeMax) { - Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs); + if (blastulaPoolCount < mBlastulaPoolSizeMin + || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) { - if (caller != null) { - return caller; - } - } + // Disable some VM functionality and reset some system values + // before forking. + ZygoteHooks.preFork(); + Zygote.resetNicePriority(); - // Re-enable runtime services for the Zygote. Blastula services - // are re-enabled in specializeBlastula. - ZygoteHooks.postForkCommon(); + while (blastulaPoolCount++ < mBlastulaPoolSizeMax) { + Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs); - Log.i("zygote", - "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); + if (caller != null) { + return caller; + } } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + // Re-enable runtime services for the Zygote. Blastula services + // are re-enabled in specializeBlastula. + ZygoteHooks.postForkCommon(); + + Log.i("zygote", + "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + return null; } @@ -334,12 +350,26 @@ class ZygoteServer { while (true) { fetchBlastulaPoolPolicyPropsWithMinInterval(); - int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs(); + int[] blastulaPipeFDs = null; + StructPollfd[] pollFDs = null; + + // Allocate enough space for the poll structs, taking into account + // the state of the blastula pool for this Zygote (could be a + // regular Zygote, a WebView Zygote, or an AppZygote). + if (mBlastulaPoolEnabled) { + blastulaPipeFDs = Zygote.getBlastulaPipeFDs(); + pollFDs = new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length]; + } else { + pollFDs = new StructPollfd[socketFDs.size()]; + } - // Space for all of the socket FDs, the Blastula Pool Event FD, and - // all of the open blastula read pipe FDs. - StructPollfd[] pollFDs = - new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length]; + /* + * For reasons of correctness the blastula pool pipe and event FDs + * must be processed before the session and server sockets. This + * is to ensure that the blastula pool accounting information is + * accurate when handling other requests like API blacklist + * exemptions. + */ int pollIndex = 0; for (FileDescriptor socketFD : socketFDs) { @@ -350,19 +380,22 @@ class ZygoteServer { } final int blastulaPoolEventFDIndex = pollIndex; - pollFDs[pollIndex] = new StructPollfd(); - pollFDs[pollIndex].fd = mBlastulaPoolEventFD; - pollFDs[pollIndex].events = (short) POLLIN; - ++pollIndex; - - for (int blastulaPipeFD : blastulaPipeFDs) { - FileDescriptor managedFd = new FileDescriptor(); - managedFd.setInt$(blastulaPipeFD); + if (mBlastulaPoolEnabled) { pollFDs[pollIndex] = new StructPollfd(); - pollFDs[pollIndex].fd = managedFd; + pollFDs[pollIndex].fd = mBlastulaPoolEventFD; pollFDs[pollIndex].events = (short) POLLIN; ++pollIndex; + + for (int blastulaPipeFD : blastulaPipeFDs) { + FileDescriptor managedFd = new FileDescriptor(); + managedFd.setInt$(blastulaPipeFD); + + pollFDs[pollIndex] = new StructPollfd(); + pollFDs[pollIndex].fd = managedFd; + pollFDs[pollIndex].events = (short) POLLIN; + ++pollIndex; + } } try { @@ -371,6 +404,8 @@ class ZygoteServer { throw new RuntimeException("poll failed", ex); } + boolean blastulaPoolFDRead = false; + while (--pollIndex >= 0) { if ((pollFDs[pollIndex].revents & POLLIN) == 0) { continue; @@ -480,17 +515,22 @@ class ZygoteServer { Zygote.removeBlastulaTableEntry((int) messagePayload); } - int[] sessionSocketRawFDs = - socketFDs.subList(1, socketFDs.size()) + blastulaPoolFDRead = true; + } + } + + // Check to see if the blastula pool needs to be refilled. + if (blastulaPoolFDRead) { + int[] sessionSocketRawFDs = + socketFDs.subList(1, socketFDs.size()) .stream() .mapToInt(fd -> fd.getInt$()) .toArray(); - final Runnable command = fillBlastulaPool(sessionSocketRawFDs); + final Runnable command = fillBlastulaPool(sessionSocketRawFDs); - if (command != null) { - return command; - } + if (command != null) { + return command; } } } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 4c5b2849ec27..4aa0bc8757c2 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -216,6 +216,10 @@ class BlastulaTableEntry { } } + void Clear() { + mStorage.store(INVALID_ENTRY_VALUE); + } + /** * @return A copy of the data stored in this entry. */ @@ -1159,6 +1163,14 @@ static void UnblockSignal(int signum, fail_fn_t fail_fn) { } } +static void ClearBlastulaTable() { + for (BlastulaTableEntry& entry : gBlastulaTable) { + entry.Clear(); + } + + gBlastulaPoolCount = 0; +} + // 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, @@ -1201,6 +1213,9 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, // Clean up any descriptors which must be closed immediately DetachDescriptors(env, fds_to_close, fail_fn); + // Invalidate the entries in the blastula table. + ClearBlastulaTable(); + // Re-open all remaining open file descriptors so that they aren't shared // with the zygote across a fork. gOpenFdTable->ReopenOrDetach(fail_fn); @@ -1887,6 +1902,24 @@ static jint com_android_internal_os_Zygote_nativeGetBlastulaPoolCount(JNIEnv* en return gBlastulaPoolCount; } +/** + * Kills all processes currently in the blastula pool. + * + * @param env Managed runtime environment + * @return The number of blastulas currently in the blastula pool + */ +static void com_android_internal_os_Zygote_nativeEmptyBlastulaPool(JNIEnv* env, jclass) { + for (auto& entry : gBlastulaTable) { + auto entry_storage = entry.GetValues(); + + if (entry_storage.has_value()) { + kill(entry_storage.value().pid, SIGKILL); + entry.Clear(); + --gBlastulaPoolCount; + } + } +} + static const JNINativeMethod gMethods[] = { { "nativeSecurityInit", "()V", (void *) com_android_internal_os_Zygote_nativeSecurityInit }, @@ -1917,7 +1950,9 @@ static const JNINativeMethod gMethods[] = { { "nativeGetBlastulaPoolEventFD", "()I", (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD }, { "nativeGetBlastulaPoolCount", "()I", - (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount } + (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount }, + { "nativeEmptyBlastulaPool", "()V", + (void *) com_android_internal_os_Zygote_nativeEmptyBlastulaPool } }; int register_com_android_internal_os_Zygote(JNIEnv* env) { |