diff options
| -rw-r--r-- | core/java/android/os/Process.java | 101 | ||||
| -rw-r--r-- | core/jni/android_util_Process.cpp | 106 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ProcessList.java | 48 |
3 files changed, 179 insertions, 76 deletions
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 5d80ab6453cc..c7a8474c8038 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -20,14 +20,20 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; +import android.system.StructPollfd; import android.util.Pair; import android.webkit.WebViewZygote; import dalvik.system.VMRuntime; +import libcore.io.IoUtils; + +import java.io.FileDescriptor; import java.util.Map; +import java.util.concurrent.TimeoutException; /** * Tools for managing OS processes. @@ -487,6 +493,15 @@ public class Process { private static long sStartElapsedRealtime; private static long sStartUptimeMillis; + private static final int PIDFD_UNKNOWN = 0; + private static final int PIDFD_SUPPORTED = 1; + private static final int PIDFD_UNSUPPORTED = 2; + + /** + * Whether or not the underlying OS supports pidfd + */ + private static int sPidFdSupported = PIDFD_UNKNOWN; + /** * State associated with the zygote process. * @hide @@ -1189,4 +1204,90 @@ public class Process { } } + + /** + * Wait for the death of the given process. + * + * @param pid The process ID to be waited on + * @param timeout The maximum time to wait in milliseconds, or -1 to wait forever + * @hide + */ + public static void waitForProcessDeath(int pid, int timeout) + throws InterruptedException, TimeoutException { + FileDescriptor pidfd = null; + if (sPidFdSupported == PIDFD_UNKNOWN) { + int fd = -1; + try { + fd = nativePidFdOpen(pid, 0); + sPidFdSupported = PIDFD_SUPPORTED; + } catch (ErrnoException e) { + sPidFdSupported = e.errno != OsConstants.ENOSYS + ? PIDFD_SUPPORTED : PIDFD_UNSUPPORTED; + } finally { + if (fd >= 0) { + pidfd = new FileDescriptor(); + pidfd.setInt$(fd); + } + } + } + boolean fallback = sPidFdSupported == PIDFD_UNSUPPORTED; + if (!fallback) { + try { + if (pidfd == null) { + int fd = nativePidFdOpen(pid, 0); + if (fd >= 0) { + pidfd = new FileDescriptor(); + pidfd.setInt$(fd); + } else { + fallback = true; + } + } + if (pidfd != null) { + StructPollfd[] fds = new StructPollfd[] { + new StructPollfd() + }; + fds[0].fd = pidfd; + fds[0].events = (short) OsConstants.POLLIN; + fds[0].revents = 0; + fds[0].userData = null; + int res = Os.poll(fds, timeout); + if (res > 0) { + return; + } else if (res == 0) { + throw new TimeoutException(); + } else { + // We should get an ErrnoException now + } + } + } catch (ErrnoException e) { + if (e.errno == OsConstants.EINTR) { + throw new InterruptedException(); + } + fallback = true; + } finally { + if (pidfd != null) { + IoUtils.closeQuietly(pidfd); + } + } + } + if (fallback) { + boolean infinity = timeout < 0; + long now = System.currentTimeMillis(); + final long end = now + timeout; + while (infinity || now < end) { + try { + Os.kill(pid, 0); + } catch (ErrnoException e) { + if (e.errno == OsConstants.ESRCH) { + return; + } + } + Thread.sleep(1); + now = System.currentTimeMillis(); + } + } + throw new TimeoutException(); + } + + private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException; } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index ee6e8c487bad..89dbca816d61 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -50,11 +50,14 @@ #include <pwd.h> #include <signal.h> #include <string.h> +#include <sys/epoll.h> #include <sys/errno.h> #include <sys/resource.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <sys/sysinfo.h> #include <sys/types.h> +#include <time.h> #include <unistd.h> #define GUARD_THREAD_PRIORITY 0 @@ -1271,39 +1274,78 @@ void android_os_Process_removeAllProcessGroups(JNIEnv* env, jobject clazz) return removeAllProcessGroups(); } +static void throwErrnoException(JNIEnv* env, const char* functionName, int error) { + ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName)); + if (detailMessage.get() == NULL) { + // Not really much we can do here. We're probably dead in the water, + // but let's try to stumble on... + env->ExceptionClear(); + } + static jclass errnoExceptionClass = + MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException")); + + static jmethodID errnoExceptionCtor = + GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V"); + + jobject exception = + env->NewObject(errnoExceptionClass, errnoExceptionCtor, detailMessage.get(), error); + env->Throw(reinterpret_cast<jthrowable>(exception)); +} + +// Wrapper function to the syscall pidfd_open, which creates a file +// descriptor that refers to the process whose PID is specified in pid. +static inline int sys_pidfd_open(pid_t pid, unsigned int flags) { + return syscall(__NR_pidfd_open, pid, flags); +} + +static jboolean android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) { + int fd = sys_pidfd_open(pid, flags); + if (fd < 0) { + throwErrnoException(env, "nativePidFdOpen", errno); + return -1; + } + return fd; +} + static const JNINativeMethod methods[] = { - {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName}, - {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName}, - {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority}, - {"setThreadScheduler", "(III)V", (void*)android_os_Process_setThreadScheduler}, - {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground}, - {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, - {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, - {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler}, - {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, - {"setThreadGroupAndCpuset", "(II)V", (void*)android_os_Process_setThreadGroupAndCpuset}, - {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, - {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, - {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores}, - {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness}, - {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, - {"setUid", "(I)I", (void*)android_os_Process_setUid}, - {"setGid", "(I)I", (void*)android_os_Process_setGid}, - {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, - {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, - {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, - {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory}, - {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, - {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, - {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile}, - {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine}, - {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, - {"getPss", "(I)J", (void*)android_os_Process_getPss}, - {"getRss", "(I)[J", (void*)android_os_Process_getRss}, - {"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands}, - //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject}, - {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup}, - {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups}, + {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName}, + {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName}, + {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority}, + {"setThreadScheduler", "(III)V", (void*)android_os_Process_setThreadScheduler}, + {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground}, + {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, + {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, + {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler}, + {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, + {"setThreadGroupAndCpuset", "(II)V", (void*)android_os_Process_setThreadGroupAndCpuset}, + {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, + {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, + {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores}, + {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness}, + {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, + {"setUid", "(I)I", (void*)android_os_Process_setUid}, + {"setGid", "(I)I", (void*)android_os_Process_setGid}, + {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, + {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, + {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, + {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory}, + {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", + (void*)android_os_Process_readProcLines}, + {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, + {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", + (void*)android_os_Process_readProcFile}, + {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", + (void*)android_os_Process_parseProcLine}, + {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, + {"getPss", "(I)J", (void*)android_os_Process_getPss}, + {"getRss", "(I)[J", (void*)android_os_Process_getRss}, + {"getPidsForCommands", "([Ljava/lang/String;)[I", + (void*)android_os_Process_getPidsForCommands}, + //{"setApplicationObject", "(Landroid/os/IBinder;)V", + //(void*)android_os_Process_setApplicationObject}, + {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup}, + {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups}, + {"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen}, }; int register_android_os_Process(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 7f9477ed5c9b..4a8984524bb7 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -91,9 +91,7 @@ import android.os.UserHandle; import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.provider.DeviceConfig; -import android.system.ErrnoException; import android.system.Os; -import android.system.OsConstants; import android.text.TextUtils; import android.util.ArrayMap; import android.util.EventLog; @@ -327,12 +325,7 @@ public final class ProcessList { /** * How long between a process kill and we actually receive its death recipient */ - private static final long PROC_KILL_TIMEOUT = 2000; // 2 seconds; - - /** - * How long between polls to check if the given process is dead or not. - */ - private static final long PROC_DEATH_POLL_INTERVAL = 100; + private static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds; ActivityManagerService mService = null; @@ -2169,28 +2162,6 @@ public final class ProcessList { } /** - * A lite version of checking if a process is alive or not, by using kill(2) with signal 0. - * - * <p> - * Note that, zombie processes are stil "alive" in this case, use the {@link - * ActivityManagerService#isProcessAliveLocked} if zombie processes need to be excluded. - * </p> - */ - @GuardedBy("mService") - private boolean isProcessAliveLiteLocked(ProcessRecord app) { - // If somehow the pid is invalid, let's think it's dead. - if (app.pid <= 0) { - return false; - } - try { - Os.kill(app.pid, 0); - } catch (ErrnoException e) { - return e.errno != OsConstants.ESRCH; - } - return true; - } - - /** * Kill (if asked to) and wait for the given process died if necessary * @param app - The process record to kill * @param doKill - Kill the given process record @@ -2214,20 +2185,9 @@ public final class ProcessList { // wait for the death if (wait) { - boolean isAlive = true; - // ideally we should use pidfd_open(2) but it's available on kernel 5.3 or later - - final long timeout = SystemClock.uptimeMillis() + PROC_KILL_TIMEOUT; - isAlive = isProcessAliveLiteLocked(app); - while (timeout > SystemClock.uptimeMillis() && isAlive) { - try { - Thread.sleep(PROC_DEATH_POLL_INTERVAL); - } catch (InterruptedException e) { - } - isAlive = isProcessAliveLiteLocked(app); - } - - if (isAlive) { + try { + Process.waitForProcessDeath(app.pid, PROC_KILL_TIMEOUT); + } catch (Exception e) { // Maybe the process goes into zombie, use an expensive API to check again. if (mService.isProcessAliveLocked(app)) { Slog.w(TAG, String.format(formatString, |