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

Commit 9f21e892 authored by Jing Ji's avatar Jing Ji
Browse files

Add an API android.os.Process.waitForProcessDeath

And use this API to wait for the death of a process from
the system_server side.

Bug: 145838141
Test: Manual
Change-Id: I594843a383c3375d967e608e0c6f8d36fa2e78a3
parent 7d8deb56
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,