Loading services/core/java/com/android/server/am/ActivityManagerService.java +22 −15 Original line number Diff line number Diff line Loading @@ -544,6 +544,7 @@ public class ActivityManagerService extends IActivityManager.Stub private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds; final OomAdjuster mOomAdjuster; final LowMemDetector mLowMemDetector; /** All system services */ SystemServiceManager mSystemServiceManager; Loading Loading @@ -2294,6 +2295,7 @@ public class ActivityManagerService extends IActivityManager.Stub ? new ActivityManagerConstants(mContext, this, mHandler) : null; final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); mProcessList.init(this, activeUids); mLowMemDetector = null; mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); mIntentFirewall = hasHandlerThread Loading Loading @@ -2342,6 +2344,7 @@ public class ActivityManagerService extends IActivityManager.Stub mConstants = new ActivityManagerConstants(mContext, this, mHandler); final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */); mProcessList.init(this, activeUids); mLowMemDetector = new LowMemDetector(this); mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); // Broadcast policy parameters Loading Loading @@ -16524,16 +16527,19 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) { final int N = mProcessList.getLruSizeLocked(); final long now = SystemClock.uptimeMillis(); int memFactor; if (mLowMemDetector != null && mLowMemDetector.isAvailable()) { memFactor = mLowMemDetector.getMemFactor(); } else { // Now determine the memory trimming level of background processes. // Unfortunately we need to start at the back of the list to do this // properly. We only do this if the number of background apps we // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. final int numCachedAndEmpty = numCached + numEmpty; int memFactor; if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) { final int numCachedAndEmpty = numCached + numEmpty; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { Loading @@ -16544,6 +16550,7 @@ public class ActivityManagerService extends IActivityManager.Stub } else { memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL; } } // We always allow the memory level to go up (better). We only allow it to go // down if we are in a state where that is allowed, *and* the total number of processes // has gone down since last time. services/core/java/com/android/server/am/LowMemDetector.java 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * Copyright (C) 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. */ package com.android.server.am; import com.android.internal.annotations.GuardedBy; /** * Detects low memory using PSI. * * If the kernel doesn't support PSI, then this class is not available. */ public final class LowMemDetector { private static final String TAG = "LowMemDetector"; private final ActivityManagerService mAm; private final LowMemThread mLowMemThread; private boolean mAvailable; private final Object mPressureStateLock = new Object(); @GuardedBy("mPressureStateLock") private int mPressureState = MEM_PRESSURE_NONE; /* getPressureState return values */ public static final int MEM_PRESSURE_NONE = 0; public static final int MEM_PRESSURE_LOW = 1; public static final int MEM_PRESSURE_MEDIUM = 2; public static final int MEM_PRESSURE_HIGH = 3; LowMemDetector(ActivityManagerService am) { mAm = am; mLowMemThread = new LowMemThread(); if (init() != 0) { mAvailable = false; } else { mAvailable = true; mLowMemThread.start(); } } public boolean isAvailable() { return mAvailable; } /** * Returns the current mem factor. * Note that getMemFactor returns LowMemDetector.MEM_PRESSURE_XXX * which match ProcessStats.ADJ_MEM_FACTOR_XXX values. If they deviate * there should be conversion performed here to translate pressure state * into memFactor. */ public int getMemFactor() { synchronized (mPressureStateLock) { return mPressureState; } } private native int init(); private native int waitForPressure(); private final class LowMemThread extends Thread { public void run() { while (true) { // sleep waiting for a PSI event int newPressureState = waitForPressure(); if (newPressureState == -1) { // epoll broke, tear this down mAvailable = false; break; } // got a PSI event? let's update lowmem info synchronized (mPressureStateLock) { mPressureState = newPressureState; } } } } } services/core/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ cc_library_static { "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", ], Loading Loading @@ -104,6 +105,7 @@ cc_defaults { "libbpf_android", "libnetdbpf", "libnetdutils", "libpsi", "android.hardware.audio.common@2.0", "android.hardware.broadcastradio@1.0", "android.hardware.broadcastradio@1.1", Loading services/core/jni/com_android_server_am_LowMemDetector.cpp 0 → 100644 +156 −0 Original line number Diff line number Diff line /** ** 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 services/core/jni/onload.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ int register_android_server_net_NetworkStatsFactory(JNIEnv* env); 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; Loading Loading @@ -105,5 +106,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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; } Loading
services/core/java/com/android/server/am/ActivityManagerService.java +22 −15 Original line number Diff line number Diff line Loading @@ -544,6 +544,7 @@ public class ActivityManagerService extends IActivityManager.Stub private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds; final OomAdjuster mOomAdjuster; final LowMemDetector mLowMemDetector; /** All system services */ SystemServiceManager mSystemServiceManager; Loading Loading @@ -2294,6 +2295,7 @@ public class ActivityManagerService extends IActivityManager.Stub ? new ActivityManagerConstants(mContext, this, mHandler) : null; final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); mProcessList.init(this, activeUids); mLowMemDetector = null; mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); mIntentFirewall = hasHandlerThread Loading Loading @@ -2342,6 +2344,7 @@ public class ActivityManagerService extends IActivityManager.Stub mConstants = new ActivityManagerConstants(mContext, this, mHandler); final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */); mProcessList.init(this, activeUids); mLowMemDetector = new LowMemDetector(this); mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); // Broadcast policy parameters Loading Loading @@ -16524,16 +16527,19 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) { final int N = mProcessList.getLruSizeLocked(); final long now = SystemClock.uptimeMillis(); int memFactor; if (mLowMemDetector != null && mLowMemDetector.isAvailable()) { memFactor = mLowMemDetector.getMemFactor(); } else { // Now determine the memory trimming level of background processes. // Unfortunately we need to start at the back of the list to do this // properly. We only do this if the number of background apps we // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. final int numCachedAndEmpty = numCached + numEmpty; int memFactor; if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) { final int numCachedAndEmpty = numCached + numEmpty; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { Loading @@ -16544,6 +16550,7 @@ public class ActivityManagerService extends IActivityManager.Stub } else { memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL; } } // We always allow the memory level to go up (better). We only allow it to go // down if we are in a state where that is allowed, *and* the total number of processes // has gone down since last time.
services/core/java/com/android/server/am/LowMemDetector.java 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * Copyright (C) 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. */ package com.android.server.am; import com.android.internal.annotations.GuardedBy; /** * Detects low memory using PSI. * * If the kernel doesn't support PSI, then this class is not available. */ public final class LowMemDetector { private static final String TAG = "LowMemDetector"; private final ActivityManagerService mAm; private final LowMemThread mLowMemThread; private boolean mAvailable; private final Object mPressureStateLock = new Object(); @GuardedBy("mPressureStateLock") private int mPressureState = MEM_PRESSURE_NONE; /* getPressureState return values */ public static final int MEM_PRESSURE_NONE = 0; public static final int MEM_PRESSURE_LOW = 1; public static final int MEM_PRESSURE_MEDIUM = 2; public static final int MEM_PRESSURE_HIGH = 3; LowMemDetector(ActivityManagerService am) { mAm = am; mLowMemThread = new LowMemThread(); if (init() != 0) { mAvailable = false; } else { mAvailable = true; mLowMemThread.start(); } } public boolean isAvailable() { return mAvailable; } /** * Returns the current mem factor. * Note that getMemFactor returns LowMemDetector.MEM_PRESSURE_XXX * which match ProcessStats.ADJ_MEM_FACTOR_XXX values. If they deviate * there should be conversion performed here to translate pressure state * into memFactor. */ public int getMemFactor() { synchronized (mPressureStateLock) { return mPressureState; } } private native int init(); private native int waitForPressure(); private final class LowMemThread extends Thread { public void run() { while (true) { // sleep waiting for a PSI event int newPressureState = waitForPressure(); if (newPressureState == -1) { // epoll broke, tear this down mAvailable = false; break; } // got a PSI event? let's update lowmem info synchronized (mPressureStateLock) { mPressureState = newPressureState; } } } } }
services/core/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ cc_library_static { "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", ], Loading Loading @@ -104,6 +105,7 @@ cc_defaults { "libbpf_android", "libnetdbpf", "libnetdutils", "libpsi", "android.hardware.audio.common@2.0", "android.hardware.broadcastradio@1.0", "android.hardware.broadcastradio@1.1", Loading
services/core/jni/com_android_server_am_LowMemDetector.cpp 0 → 100644 +156 −0 Original line number Diff line number Diff line /** ** 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
services/core/jni/onload.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ int register_android_server_net_NetworkStatsFactory(JNIEnv* env); 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; Loading Loading @@ -105,5 +106,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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; }