psi support for low memory detection inside ActivityManagerService

Add LowMemDetector class and use it in ActivityManagerService to detect
memory conditions based on in-kernel psi monitors.

Test: boots, detects low memory
Bug: 129476847
Change-Id: I7a05ba36196d24ce5147b97c076e38b92dc71e2e
Signed-off-by: Tim Murray <timmurray@google.com>
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index e1318af..6218498 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -51,6 +51,7 @@
         "com_android_server_PersistentDataBlockService.cpp",
         "com_android_server_GraphicsStatsService.cpp",
         "com_android_server_am_AppCompactor.cpp",
+        "com_android_server_am_LowMemDetector.cpp",
         "onload.cpp",
         ":lib_networkStatsFactory_native",
     ],
@@ -104,6 +105,7 @@
         "libbpf_android",
         "libnetdbpf",
         "libnetdutils",
+        "libpsi",
         "android.hardware.audio.common@2.0",
         "android.hardware.broadcastradio@1.0",
         "android.hardware.broadcastradio@1.1",
diff --git a/services/core/jni/com_android_server_am_LowMemDetector.cpp b/services/core/jni/com_android_server_am_LowMemDetector.cpp
new file mode 100644
index 0000000..e41de4d
--- /dev/null
+++ b/services/core/jni/com_android_server_am_LowMemDetector.cpp
@@ -0,0 +1,156 @@
+/**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "LowMemDetector"
+
+#include <errno.h>
+#include <psi/psi.h>
+#include <string.h>
+#include <sys/epoll.h>
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+namespace android {
+
+enum pressure_levels {
+    PRESSURE_NONE,
+    PRESSURE_LOW,
+    PRESSURE_MEDIUM,
+    PRESSURE_HIGH,
+    PRESSURE_LEVEL_COUNT = PRESSURE_HIGH
+};
+
+// amount of stall in us for each level
+static constexpr int PSI_LOW_STALL_US = 15000;
+static constexpr int PSI_MEDIUM_STALL_US = 30000;
+static constexpr int PSI_HIGH_STALL_US = 50000;
+
+// stall tracking window size in us
+static constexpr int PSI_WINDOW_SIZE_US = 1000000;
+
+static int psi_epollfd = -1;
+
+static jint android_server_am_LowMemDetector_init(JNIEnv*, jobject) {
+    int epollfd;
+    int low_psi_fd;
+    int medium_psi_fd;
+    int high_psi_fd;
+
+    epollfd = epoll_create(PRESSURE_LEVEL_COUNT);
+    if (epollfd == -1) {
+        ALOGE("epoll_create failed: %s", strerror(errno));
+        return -1;
+    }
+
+    low_psi_fd = init_psi_monitor(PSI_SOME, PSI_LOW_STALL_US, PSI_WINDOW_SIZE_US);
+    if (low_psi_fd < 0 ||
+        register_psi_monitor(epollfd, low_psi_fd, (void*)PRESSURE_LOW) != 0) {
+        goto low_fail;
+    }
+
+    medium_psi_fd =
+        init_psi_monitor(PSI_FULL, PSI_MEDIUM_STALL_US, PSI_WINDOW_SIZE_US);
+    if (medium_psi_fd < 0 || register_psi_monitor(epollfd, medium_psi_fd,
+                                                  (void*)PRESSURE_MEDIUM) != 0) {
+        goto medium_fail;
+    }
+
+    high_psi_fd =
+        init_psi_monitor(PSI_FULL, PSI_HIGH_STALL_US, PSI_WINDOW_SIZE_US);
+    if (high_psi_fd < 0 ||
+        register_psi_monitor(epollfd, high_psi_fd, (void*)PRESSURE_HIGH) != 0) {
+        goto high_fail;
+    }
+
+    psi_epollfd = epollfd;
+    return 0;
+
+high_fail:
+    unregister_psi_monitor(epollfd, medium_psi_fd);
+medium_fail:
+    unregister_psi_monitor(epollfd, low_psi_fd);
+low_fail:
+    ALOGE("Failed to register psi trigger");
+    close(epollfd);
+    return -1;
+}
+
+static jint android_server_am_LowMemDetector_waitForPressure(JNIEnv*, jobject) {
+    static uint32_t pressure_level = PRESSURE_NONE;
+    struct epoll_event events[PRESSURE_LEVEL_COUNT];
+    int nevents = 0;
+
+    if (psi_epollfd < 0) {
+        ALOGE("Memory pressure detector is not initialized");
+        return -1;
+    }
+
+    do {
+        if (pressure_level == PRESSURE_NONE) {
+            /* Wait for events with no timeout */
+            nevents = epoll_wait(psi_epollfd, events, PRESSURE_LEVEL_COUNT, -1);
+        } else {
+            // This is simpler than lmkd. Assume that the memory pressure
+            // state will stay high for at least 1s. Within that 1s window,
+            // the memory pressure state can go up due to a different FD
+            // becoming available or it can go down when that window expires.
+            // Accordingly, there's no polling: just epoll_wait with a 1s timeout.
+            nevents = epoll_wait(psi_epollfd, events, PRESSURE_LEVEL_COUNT, 1000);
+            if (nevents == 0) {
+                pressure_level = PRESSURE_NONE;
+                return pressure_level;
+            }
+        }
+        // keep waiting if interrupted
+    } while (nevents == -1 && errno == EINTR);
+
+    if (nevents == -1) {
+        ALOGE("epoll_wait failed while waiting for psi events: %s", strerror(errno));
+        return -1;
+    }
+
+    // reset pressure_level and raise it based on received events
+    pressure_level = PRESSURE_NONE;
+    for (int i = 0; i < nevents; i++) {
+        if (events[i].events & (EPOLLERR | EPOLLHUP)) {
+            // should never happen unless psi got disabled in kernel
+            ALOGE("Memory pressure events are not available anymore");
+            return -1;
+        }
+        // record the highest reported level
+        if (events[i].data.u32 > pressure_level) {
+            pressure_level = events[i].data.u32;
+        }
+    }
+
+    return pressure_level;
+}
+
+static const JNINativeMethod sMethods[] = {
+    /* name, signature, funcPtr */
+    {"init", "()I", (void*)android_server_am_LowMemDetector_init},
+    {"waitForPressure", "()I",
+     (void*)android_server_am_LowMemDetector_waitForPressure},
+};
+
+int register_android_server_am_LowMemDetector(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/server/am/LowMemDetector",
+                                    sMethods, NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index cce96ff..efffa6c 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -56,6 +56,7 @@
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
 int register_android_server_security_VerityUtils(JNIEnv* env);
 int register_android_server_am_AppCompactor(JNIEnv* env);
+int register_android_server_am_LowMemDetector(JNIEnv* env);
 };
 
 using namespace android;
@@ -105,5 +106,6 @@
     register_android_server_net_NetworkStatsService(env);
     register_android_server_security_VerityUtils(env);
     register_android_server_am_AppCompactor(env);
+    register_android_server_am_LowMemDetector(env);
     return JNI_VERSION_1_4;
 }