Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3dd4c345 authored by Jing Ji's avatar Jing Ji Committed by Android (Google) Code Review
Browse files

Merge "Add an API android.os.Process.waitForProcessDeath"

parents 1d1973ef 9f21e892
Loading
Loading
Loading
Loading
+101 −0
Original line number Diff line number Diff line
@@ -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;
}
+74 −32
Original line number Diff line number Diff line
@@ -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,6 +1274,39 @@ 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},
@@ -1293,17 +1329,23 @@ static const JNINativeMethod methods[] = {
        {"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},
        {"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},
        {"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},
        {"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)
+4 −44
Original line number Diff line number Diff line
@@ -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;

@@ -2168,28 +2161,6 @@ public final class ProcessList {
        return success ? app : null;
    }

    /**
     * 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
@@ -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) {
                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,