DO NOT MERGE: Add support to send notification from Zygote to system_server
Zygote will send message to system_server on SIGCHILD now.
Bug: 136036078
Test: atest CtsWebkitTestCases
Test: atest CtsHostsideWebViewTests
Test: atest CtsAppTestCases
Change-Id: I0ad2a3299f34344a12e2a3c153ca518aca6f6f11
Merged-In: Ifb385d907018bbe76b2aa81eb09209a77ea9224d
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fd9f025..c1c74dc 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -35,6 +35,7 @@
import android.system.Os;
import android.util.Log;
+import dalvik.annotation.optimization.FastNative;
import dalvik.system.ZygoteHooks;
import libcore.io.IoUtils;
@@ -969,4 +970,19 @@
command.append(" '").append(arg.replace("'", "'\\''")).append("'");
}
}
+
+ /**
+ * Parse the given unsolicited zygote message as type SIGCHLD,
+ * extract the payload information into the given output buffer.
+ *
+ * @param in The unsolicited zygote message to be parsed
+ * @param length The number of bytes in the message
+ * @param out The output buffer where the payload information will be placed
+ * @return Number of elements being place into output buffer, or -1 if
+ * either the message is malformed or not the type as expected here.
+ *
+ * @hide
+ */
+ @FastNative
+ public static native int nativeParseSigChld(byte[] in, int length, int[] out);
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8cff0fd..78ccba4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -63,6 +63,7 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/un.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -164,6 +165,12 @@
static int gUsapPoolEventFD = -1;
/**
+ * The socket file descriptor used to send notifications to the
+ * system_server.
+ */
+static int gSystemServerSocketFd = -1;
+
+/**
* The maximum value that the gUSAPPoolSizeMax variable may take. This value
* is a mirror of ZygoteServer.USAP_POOL_SIZE_MAX_LIMIT
*/
@@ -314,6 +321,26 @@
PROFILE_FROM_SHELL = 1 << 15,
};
+enum UnsolicitedZygoteMessageTypes : uint32_t {
+ UNSOLICITED_ZYGOTE_MESSAGE_TYPE_RESERVED = 0,
+ UNSOLICITED_ZYGOTE_MESSAGE_TYPE_SIGCHLD = 1,
+};
+
+struct UnsolicitedZygoteMessageSigChld {
+ struct {
+ UnsolicitedZygoteMessageTypes type;
+ } header;
+ struct {
+ pid_t pid;
+ uid_t uid;
+ int status;
+ } payload;
+};
+
+// Keep sync with services/core/java/com/android/server/am/ProcessList.java
+static constexpr struct sockaddr_un kSystemServerSockAddr =
+ {.sun_family = AF_LOCAL, .sun_path = "/data/system/unsolzygotesocket"};
+
// Forward declaration so we don't have to move the signal handler.
static bool RemoveUsapTableEntry(pid_t usap_pid);
@@ -323,8 +350,37 @@
env->FatalError(oss.str().c_str());
}
+// Create the socket which is going to be used to send unsolicited message
+// to system_server, the socket will be closed post forking a child process.
+// It's expected to be called at each zygote's initialization.
+static void initUnsolSocketToSystemServer() {
+ gSystemServerSocketFd = socket(AF_LOCAL, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ if (gSystemServerSocketFd >= 0) {
+ ALOGV("Zygote:systemServerSocketFD = %d", gSystemServerSocketFd);
+ } else {
+ ALOGE("Unable to create socket file descriptor to connect to system_server");
+ }
+}
+
+static void sendSigChildStatus(const pid_t pid, const uid_t uid, const int status) {
+ int socketFd = gSystemServerSocketFd;
+ if (socketFd >= 0) {
+ // fill the message buffer
+ struct UnsolicitedZygoteMessageSigChld data =
+ {.header = {.type = UNSOLICITED_ZYGOTE_MESSAGE_TYPE_SIGCHLD},
+ .payload = {.pid = pid, .uid = uid, .status = status}};
+ if (TEMP_FAILURE_RETRY(
+ sendto(socketFd, &data, sizeof(data), 0,
+ reinterpret_cast<const struct sockaddr*>(&kSystemServerSockAddr),
+ sizeof(kSystemServerSockAddr))) == -1) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Zygote failed to write to system_server FD: %s",
+ strerror(errno));
+ }
+ }
+}
// This signal handler is for zygote mode, since the zygote must reap its children
-static void SigChldHandler(int /*signal_number*/) {
+static void SigChldHandler(int /*signal_number*/, siginfo_t* info, void* /*ucontext*/) {
pid_t pid;
int status;
int64_t usaps_removed = 0;
@@ -338,6 +394,8 @@
int saved_errno = errno;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ // Notify system_server that we received a SIGCHLD
+ sendSigChildStatus(pid, info->si_uid, status);
// Log process-death status that we care about.
if (WIFEXITED(status)) {
async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
@@ -411,8 +469,7 @@
// This ends up being called repeatedly before each fork(), but there's
// no real harm in that.
static void SetSignalHandlers() {
- struct sigaction sig_chld = {};
- sig_chld.sa_handler = SigChldHandler;
+ struct sigaction sig_chld = {.sa_flags = SA_SIGINFO, .sa_sigaction = SigChldHandler};
if (sigaction(SIGCHLD, &sig_chld, nullptr) < 0) {
ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
@@ -967,6 +1024,9 @@
// Turn fdsan back on.
android_fdsan_set_error_level(fdsan_error_level);
+
+ // Reset the fd to the unsolicited zygote socket
+ gSystemServerSocketFd = -1;
} else {
ALOGD("Forked child process %d", pid);
}
@@ -1146,6 +1206,10 @@
}
}
+ if (is_child_zygote) {
+ initUnsolSocketToSystemServer();
+ }
+
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);
@@ -1391,6 +1455,11 @@
fds_to_ignore.push_back(gUsapPoolEventFD);
}
+ if (gSystemServerSocketFd != -1) {
+ fds_to_close.push_back(gSystemServerSocketFd);
+ fds_to_ignore.push_back(gSystemServerSocketFd);
+ }
+
pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
if (pid == 0) {
@@ -1416,6 +1485,11 @@
fds_to_ignore.push_back(gUsapPoolEventFD);
}
+ if (gSystemServerSocketFd != -1) {
+ fds_to_close.push_back(gSystemServerSocketFd);
+ fds_to_ignore.push_back(gSystemServerSocketFd);
+ }
+
pid_t pid = ForkCommon(env, true,
fds_to_close,
fds_to_ignore,
@@ -1483,6 +1557,9 @@
fds_to_close.push_back(gZygoteSocketFD);
fds_to_close.push_back(gUsapPoolEventFD);
fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end());
+ if (gSystemServerSocketFd != -1) {
+ fds_to_close.push_back(gSystemServerSocketFd);
+ }
fds_to_ignore.push_back(gZygoteSocketFD);
fds_to_ignore.push_back(gUsapPoolSocketFD);
@@ -1490,6 +1567,9 @@
fds_to_ignore.push_back(read_pipe_fd);
fds_to_ignore.push_back(write_pipe_fd);
fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end());
+ if (gSystemServerSocketFd != -1) {
+ fds_to_ignore.push_back(gSystemServerSocketFd);
+ }
pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore,
is_priority_fork == JNI_TRUE);
@@ -1590,6 +1670,7 @@
ALOGE("Unable to fetch USAP pool socket file descriptor");
}
+ initUnsolSocketToSystemServer();
/*
* Security Initialization
*/
@@ -1733,6 +1814,44 @@
setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
}
+static jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclass, jbyteArray in,
+ jint length, jintArray out) {
+ if (length != sizeof(struct UnsolicitedZygoteMessageSigChld)) {
+ // Apparently it's not the message we are expecting.
+ return -1;
+ }
+ if (in == nullptr || out == nullptr) {
+ // Invalid parameter
+ jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
+ return -1;
+ }
+ ScopedByteArrayRO source(env, in);
+ if (source.size() < length) {
+ // Invalid parameter
+ jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
+ return -1;
+ }
+ const struct UnsolicitedZygoteMessageSigChld* msg =
+ reinterpret_cast<const struct UnsolicitedZygoteMessageSigChld*>(source.get());
+
+ switch (msg->header.type) {
+ case UNSOLICITED_ZYGOTE_MESSAGE_TYPE_SIGCHLD: {
+ ScopedIntArrayRW buf(env, out);
+ if (buf.size() != 3) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
+ return UNSOLICITED_ZYGOTE_MESSAGE_TYPE_RESERVED;
+ }
+ buf[0] = msg->payload.pid;
+ buf[1] = msg->payload.uid;
+ buf[2] = msg->payload.status;
+ return 3;
+ }
+ default:
+ break;
+ }
+ return -1;
+}
+
static const JNINativeMethod gMethods[] = {
{ "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
@@ -1769,7 +1888,9 @@
{ "nativeUnblockSigTerm", "()V",
(void* ) com_android_internal_os_Zygote_nativeUnblockSigTerm },
{ "nativeBoostUsapPriority", "()V",
- (void* ) com_android_internal_os_Zygote_nativeBoostUsapPriority }
+ (void* ) com_android_internal_os_Zygote_nativeBoostUsapPriority },
+ {"nativeParseSigChld", "([BI[I)I",
+ (void* ) com_android_internal_os_Zygote_nativeParseSigChld},
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 766fa3b..c2652c0 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.getFreeMemory;
@@ -57,6 +58,8 @@
import android.content.pm.IPackageManager;
import android.content.res.Resources;
import android.graphics.Point;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
import android.os.AppZygote;
import android.os.Binder;
import android.os.Build;
@@ -74,6 +77,7 @@
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
+import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.EventLog;
@@ -102,6 +106,7 @@
import dalvik.system.VMRuntime;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
@@ -245,6 +250,10 @@
private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE =
"persist.device_config.runtime_native.use_app_image_startup_cache";
+ // The socket path for zygote to send unsolicited msg.
+ // Must keep sync with com_android_internal_os_Zygote.cpp.
+ private static final String UNSOL_ZYGOTE_MSG_SOCKET_PATH = "/data/system/unsolzygotesocket";
+
// Low Memory Killer Daemon command codes.
// These must be kept in sync with lmk_cmd definitions in lmkd.h
//
@@ -388,6 +397,28 @@
private PlatformCompat mPlatformCompat = null;
+ /**
+ * The server socket in system_server, zygote will connect to it
+ * in order to send unsolicited messages to system_server.
+ */
+ private LocalSocket mSystemServerSocketForZygote;
+
+ /**
+ * Maximum number of bytes that an incoming unsolicited zygote message could be.
+ * To be updated if new message type needs to be supported.
+ */
+ private static final int MAX_ZYGOTE_UNSOLICITED_MESSAGE_SIZE = 16;
+
+ /**
+ * The buffer to be used to receive the incoming unsolicited zygote message.
+ */
+ private final byte[] mZygoteUnsolicitedMessage = new byte[MAX_ZYGOTE_UNSOLICITED_MESSAGE_SIZE];
+
+ /**
+ * The buffer to be used to receive the SIGCHLD data, it includes pid/uid/status.
+ */
+ private final int[] mZygoteSigChldMessage = new int[3];
+
interface LmkdKillListener {
/**
* Called when there is a process kill by lmkd.
@@ -645,6 +676,13 @@
}
}
);
+ // Start listening on incoming connections from zygotes.
+ mSystemServerSocketForZygote = createSystemServerSocketForZygote();
+ if (mSystemServerSocketForZygote != null) {
+ sKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
+ mSystemServerSocketForZygote.getFileDescriptor(),
+ EVENT_INPUT, this::handleZygoteMessages);
+ }
}
}
@@ -3267,4 +3305,66 @@
}
}
}
+
+ private void handleZygoteSigChld(int pid, int uid, int status) {
+ // Just log it now.
+ if (DEBUG_PROCESSES) {
+ Slog.i(TAG, "Got SIGCHLD from zygote: pid=" + pid + ", uid=" + uid
+ + ", status=" + Integer.toHexString(status));
+ }
+ }
+
+ /**
+ * Create a server socket in system_server, zygote will connect to it
+ * in order to send unsolicited messages to system_server.
+ */
+ private LocalSocket createSystemServerSocketForZygote() {
+ // The file system entity for this socket is created with 0666 perms, owned
+ // by system:system. selinux restricts things so that only zygotes can
+ // access it.
+ final File socketFile = new File(UNSOL_ZYGOTE_MSG_SOCKET_PATH);
+ if (socketFile.exists()) {
+ socketFile.delete();
+ }
+
+ LocalSocket serverSocket = null;
+ try {
+ serverSocket = new LocalSocket(LocalSocket.SOCKET_DGRAM);
+ serverSocket.bind(new LocalSocketAddress(
+ UNSOL_ZYGOTE_MSG_SOCKET_PATH, LocalSocketAddress.Namespace.FILESYSTEM));
+ Os.chmod(UNSOL_ZYGOTE_MSG_SOCKET_PATH, 0666);
+ } catch (Exception e) {
+ if (serverSocket != null) {
+ try {
+ serverSocket.close();
+ } catch (IOException ex) {
+ }
+ serverSocket = null;
+ }
+ }
+ return serverSocket;
+ }
+
+ /**
+ * Handle the unsolicited message from zygote.
+ */
+ private int handleZygoteMessages(FileDescriptor fd, int events) {
+ final int eventFd = fd.getInt$();
+ if ((events & EVENT_INPUT) != 0) {
+ // An incoming message from zygote
+ try {
+ final int len = Os.read(fd, mZygoteUnsolicitedMessage, 0,
+ mZygoteUnsolicitedMessage.length);
+ if (len > 0 && mZygoteSigChldMessage.length == Zygote.nativeParseSigChld(
+ mZygoteUnsolicitedMessage, len, mZygoteSigChldMessage)) {
+ handleZygoteSigChld(mZygoteSigChldMessage[0] /* pid */,
+ mZygoteSigChldMessage[1] /* uid */,
+ mZygoteSigChldMessage[2] /* status */);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception in reading unsolicited zygote message: " + e);
+ }
+ }
+ return EVENT_INPUT;
+ }
}