summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/ZygoteProcess.java2
-rw-r--r--core/java/com/android/internal/os/Zygote.java27
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java128
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp37
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) {