diff options
| -rw-r--r-- | core/java/com/android/internal/os/Zygote.java | 16 | ||||
| -rw-r--r-- | core/jni/com_android_internal_os_Zygote.cpp | 129 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ProcessList.java | 100 |
3 files changed, 241 insertions, 4 deletions
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index fd9f025ce9ba..c1c74dcfb9e6 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.ErrnoException; 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 @@ public final class Zygote { 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 8cff0fde11f7..78ccba4c29dc 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 std::atomic_uint32_t gUsapPoolCount = 0; 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 @@ enum RuntimeFlags : uint32_t { 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 @@ static void RuntimeAbort(JNIEnv* env, int line, const char* msg) { 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 @@ static void SigChldHandler(int /*signal_number*/) { 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 @@ static void SigChldHandler(int /*signal_number*/) { // 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 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, // 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } } + if (is_child_zygote) { + initUnsolSocketToSystemServer(); + } + env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags, is_system_server, is_child_zygote, managed_instruction_set); @@ -1391,6 +1455,11 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( 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 @@ static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, 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 @@ static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, 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 @@ static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jc ALOGE("Unable to fetch USAP pool socket file descriptor"); } + initUnsolSocketToSystemServer(); /* * Security Initialization */ @@ -1733,6 +1814,44 @@ static void com_android_internal_os_Zygote_nativeBoostUsapPriority(JNIEnv* env, 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 @@ static const JNINativeMethod gMethods[] = { { "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 766fa3ba55c1..c2652c06e5a9 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_CACHED_ACTIVITY; 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.ApplicationInfo; 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.Trace; 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 com.android.server.wm.WindowManagerService; 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } } ); + // 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 @@ public final class ProcessList { } } } + + 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; + } } |