Use Process.getRss instead of mProfile.getLastRss in CacheOomRanker.

RSS values are cached in ProcessStateRecord, and repopulated when they
are >10s old.

mProfile.getLastRss values can be 15s-2h stale, which is why we're using
this implementation. My understanding is that the best solution would be
to populate mProfile.getLastRss more often - but it is currently
populated alongside PSS, which is more expensive to calculate.
Therefore, rather than the more complex change of splitting up PSS & RSS
collection, we opt for this simple (if not optimal) approach as
CacheOomRanker is still experimental. Upon successful launch, we can
revisit this.

When logging the duration of CacheOomRanker calculations, we saw no
significant increase in computation time with this change. If we turn
off caching RSS values, we see a 10x increase in computation time.

Test: atest CacheOomRankerTest
Bug: 196031723
Change-Id: I8e80db2aab338df07f07842131a9d4b55bebd054
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index bd7ee01..7413808 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import android.os.Process;
+import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.util.Slog;
 
@@ -46,6 +48,14 @@
     @VisibleForTesting
     static final int DEFAULT_PRESERVE_TOP_N_APPS = 3;
     @VisibleForTesting
+    static final String KEY_OOM_RE_RANKING_USE_FREQUENT_RSS = "oom_re_ranking_rss_use_frequent_rss";
+    @VisibleForTesting
+    static final boolean DEFAULT_USE_FREQUENT_RSS = true;
+    @VisibleForTesting
+    static final String KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS = "oom_re_ranking_rss_update_rate_ms";
+    @VisibleForTesting
+    static final long DEFAULT_RSS_UPDATE_RATE_MS = 10_000; // 10 seconds
+    @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
     @VisibleForTesting
     static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
@@ -62,6 +72,8 @@
             new ScoreComparator();
     private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
             new CacheUseComparator();
+    private static final Comparator<RankedProcessRecord> RSS_COMPARATOR =
+            new RssComparator();
     private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
             new LastRssComparator();
     private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
@@ -70,6 +82,7 @@
     private final Object mPhenotypeFlagLock = new Object();
 
     private final ActivityManagerService mService;
+    private final ProcessDependencies mProcessDependencies;
     private final ActivityManagerGlobalLock mProcLock;
     private final Object mProfilerLock;
 
@@ -78,6 +91,12 @@
     @GuardedBy("mPhenotypeFlagLock")
     @VisibleForTesting
     int mPreserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting
+    boolean mUseFrequentRss = DEFAULT_USE_FREQUENT_RSS;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting
+    long mRssUpdateRateMs = DEFAULT_RSS_UPDATE_RATE_MS;
     // Weight to apply to the LRU ordering.
     @GuardedBy("mPhenotypeFlagLock")
     @VisibleForTesting
@@ -110,6 +129,10 @@
                                 updateNumberToReRank();
                             } else if (KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS.equals(name)) {
                                 updatePreserveTopNApps();
+                            } else if (KEY_OOM_RE_RANKING_USE_FREQUENT_RSS.equals(name)) {
+                                updateUseFrequentRss();
+                            } else if (KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS.equals(name)) {
+                                updateRssUpdateRateMs();
                             } else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
                                 updateLruWeight();
                             } else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
@@ -123,9 +146,15 @@
             };
 
     CacheOomRanker(final ActivityManagerService service) {
+        this(service, new ProcessDependenciesImpl());
+    }
+
+    @VisibleForTesting
+    CacheOomRanker(final ActivityManagerService service, ProcessDependencies processDependencies) {
         mService = service;
         mProcLock = service.mProcLock;
         mProfilerLock = service.mAppProfiler.mProfilerLock;
+        mProcessDependencies = processDependencies;
     }
 
     /** Load settings from device config and register a listener for changes. */
@@ -190,6 +219,18 @@
     }
 
     @GuardedBy("mPhenotypeFlagLock")
+    private void updateRssUpdateRateMs() {
+        mRssUpdateRateMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS, DEFAULT_RSS_UPDATE_RATE_MS);
+    }
+
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateUseFrequentRss() {
+        mUseFrequentRss = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_OOM_RE_RANKING_USE_FREQUENT_RSS, DEFAULT_USE_FREQUENT_RSS);
+    }
+
+    @GuardedBy("mPhenotypeFlagLock")
     private void updateLruWeight() {
         mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
@@ -244,6 +285,8 @@
         float usesWeight;
         float rssWeight;
         int preserveTopNApps;
+        boolean useFrequentRss;
+        long rssUpdateRateMs;
         int[] lruPositions;
         RankedProcessRecord[] scoredProcessRecords;
 
@@ -252,6 +295,8 @@
             usesWeight = mUsesWeight;
             rssWeight = mRssWeight;
             preserveTopNApps = mPreserveTopNApps;
+            useFrequentRss = mUseFrequentRss;
+            rssUpdateRateMs = mRssUpdateRateMs;
             lruPositions = mLruPositions;
             scoredProcessRecords = mScoredProcessRecords;
         }
@@ -296,6 +341,32 @@
             }
         }
 
+        if (useFrequentRss) {
+            // Update RSS values for re-ranked apps.
+            long nowMs = SystemClock.elapsedRealtime();
+            for (int i = 0; i < numProcessesReRanked; ++i) {
+                RankedProcessRecord scoredProcessRecord = scoredProcessRecords[i];
+                long sinceUpdateMs =
+                        nowMs - scoredProcessRecord.proc.mState.getCacheOomRankerRssTimeMs();
+                if (scoredProcessRecord.proc.mState.getCacheOomRankerRss() != 0
+                        && sinceUpdateMs < rssUpdateRateMs) {
+                    continue;
+                }
+
+                long[] rss = mProcessDependencies.getRss(scoredProcessRecord.proc.getPid());
+                if (rss == null || rss.length == 0) {
+                    Slog.e(
+                            OomAdjuster.TAG,
+                            "Process.getRss returned bad value, not re-ranking: "
+                                    + Arrays.toString(rss));
+                    return;
+                }
+                // First element is total RSS:
+                // frameworks/base/core/jni/android_util_Process.cpp:1192
+                scoredProcessRecord.proc.mState.setCacheOomRankerRss(rss[0], nowMs);
+            }
+        }
+
         // Add scores for each of the weighted features we want to rank based on.
         if (lruWeight > 0.0f) {
             // This doesn't use the LRU list ordering as after the first re-ranking
@@ -305,8 +376,12 @@
             addToScore(scoredProcessRecords, lruWeight);
         }
         if (rssWeight > 0.0f) {
-            synchronized (mService.mAppProfiler.mProfilerLock) {
-                Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
+            if (useFrequentRss) {
+                Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, RSS_COMPARATOR);
+            } else {
+                synchronized (mService.mAppProfiler.mProfilerLock) {
+                    Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
+                }
             }
             addToScore(scoredProcessRecords, rssWeight);
         }
@@ -385,6 +460,16 @@
         }
     }
 
+    private static class RssComparator implements Comparator<RankedProcessRecord> {
+        @Override
+        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
+            // High RSS first to match least recently used.
+            return Long.compare(
+                    o2.proc.mState.getCacheOomRankerRss(),
+                    o1.proc.mState.getCacheOomRankerRss());
+        }
+    }
+
     private static class LastRssComparator implements Comparator<RankedProcessRecord> {
         @Override
         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
@@ -397,4 +482,18 @@
         public ProcessRecord proc;
         public float score;
     }
+
+    /**
+     * Interface for mocking {@link Process} static methods.
+     */
+    interface ProcessDependencies {
+        long[] getRss(int pid);
+    }
+
+    private static class ProcessDependenciesImpl implements ProcessDependencies {
+        @Override
+        public long[] getRss(int pid) {
+            return Process.getRss(pid);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 7b9a356e..c4ce873 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -360,6 +360,20 @@
     private int mCacheOomRankerUseCount;
 
     /**
+     * Process memory usage (RSS).
+     *
+     * Periodically populated by {@code CacheOomRanker}, stored in this object to cache the values.
+     */
+    @GuardedBy("mService")
+    private long mCacheOomRankerRss;
+
+    /**
+     * The last time, in milliseconds since boot, since {@link #mCacheOomRankerRss} was updated.
+     */
+    @GuardedBy("mService")
+    private long mCacheOomRankerRssTimeMs;
+
+    /**
      * Whether or not this process is reachable from given process.
      */
     @GuardedBy("mService")
@@ -1266,6 +1280,21 @@
         return mLastCanKillOnBgRestrictedAndIdleTime;
     }
 
+    public void setCacheOomRankerRss(long rss, long rssTimeMs) {
+        mCacheOomRankerRss = rss;
+        mCacheOomRankerRssTimeMs = rssTimeMs;
+    }
+
+    @GuardedBy("mService")
+    public long getCacheOomRankerRss() {
+        return mCacheOomRankerRss;
+    }
+
+    @GuardedBy("mService")
+    public long getCacheOomRankerRssTimeMs() {
+        return mCacheOomRankerRssTimeMs;
+    }
+
     @GuardedBy({"mService", "mProcLock"})
     void dump(PrintWriter pw, String prefix, long nowUptime) {
         if (mReportedInteraction || mFgInteractionTime != 0) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index d0c9242..609768c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -57,6 +57,9 @@
 import java.time.ZoneOffset;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -67,6 +70,7 @@
  * Build/Install/Run:
  * atest FrameworksMockingServicesTests:CacheOomRankerTest
  */
+@SuppressWarnings("GuardedBy") // No tests are concurrent, so no need to test locking.
 @RunWith(MockitoJUnitRunner.class)
 public class CacheOomRankerTest {
     private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay(
@@ -91,6 +95,7 @@
     private int mNextUid = 30000;
     private int mNextPackageUid = 40000;
     private int mNextPackageName = 1;
+    private Map<Integer, Long> mPidToRss;
 
     private TestExecutor mExecutor = new TestExecutor();
     private CacheOomRanker mCacheOomRanker;
@@ -116,7 +121,15 @@
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
 
-        mCacheOomRanker = new CacheOomRanker(mAms);
+        mPidToRss = new HashMap<>();
+        mCacheOomRanker = new CacheOomRanker(
+                mAms,
+                pid -> {
+                    Long rss = mPidToRss.get(pid);
+                    assertThat(rss).isNotNull();
+                    return new long[]{rss};
+                }
+        );
         mCacheOomRanker.init(mExecutor);
     }
 
@@ -184,6 +197,8 @@
     public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 5,
                 /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 0.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */1.0f);
@@ -223,6 +238,8 @@
     public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 6,
                 /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 0.0f,
                 /* pssWeight= */ 1.0f,
                 /* lruWeight= */ 0.0f);
@@ -261,9 +278,128 @@
     }
 
     @Test
+    public void reRankLruCachedApps_rssImpactsOrdering_cachedRssValues()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 10000000,
+                /* usesWeight= */ 0.0f,
+                /* pssWeight= */ 1.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(rss10k);
+        ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(rss20k);
+        ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
+        processList.add(rss1k);
+        ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(rss100k);
+        ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(rss2k);
+        ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
+        processList.add(rss15k);
+        // Only re-ranking 6 entries so this should stay in most recent position.
+        ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
+        processList.add(rss16k);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // First 6 ordered by largest pss, then last processes position unchanged.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+
+        // Clear mPidToRss so that Process.getRss calls fail.
+        mPidToRss.clear();
+        // Mix up the process list to ensure that CacheOomRanker actually re-ranks.
+        Collections.swap(processList, 0, 1);
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // Re ranking is the same.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_rssImpactsOrdering_profileRss()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ false,
+                /* rssUpdateRateMs= */ 10000000,
+                /* usesWeight= */ 0.0f,
+                /* pssWeight= */ 1.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 0L, 1000);
+        rss10k.mProfile.setLastRss(10 * 1024L);
+        processList.add(rss10k);
+        ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 0L, 2000);
+        rss20k.mProfile.setLastRss(20 * 1024L);
+        processList.add(rss20k);
+        ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10000);
+        rss1k.mProfile.setLastRss(1024L);
+        processList.add(rss1k);
+        ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10);
+        rss100k.mProfile.setLastRss(100 * 1024L);
+        processList.add(rss100k);
+        ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 0L, 20);
+        rss2k.mProfile.setLastRss(2 * 1024L);
+        processList.add(rss2k);
+        ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
+        rss15k.mProfile.setLastRss(15 * 1024L);
+        processList.add(rss15k);
+        // Only re-ranking 6 entries so this should stay in most recent position.
+        ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
+        rss16k.mProfile.setLastRss(16 * 1024L);
+        processList.add(rss16k);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        // This should not be used, as RSS values are taken from mProfile.
+        mPidToRss.clear();
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // First 6 ordered by largest pss, then last processes position unchanged.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+
+        // Clear mPidToRss so that Process.getRss calls fail.
+        mPidToRss.clear();
+        // Mix up the process list to ensure that CacheOomRanker actually re-ranks.
+        Collections.swap(processList, 0, 1);
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // Re ranking is the same.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+    }
+
+
+    @Test
     public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 4,
                 /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
@@ -302,6 +438,8 @@
     public void reRankLruCachedApps_fewProcesses() throws InterruptedException {
         setConfig(/* numberToReRank= */ 4,
                 /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
@@ -339,6 +477,8 @@
     public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException {
         setConfig(/* numberToReRank= */ 4,
                 /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
@@ -376,6 +516,8 @@
     public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException {
         setConfig(/* numberToReRank= */ 6,
                 /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
@@ -439,6 +581,8 @@
     public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException {
         setConfig(/* numberToReRank= */ 6,
                 /* preserveTopNApps= */ 3,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
@@ -478,6 +622,8 @@
             throws InterruptedException {
         setConfig(/* numberToReRank= */ 6,
                 /* preserveTopNApps= */ 100,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
@@ -516,6 +662,8 @@
             throws InterruptedException {
         setConfig(/* numberToReRank= */ 6,
                 /* preserveTopNApps= */ -100,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
@@ -550,8 +698,8 @@
                 used200).inOrder();
     }
 
-    private void setConfig(int numberToReRank, int preserveTopNApps, float usesWeight,
-            float pssWeight, float lruWeight)
+    private void setConfig(int numberToReRank, int preserveTopNApps, boolean useFrequentRss,
+            long rssUpdateRateMs, float usesWeight, float pssWeight, float lruWeight)
             throws InterruptedException {
         mExecutor.init(4);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -563,6 +711,14 @@
                 Integer.toString(preserveTopNApps),
                 false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_USE_FREQUENT_RSS,
+                Boolean.toString(useFrequentRss),
+                false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS,
+                Long.toString(rssUpdateRateMs),
+                false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
                 Float.toString(lruWeight),
                 false);
@@ -576,6 +732,8 @@
                 false);
         mExecutor.waitForLatch();
         assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
+        assertThat(mCacheOomRanker.mUseFrequentRss).isEqualTo(useFrequentRss);
+        assertThat(mCacheOomRanker.mRssUpdateRateMs).isEqualTo(rssUpdateRateMs);
         assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
         assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight);
         assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
@@ -592,7 +750,7 @@
         app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
         app.mState.setCurAdj(setAdj);
         app.setLastActivityTime(lastActivityTime);
-        app.mProfile.setLastRss(lastRss);
+        mPidToRss.put(app.getPid(), lastRss);
         app.mState.setCached(false);
         for (int i = 0; i < wentToForegroundCount; ++i) {
             app.mState.setSetProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);