Merge "Apply correct memory trim levels for processes in LRU list"
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50c992c..112814c69 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -340,6 +340,7 @@
 import com.android.server.ThreadPriorityBooster;
 import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
+import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.appop.AppOpsService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -8224,13 +8225,25 @@
     }
 
     @Override
-    public int getMemoryTrimLevel() {
+    public @MemFactor int getMemoryTrimLevel() {
         enforceNotIsolatedCaller("getMyMemoryState");
         synchronized (this) {
             return mAppProfiler.getLastMemoryLevelLocked();
         }
     }
 
+    void setMemFactorOverride(@MemFactor int level) {
+        synchronized (this) {
+            if (level == mAppProfiler.getLastMemoryLevelLocked()) {
+                return;
+            }
+
+            mAppProfiler.setMemFactorOverrideLocked(level);
+            // Kick off an oom adj update since we forced a mem factor update.
+            updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+        }
+    }
+
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 31e7106..e3c071f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -25,6 +25,12 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
+
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -92,6 +98,7 @@
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.MemInfoReader;
+import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.compat.PlatformCompat;
 
 import java.io.BufferedReader;
@@ -309,6 +316,8 @@
                     return runCompat(pw);
                 case "refresh-settings-cache":
                     return runRefreshSettingsCache();
+                case "memory-factor":
+                    return runMemoryFactor(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -3014,6 +3023,81 @@
         return -1;
     }
 
+    private int runSetMemoryFactor(PrintWriter pw) throws RemoteException {
+        final String levelArg = getNextArgRequired();
+        @MemFactor int level = ADJ_MEM_FACTOR_NOTHING;
+        switch (levelArg) {
+            case "NORMAL":
+                level = ADJ_MEM_FACTOR_NORMAL;
+                break;
+            case "MODERATE":
+                level = ADJ_MEM_FACTOR_MODERATE;
+                break;
+            case "LOW":
+                level = ADJ_MEM_FACTOR_LOW;
+                break;
+            case "CRITICAL":
+                level = ADJ_MEM_FACTOR_CRITICAL;
+                break;
+            default:
+                try {
+                    level = Integer.parseInt(levelArg);
+                } catch (NumberFormatException e) {
+                }
+                if (level < ADJ_MEM_FACTOR_NORMAL || level > ADJ_MEM_FACTOR_CRITICAL) {
+                    getErrPrintWriter().println("Error: Unknown level option: " + levelArg);
+                    return -1;
+                }
+        }
+        mInternal.setMemFactorOverride(level);
+        return 0;
+    }
+
+    private int runShowMemoryFactor(PrintWriter pw) throws RemoteException {
+        final @MemFactor int level = mInternal.getMemoryTrimLevel();
+        switch (level) {
+            case ADJ_MEM_FACTOR_NOTHING:
+                pw.println("<UNKNOWN>");
+                break;
+            case ADJ_MEM_FACTOR_NORMAL:
+                pw.println("NORMAL");
+                break;
+            case ADJ_MEM_FACTOR_MODERATE:
+                pw.println("MODERATE");
+                break;
+            case ADJ_MEM_FACTOR_LOW:
+                pw.println("LOW");
+                break;
+            case ADJ_MEM_FACTOR_CRITICAL:
+                pw.println("CRITICAL");
+                break;
+        }
+        pw.flush();
+        return 0;
+    }
+
+    private int runResetMemoryFactor(PrintWriter pw) throws RemoteException {
+        mInternal.setMemFactorOverride(ADJ_MEM_FACTOR_NOTHING);
+        return 0;
+    }
+
+    private int runMemoryFactor(PrintWriter pw) throws RemoteException {
+        mInternal.enforceCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
+                "runMemoryFactor()");
+
+        final String op = getNextArgRequired();
+        switch (op) {
+            case "set":
+                return runSetMemoryFactor(pw);
+            case "show":
+                return runShowMemoryFactor(pw);
+            case "reset":
+                return runResetMemoryFactor(pw);
+            default:
+                getErrPrintWriter().println("Error: unknown command '" + op + "'");
+                return -1;
+        }
+    }
 
     private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
@@ -3334,6 +3418,13 @@
             pw.println("            Removes all existing overrides for all changes for ");
             pw.println("            <PACKAGE_NAME> (back to default behaviour).");
             pw.println("            It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+            pw.println("  memory-factor [command] [...]: sub-commands for overriding memory pressure factor");
+            pw.println("         set <NORMAL|MODERATE|LOW|CRITICAL>");
+            pw.println("            Overrides memory pressure factor. May also supply a raw int level");
+            pw.println("         show");
+            pw.println("            Shows the existing memory pressure factor");
+            pw.println("         reset");
+            pw.println("            Removes existing override for memory pressure factor");
             pw.println();
             Intent.printIntentArgsHelp(pw, "");
         }
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 31ffb35..5e59a35 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -20,11 +20,16 @@
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.Process.FIRST_APPLICATION_UID;
 
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 
 import android.annotation.BroadcastBehavior;
@@ -72,6 +77,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.MemInfoReader;
+import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.am.ProcessList.ProcStateMemTracker;
 import com.android.server.utils.PriorityDump;
 
@@ -202,7 +208,10 @@
      * processes are going away for other reasons.
      */
     @GuardedBy("mService")
-    private int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+    private @MemFactor int mLastMemoryLevel = ADJ_MEM_FACTOR_NORMAL;
+
+    @GuardedBy("mService")
+    private @MemFactor int mMemFactorOverride = ADJ_MEM_FACTOR_NOTHING;
 
     /**
      * The last total number of process we have, to determine if changes actually look
@@ -851,7 +860,7 @@
 
     @GuardedBy("mService")
     boolean isLastMemoryLevelNormal() {
-        return mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+        return mLastMemoryLevel <= ADJ_MEM_FACTOR_NORMAL;
     }
 
     @GuardedBy("mService")
@@ -868,6 +877,11 @@
     }
 
     @GuardedBy("mService")
+    void setMemFactorOverrideLocked(@MemFactor int factor) {
+        mMemFactorOverride = factor;
+    }
+
+    @GuardedBy("mService")
     boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
         final int numOfLru = mService.mProcessList.getLruSizeLocked();
         final long now = SystemClock.uptimeMillis();
@@ -885,28 +899,32 @@
                     && numEmpty <= mService.mConstants.CUR_TRIM_EMPTY_PROCESSES) {
                 final int numCachedAndEmpty = numCached + numEmpty;
                 if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
-                    memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+                    memFactor = ADJ_MEM_FACTOR_CRITICAL;
                 } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
-                    memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
+                    memFactor = ADJ_MEM_FACTOR_LOW;
                 } else {
-                    memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+                    memFactor = ADJ_MEM_FACTOR_MODERATE;
                 }
             } else {
-                memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+                memFactor = 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.
         if (DEBUG_OOM_ADJ) {
-            Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+            Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor + " override=" + mMemFactorOverride
                     + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
                     + " numProcs=" + mService.mProcessList.getLruSizeLocked()
                     + " last=" + mLastNumProcesses);
         }
+        boolean override;
+        if (override = (mMemFactorOverride != ADJ_MEM_FACTOR_NOTHING)) {
+            memFactor = mMemFactorOverride;
+        }
         if (memFactor > mLastMemoryLevel) {
-            if (!mAllowLowerMemLevel
-                    || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
+            if (!override && (!mAllowLowerMemLevel
+                    || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses)) {
                 memFactor = mLastMemoryLevel;
                 if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
             }
@@ -924,17 +942,17 @@
                     mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);
             trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
         }
-        if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
+        if (memFactor != ADJ_MEM_FACTOR_NORMAL) {
             if (mLowRamStartTime == 0) {
                 mLowRamStartTime = now;
             }
             int step = 0;
             int fgTrimLevel;
             switch (memFactor) {
-                case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+                case ADJ_MEM_FACTOR_CRITICAL:
                     fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
                     break;
-                case ProcessStats.ADJ_MEM_FACTOR_LOW:
+                case ADJ_MEM_FACTOR_LOW:
                     fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
                     break;
                 default:
@@ -947,7 +965,7 @@
             if (mService.mAtmInternal.getPreviousProcess() != null) minFactor++;
             if (factor < minFactor) factor = minFactor;
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
-            for (int i = numOfLru - 1; i >= 0; i--) {
+            for (int i = 0; i < numOfLru; i++) {
                 ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
                     mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
@@ -1032,7 +1050,7 @@
                 mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
                 mLowRamStartTime = 0;
             }
-            for (int i = numOfLru - 1; i >= 0; i--) {
+            for (int i = 0; i < numOfLru; i++) {
                 ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
                     mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
@@ -1622,16 +1640,16 @@
     @GuardedBy("mService")
     void dumpLastMemoryLevelLocked(PrintWriter pw) {
         switch (mLastMemoryLevel) {
-            case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
+            case ADJ_MEM_FACTOR_NORMAL:
                 pw.println("normal)");
                 break;
-            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+            case ADJ_MEM_FACTOR_MODERATE:
                 pw.println("moderate)");
                 break;
-            case ProcessStats.ADJ_MEM_FACTOR_LOW:
+            case ADJ_MEM_FACTOR_LOW:
                 pw.println("low)");
                 break;
-            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+            case ADJ_MEM_FACTOR_CRITICAL:
                 pw.println("critical)");
                 break;
             default:
diff --git a/services/core/java/com/android/server/am/LowMemDetector.java b/services/core/java/com/android/server/am/LowMemDetector.java
index e82a207..8f79133 100644
--- a/services/core/java/com/android/server/am/LowMemDetector.java
+++ b/services/core/java/com/android/server/am/LowMemDetector.java
@@ -16,8 +16,19 @@
 
 package com.android.server.am;
 
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING;
+
+import android.annotation.IntDef;
+
 import com.android.internal.annotations.GuardedBy;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Detects low memory using PSI.
  *
@@ -32,13 +43,20 @@
     private final Object mPressureStateLock = new Object();
 
     @GuardedBy("mPressureStateLock")
-    private int mPressureState = MEM_PRESSURE_NONE;
+    private int mPressureState = ADJ_MEM_FACTOR_NORMAL;
+
+    public static final int ADJ_MEM_FACTOR_NOTHING = ADJ_NOTHING;
 
     /* 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;
+    @IntDef(prefix = { "ADJ_MEM_FACTOR_" }, value = {
+        ADJ_MEM_FACTOR_NOTHING,
+        ADJ_MEM_FACTOR_NORMAL,
+        ADJ_MEM_FACTOR_MODERATE,
+        ADJ_MEM_FACTOR_LOW,
+        ADJ_MEM_FACTOR_CRITICAL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MemFactor{}
 
     LowMemDetector(ActivityManagerService am) {
         mAm = am;
@@ -62,7 +80,7 @@
      * there should be conversion performed here to translate pressure state
      * into memFactor.
      */
-    public int getMemFactor() {
+    public @MemFactor int getMemFactor() {
         synchronized (mPressureStateLock) {
             return mPressureState;
         }