Merge changes from topic "CacheOomRanker-new-features-sc-qpr1-dev" into sc-qpr1-dev
* changes: Use Process.getRss instead of mProfile.getLastRss in CacheOomRanker. Change CacheOomRanker's "uses" feature. Fix race condition when using mPreserveTopNApps. Allow CacheOomRanker to rerank when there's <8 processes in the cache. Fix CacheOomRankerTest.
This commit is contained in:
@@ -771,6 +771,11 @@ public class ActivityManager {
|
|||||||
return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
|
return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide Should this process state be considered in the cache? */
|
||||||
|
public static final boolean isProcStateCached(int procState) {
|
||||||
|
return procState >= PROCESS_STATE_CACHED_ACTIVITY;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide Is this a foreground service type? */
|
/** @hide Is this a foreground service type? */
|
||||||
public static boolean isForegroundService(int procState) {
|
public static boolean isForegroundService(int procState) {
|
||||||
return procState == PROCESS_STATE_FOREGROUND_SERVICE;
|
return procState == PROCESS_STATE_FOREGROUND_SERVICE;
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.server.am;
|
package com.android.server.am;
|
||||||
|
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
@@ -38,21 +40,40 @@ public class CacheOomRanker {
|
|||||||
private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
|
private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
|
static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
|
||||||
@VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
|
@VisibleForTesting
|
||||||
|
static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS =
|
||||||
|
"oom_re_ranking_preserve_top_n_apps";
|
||||||
|
@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
|
@VisibleForTesting
|
||||||
static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
|
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;
|
@VisibleForTesting
|
||||||
|
static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
|
static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
|
||||||
@VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
|
@VisibleForTesting
|
||||||
|
static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
|
static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
|
||||||
@VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
|
@VisibleForTesting
|
||||||
|
static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
|
||||||
|
|
||||||
private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
|
private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
|
||||||
new ScoreComparator();
|
new ScoreComparator();
|
||||||
private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
|
private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
|
||||||
new CacheUseComparator();
|
new CacheUseComparator();
|
||||||
|
private static final Comparator<RankedProcessRecord> RSS_COMPARATOR =
|
||||||
|
new RssComparator();
|
||||||
private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
|
private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
|
||||||
new LastRssComparator();
|
new LastRssComparator();
|
||||||
private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
|
private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
|
||||||
@@ -61,20 +82,33 @@ public class CacheOomRanker {
|
|||||||
private final Object mPhenotypeFlagLock = new Object();
|
private final Object mPhenotypeFlagLock = new Object();
|
||||||
|
|
||||||
private final ActivityManagerService mService;
|
private final ActivityManagerService mService;
|
||||||
|
private final ProcessDependencies mProcessDependencies;
|
||||||
private final ActivityManagerGlobalLock mProcLock;
|
private final ActivityManagerGlobalLock mProcLock;
|
||||||
private final Object mProfilerLock;
|
private final Object mProfilerLock;
|
||||||
|
|
||||||
@GuardedBy("mPhenotypeFlagLock")
|
@GuardedBy("mPhenotypeFlagLock")
|
||||||
private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
|
private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
|
||||||
|
@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.
|
// Weight to apply to the LRU ordering.
|
||||||
@GuardedBy("mPhenotypeFlagLock")
|
@GuardedBy("mPhenotypeFlagLock")
|
||||||
@VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
|
@VisibleForTesting
|
||||||
|
float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
|
||||||
// Weight to apply to the ordering by number of times the process has been added to the cache.
|
// Weight to apply to the ordering by number of times the process has been added to the cache.
|
||||||
@GuardedBy("mPhenotypeFlagLock")
|
@GuardedBy("mPhenotypeFlagLock")
|
||||||
@VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
|
@VisibleForTesting
|
||||||
|
float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
|
||||||
// Weight to apply to the ordering by RSS used by the processes.
|
// Weight to apply to the ordering by RSS used by the processes.
|
||||||
@GuardedBy("mPhenotypeFlagLock")
|
@GuardedBy("mPhenotypeFlagLock")
|
||||||
@VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
|
@VisibleForTesting
|
||||||
|
float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
|
||||||
|
|
||||||
// Positions to replace in the lru list.
|
// Positions to replace in the lru list.
|
||||||
@GuardedBy("mPhenotypeFlagLock")
|
@GuardedBy("mPhenotypeFlagLock")
|
||||||
@@ -93,6 +127,12 @@ public class CacheOomRanker {
|
|||||||
updateUseOomReranking();
|
updateUseOomReranking();
|
||||||
} else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
|
} else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
|
||||||
updateNumberToReRank();
|
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)) {
|
} else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
|
||||||
updateLruWeight();
|
updateLruWeight();
|
||||||
} else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
|
} else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
|
||||||
@@ -106,9 +146,15 @@ public class CacheOomRanker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CacheOomRanker(final ActivityManagerService service) {
|
CacheOomRanker(final ActivityManagerService service) {
|
||||||
|
this(service, new ProcessDependenciesImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
CacheOomRanker(final ActivityManagerService service, ProcessDependencies processDependencies) {
|
||||||
mService = service;
|
mService = service;
|
||||||
mProcLock = service.mProcLock;
|
mProcLock = service.mProcLock;
|
||||||
mProfilerLock = service.mAppProfiler.mProfilerLock;
|
mProfilerLock = service.mAppProfiler.mProfilerLock;
|
||||||
|
mProcessDependencies = processDependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load settings from device config and register a listener for changes. */
|
/** Load settings from device config and register a listener for changes. */
|
||||||
@@ -159,6 +205,31 @@ public class CacheOomRanker {
|
|||||||
return mScoredProcessRecords == null ? 0 : mScoredProcessRecords.length;
|
return mScoredProcessRecords == null ? 0 : mScoredProcessRecords.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mPhenotypeFlagLock")
|
||||||
|
private void updatePreserveTopNApps() {
|
||||||
|
int preserveTopNApps = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
|
KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, DEFAULT_PRESERVE_TOP_N_APPS);
|
||||||
|
if (preserveTopNApps < 0) {
|
||||||
|
Slog.w(OomAdjuster.TAG,
|
||||||
|
"Found negative value for preserveTopNApps, setting to default: "
|
||||||
|
+ preserveTopNApps);
|
||||||
|
preserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
|
||||||
|
}
|
||||||
|
mPreserveTopNApps = preserveTopNApps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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")
|
@GuardedBy("mPhenotypeFlagLock")
|
||||||
private void updateLruWeight() {
|
private void updateLruWeight() {
|
||||||
mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
@@ -183,9 +254,39 @@ public class CacheOomRanker {
|
|||||||
*/
|
*/
|
||||||
@GuardedBy({"mService", "mProcLock"})
|
@GuardedBy({"mService", "mProcLock"})
|
||||||
void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
|
void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
|
||||||
|
// The lruList is a list of processes ordered by how recently they were used. The
|
||||||
|
// least-recently-used apps are at the beginning of the list. We keep track of two
|
||||||
|
// indices in the lruList:
|
||||||
|
//
|
||||||
|
// getNumberToReRank=5, preserveTopNApps=3, lruProcessServiceStart=7,
|
||||||
|
// lruList=
|
||||||
|
// 0: app A ^
|
||||||
|
// 1: app B | These apps are re-ranked, as they are the first five apps (see
|
||||||
|
// 2: app C | getNumberToReRank), excluding...
|
||||||
|
// 3: app D v
|
||||||
|
// 4: app E ^
|
||||||
|
// 5: app F | The three most-recently-used apps in the cache (see preserveTopNApps).
|
||||||
|
// 6: app G v
|
||||||
|
// 7: service A ^
|
||||||
|
// 8: service B | Everything beyond lruProcessServiceStart is ignored, as these aren't
|
||||||
|
// 9: service C | apps.
|
||||||
|
// 10: activity A |
|
||||||
|
// ... |
|
||||||
|
//
|
||||||
|
// `numProcessesEvaluated` moves across the apps (indices 0-6) or until we've found enough
|
||||||
|
// apps to re-rank, and made sure none of them are in the top `preserveTopNApps` apps.
|
||||||
|
// Re-ranked apps are copied into `scoredProcessRecords`, where the re-ranking calculation
|
||||||
|
// happens.
|
||||||
|
//
|
||||||
|
// Note that some apps in the `lruList` can be skipped, if they don't pass
|
||||||
|
//`appCanBeReRanked`.
|
||||||
|
|
||||||
float lruWeight;
|
float lruWeight;
|
||||||
float usesWeight;
|
float usesWeight;
|
||||||
float rssWeight;
|
float rssWeight;
|
||||||
|
int preserveTopNApps;
|
||||||
|
boolean useFrequentRss;
|
||||||
|
long rssUpdateRateMs;
|
||||||
int[] lruPositions;
|
int[] lruPositions;
|
||||||
RankedProcessRecord[] scoredProcessRecords;
|
RankedProcessRecord[] scoredProcessRecords;
|
||||||
|
|
||||||
@@ -193,6 +294,9 @@ public class CacheOomRanker {
|
|||||||
lruWeight = mLruWeight;
|
lruWeight = mLruWeight;
|
||||||
usesWeight = mUsesWeight;
|
usesWeight = mUsesWeight;
|
||||||
rssWeight = mRssWeight;
|
rssWeight = mRssWeight;
|
||||||
|
preserveTopNApps = mPreserveTopNApps;
|
||||||
|
useFrequentRss = mUseFrequentRss;
|
||||||
|
rssUpdateRateMs = mRssUpdateRateMs;
|
||||||
lruPositions = mLruPositions;
|
lruPositions = mLruPositions;
|
||||||
scoredProcessRecords = mScoredProcessRecords;
|
scoredProcessRecords = mScoredProcessRecords;
|
||||||
}
|
}
|
||||||
@@ -202,52 +306,98 @@ public class CacheOomRanker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int numProcessesEvaluated = 0;
|
||||||
// Collect the least recently used processes to re-rank, only rank cached
|
// Collect the least recently used processes to re-rank, only rank cached
|
||||||
// processes further down the list than mLruProcessServiceStart.
|
// processes further down the list than mLruProcessServiceStart.
|
||||||
int cachedProcessPos = 0;
|
int numProcessesReRanked = 0;
|
||||||
for (int i = 0; i < lruProcessServiceStart
|
while (numProcessesEvaluated < lruProcessServiceStart
|
||||||
&& cachedProcessPos < scoredProcessRecords.length; ++i) {
|
&& numProcessesReRanked < scoredProcessRecords.length) {
|
||||||
ProcessRecord app = lruList.get(i);
|
ProcessRecord process = lruList.get(numProcessesEvaluated);
|
||||||
// Processes that will be assigned a cached oom adj score.
|
// Processes that will be assigned a cached oom adj score.
|
||||||
if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
|
if (appCanBeReRanked(process)) {
|
||||||
>= ProcessList.UNKNOWN_ADJ) {
|
scoredProcessRecords[numProcessesReRanked].proc = process;
|
||||||
scoredProcessRecords[cachedProcessPos].proc = app;
|
scoredProcessRecords[numProcessesReRanked].score = 0.0f;
|
||||||
scoredProcessRecords[cachedProcessPos].score = 0.0f;
|
lruPositions[numProcessesReRanked] = numProcessesEvaluated;
|
||||||
lruPositions[cachedProcessPos] = i;
|
++numProcessesReRanked;
|
||||||
++cachedProcessPos;
|
}
|
||||||
|
++numProcessesEvaluated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count how many apps we're not re-ranking (up to preserveTopNApps).
|
||||||
|
int numProcessesNotReRanked = 0;
|
||||||
|
while (numProcessesEvaluated < lruProcessServiceStart
|
||||||
|
&& numProcessesNotReRanked < preserveTopNApps) {
|
||||||
|
ProcessRecord process = lruList.get(numProcessesEvaluated);
|
||||||
|
if (appCanBeReRanked(process)) {
|
||||||
|
numProcessesNotReRanked++;
|
||||||
|
}
|
||||||
|
numProcessesEvaluated++;
|
||||||
|
}
|
||||||
|
// Exclude the top `preserveTopNApps` apps from re-ranking.
|
||||||
|
if (numProcessesNotReRanked < preserveTopNApps) {
|
||||||
|
numProcessesReRanked -= preserveTopNApps - numProcessesNotReRanked;
|
||||||
|
if (numProcessesReRanked < 0) {
|
||||||
|
numProcessesReRanked = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO maybe ensure a certain number above this in the cache before re-ranking.
|
if (useFrequentRss) {
|
||||||
if (cachedProcessPos < scoredProcessRecords.length) {
|
// Update RSS values for re-ranked apps.
|
||||||
// Ignore we don't have enough processes to worry about re-ranking.
|
long nowMs = SystemClock.elapsedRealtime();
|
||||||
return;
|
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);
|
||||||
|
scoredProcessRecord.proc.mProfile.setLastRss(rss[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add scores for each of the weighted features we want to rank based on.
|
// Add scores for each of the weighted features we want to rank based on.
|
||||||
if (lruWeight > 0.0f) {
|
if (lruWeight > 0.0f) {
|
||||||
// This doesn't use the LRU list ordering as after the first re-ranking
|
// This doesn't use the LRU list ordering as after the first re-ranking
|
||||||
// that will no longer be lru.
|
// that will no longer be lru.
|
||||||
Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
|
Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
|
||||||
|
LAST_ACTIVITY_TIME_COMPARATOR);
|
||||||
addToScore(scoredProcessRecords, lruWeight);
|
addToScore(scoredProcessRecords, lruWeight);
|
||||||
}
|
}
|
||||||
if (rssWeight > 0.0f) {
|
if (rssWeight > 0.0f) {
|
||||||
synchronized (mService.mAppProfiler.mProfilerLock) {
|
if (useFrequentRss) {
|
||||||
Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
|
Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, RSS_COMPARATOR);
|
||||||
|
} else {
|
||||||
|
synchronized (mService.mAppProfiler.mProfilerLock) {
|
||||||
|
Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addToScore(scoredProcessRecords, rssWeight);
|
addToScore(scoredProcessRecords, rssWeight);
|
||||||
}
|
}
|
||||||
if (usesWeight > 0.0f) {
|
if (usesWeight > 0.0f) {
|
||||||
Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
|
Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, CACHE_USE_COMPARATOR);
|
||||||
addToScore(scoredProcessRecords, usesWeight);
|
addToScore(scoredProcessRecords, usesWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-rank by the new combined score.
|
// Re-rank by the new combined score.
|
||||||
Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
|
Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
|
||||||
|
SCORED_PROCESS_RECORD_COMPARATOR);
|
||||||
|
|
||||||
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
|
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
|
||||||
boolean printedHeader = false;
|
boolean printedHeader = false;
|
||||||
for (int i = 0; i < scoredProcessRecords.length; ++i) {
|
for (int i = 0; i < numProcessesReRanked; ++i) {
|
||||||
if (scoredProcessRecords[i].proc.getPid()
|
if (scoredProcessRecords[i].proc.getPid()
|
||||||
!= lruList.get(lruPositions[i]).getPid()) {
|
!= lruList.get(lruPositions[i]).getPid()) {
|
||||||
if (!printedHeader) {
|
if (!printedHeader) {
|
||||||
@@ -260,12 +410,18 @@ public class CacheOomRanker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < scoredProcessRecords.length; ++i) {
|
for (int i = 0; i < numProcessesReRanked; ++i) {
|
||||||
lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
|
lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
|
||||||
scoredProcessRecords[i].proc = null;
|
scoredProcessRecords[i].proc = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean appCanBeReRanked(ProcessRecord process) {
|
||||||
|
return !process.isKilledByAm()
|
||||||
|
&& process.getThread() != null
|
||||||
|
&& process.mState.getCurAdj() >= ProcessList.UNKNOWN_ADJ;
|
||||||
|
}
|
||||||
|
|
||||||
private static void addToScore(RankedProcessRecord[] scores, float weight) {
|
private static void addToScore(RankedProcessRecord[] scores, float weight) {
|
||||||
for (int i = 1; i < scores.length; ++i) {
|
for (int i = 1; i < scores.length; ++i) {
|
||||||
scores[i].score += i * weight;
|
scores[i].score += i * weight;
|
||||||
@@ -305,6 +461,16 @@ public class CacheOomRanker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
private static class LastRssComparator implements Comparator<RankedProcessRecord> {
|
||||||
@Override
|
@Override
|
||||||
public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
|
public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
|
||||||
@@ -317,4 +483,18 @@ public class CacheOomRanker {
|
|||||||
public ProcessRecord proc;
|
public ProcessRecord proc;
|
||||||
public float score;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,6 +342,20 @@ final class ProcessStateRecord {
|
|||||||
@GuardedBy("mService")
|
@GuardedBy("mService")
|
||||||
private int mCacheOomRankerUseCount;
|
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.
|
* Whether or not this process is reachable from given process.
|
||||||
*/
|
*/
|
||||||
@@ -577,6 +591,10 @@ final class ProcessStateRecord {
|
|||||||
|
|
||||||
@GuardedBy({"mService", "mProcLock"})
|
@GuardedBy({"mService", "mProcLock"})
|
||||||
void setSetProcState(int setProcState) {
|
void setSetProcState(int setProcState) {
|
||||||
|
if (ActivityManager.isProcStateCached(mSetProcState)
|
||||||
|
&& !ActivityManager.isProcStateCached(setProcState)) {
|
||||||
|
mCacheOomRankerUseCount++;
|
||||||
|
}
|
||||||
mSetProcState = setProcState;
|
mSetProcState = setProcState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -840,12 +858,7 @@ final class ProcessStateRecord {
|
|||||||
|
|
||||||
@GuardedBy("mService")
|
@GuardedBy("mService")
|
||||||
void setCached(boolean cached) {
|
void setCached(boolean cached) {
|
||||||
if (mCached != cached) {
|
mCached = cached;
|
||||||
mCached = cached;
|
|
||||||
if (cached) {
|
|
||||||
++mCacheOomRankerUseCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mService")
|
@GuardedBy("mService")
|
||||||
@@ -1151,6 +1164,21 @@ final class ProcessStateRecord {
|
|||||||
return mLastInvisibleTime;
|
return mLastInvisibleTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"})
|
@GuardedBy({"mService", "mProcLock"})
|
||||||
void dump(PrintWriter pw, String prefix, long nowUptime) {
|
void dump(PrintWriter pw, String prefix, long nowUptime) {
|
||||||
if (mReportedInteraction || mFgInteractionTime != 0) {
|
if (mReportedInteraction || mFgInteractionTime != 0) {
|
||||||
|
|||||||
@@ -21,12 +21,16 @@ import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.app.IApplicationThread;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManagerInternal;
|
import android.content.pm.PackageManagerInternal;
|
||||||
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
@@ -48,8 +52,14 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.time.Duration;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
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.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -60,8 +70,11 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* Build/Install/Run:
|
* Build/Install/Run:
|
||||||
* atest FrameworksMockingServicesTests:CacheOomRankerTest
|
* atest FrameworksMockingServicesTests:CacheOomRankerTest
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("GuardedBy") // No tests are concurrent, so no need to test locking.
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class CacheOomRankerTest {
|
public class CacheOomRankerTest {
|
||||||
|
private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay(
|
||||||
|
ZoneOffset.UTC).toInstant();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private AppOpsService mAppOpsService;
|
private AppOpsService mAppOpsService;
|
||||||
@@ -82,6 +95,7 @@ public class CacheOomRankerTest {
|
|||||||
private int mNextUid = 30000;
|
private int mNextUid = 30000;
|
||||||
private int mNextPackageUid = 40000;
|
private int mNextPackageUid = 40000;
|
||||||
private int mNextPackageName = 1;
|
private int mNextPackageName = 1;
|
||||||
|
private Map<Integer, Long> mPidToRss;
|
||||||
|
|
||||||
private TestExecutor mExecutor = new TestExecutor();
|
private TestExecutor mExecutor = new TestExecutor();
|
||||||
private CacheOomRanker mCacheOomRanker;
|
private CacheOomRanker mCacheOomRanker;
|
||||||
@@ -107,7 +121,15 @@ public class CacheOomRankerTest {
|
|||||||
LocalServices.removeServiceForTest(PackageManagerInternal.class);
|
LocalServices.removeServiceForTest(PackageManagerInternal.class);
|
||||||
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
|
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);
|
mCacheOomRanker.init(mExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +156,15 @@ public class CacheOomRankerTest {
|
|||||||
assertThat(mCacheOomRanker.getNumberToReRank())
|
assertThat(mCacheOomRanker.getNumberToReRank())
|
||||||
.isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK + 2);
|
.isEqualTo(CacheOomRanker.DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK + 2);
|
||||||
|
|
||||||
|
mExecutor.init();
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
|
CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
|
||||||
|
Integer.toString(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1),
|
||||||
|
false);
|
||||||
|
mExecutor.waitForLatch();
|
||||||
|
assertThat(mCacheOomRanker.mPreserveTopNApps)
|
||||||
|
.isEqualTo(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1);
|
||||||
|
|
||||||
mExecutor.init();
|
mExecutor.init();
|
||||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
|
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
|
||||||
@@ -165,6 +196,9 @@ public class CacheOomRankerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
|
public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
|
||||||
setConfig(/* numberToReRank= */ 5,
|
setConfig(/* numberToReRank= */ 5,
|
||||||
|
/* preserveTopNApps= */ 0,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
/* usesWeight= */ 0.0f,
|
/* usesWeight= */ 0.0f,
|
||||||
/* pssWeight= */ 0.0f,
|
/* pssWeight= */ 0.0f,
|
||||||
/* lruWeight= */1.0f);
|
/* lruWeight= */1.0f);
|
||||||
@@ -172,36 +206,40 @@ public class CacheOomRankerTest {
|
|||||||
ProcessList list = new ProcessList();
|
ProcessList list = new ProcessList();
|
||||||
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
processList.add(lastUsed40MinutesAgo);
|
processList.add(lastUsed40MinutesAgo);
|
||||||
ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
processList.add(lastUsed42MinutesAgo);
|
processList.add(lastUsed42MinutesAgo);
|
||||||
ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(60).toMillis(), 1024L, 10000);
|
NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
|
||||||
processList.add(lastUsed60MinutesAgo);
|
processList.add(lastUsed60MinutesAgo);
|
||||||
ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
processList.add(lastUsed15MinutesAgo);
|
processList.add(lastUsed15MinutesAgo);
|
||||||
ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(17).toMillis(), 1024L, 20);
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
|
||||||
processList.add(lastUsed17MinutesAgo);
|
processList.add(lastUsed17MinutesAgo);
|
||||||
// Only re-ranking 5 entries so this should stay in most recent position.
|
// Only re-ranking 5 entries so this should stay in most recent position.
|
||||||
ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(30).toMillis(), 1024L, 20);
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
|
||||||
processList.add(lastUsed30MinutesAgo);
|
processList.add(lastUsed30MinutesAgo);
|
||||||
|
list.setLruProcessServiceStartLSP(processList.size());
|
||||||
|
|
||||||
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
// First 5 ordered by least recently used first, then last processes position unchanged.
|
// First 5 ordered by least recently used first, then last processes position unchanged.
|
||||||
assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
|
assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
|
||||||
lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo,
|
lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo,
|
||||||
lastUsed30MinutesAgo);
|
lastUsed30MinutesAgo).inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
|
public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
|
||||||
setConfig(/* numberToReRank= */ 6,
|
setConfig(/* numberToReRank= */ 6,
|
||||||
|
/* preserveTopNApps= */ 0,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
/* usesWeight= */ 0.0f,
|
/* usesWeight= */ 0.0f,
|
||||||
/* pssWeight= */ 1.0f,
|
/* pssWeight= */ 1.0f,
|
||||||
/* lruWeight= */ 0.0f);
|
/* lruWeight= */ 0.0f);
|
||||||
@@ -209,151 +247,477 @@ public class CacheOomRankerTest {
|
|||||||
ProcessList list = new ProcessList();
|
ProcessList list = new ProcessList();
|
||||||
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
processList.add(rss10k);
|
processList.add(rss10k);
|
||||||
ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
processList.add(rss20k);
|
processList.add(rss20k);
|
||||||
ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(60).toMillis(), 1024L, 10000);
|
NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
|
||||||
processList.add(rss1k);
|
processList.add(rss1k);
|
||||||
ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
processList.add(rss100k);
|
processList.add(rss100k);
|
||||||
ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
processList.add(rss2k);
|
processList.add(rss2k);
|
||||||
ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(30).toMillis(), 15 * 1024L, 20);
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
|
||||||
processList.add(rss15k);
|
processList.add(rss15k);
|
||||||
// Only re-ranking 6 entries so this should stay in most recent position.
|
// Only re-ranking 6 entries so this should stay in most recent position.
|
||||||
ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
|
||||||
processList.add(rss16k);
|
processList.add(rss16k);
|
||||||
|
list.setLruProcessServiceStartLSP(processList.size());
|
||||||
|
|
||||||
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
// First 6 ordered by largest pss, then last processes position unchanged.
|
// First 6 ordered by largest pss, then last processes position unchanged.
|
||||||
assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
|
assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
|
||||||
rss16k);
|
rss16k).inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
|
public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
|
||||||
setConfig(/* numberToReRank= */ 4,
|
setConfig(/* numberToReRank= */ 4,
|
||||||
|
/* preserveTopNApps= */ 0,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
/* usesWeight= */ 1.0f,
|
/* usesWeight= */ 1.0f,
|
||||||
/* pssWeight= */ 0.0f,
|
/* pssWeight= */ 0.0f,
|
||||||
/* lruWeight= */ 0.0f);
|
/* lruWeight= */ 0.0f);
|
||||||
|
|
||||||
ProcessList list = new ProcessList();
|
ProcessList list = new ProcessList();
|
||||||
list.setLruProcessServiceStartLSP(1);
|
|
||||||
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
processList.add(used1000);
|
processList.add(used1000);
|
||||||
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
processList.add(used2000);
|
processList.add(used2000);
|
||||||
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
processList.add(used10);
|
processList.add(used10);
|
||||||
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
processList.add(used20);
|
processList.add(used20);
|
||||||
// Only re-ranking 6 entries so last two should stay in most recent position.
|
// Only re-ranking 6 entries so last two should stay in most recent position.
|
||||||
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
processList.add(used500);
|
processList.add(used500);
|
||||||
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
processList.add(used200);
|
processList.add(used200);
|
||||||
|
list.setLruProcessServiceStartLSP(processList.size());
|
||||||
|
|
||||||
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
// First 4 ordered by uses, then last processes position unchanged.
|
// First 4 ordered by uses, then last processes position unchanged.
|
||||||
assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
|
assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
|
||||||
used200);
|
used200).inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void reRankLruCachedApps_notEnoughProcesses() throws InterruptedException {
|
public void reRankLruCachedApps_fewProcesses() throws InterruptedException {
|
||||||
setConfig(/* numberToReRank= */ 4,
|
|
||||||
/* usesWeight= */ 0.5f,
|
|
||||||
/* pssWeight= */ 0.2f,
|
|
||||||
/* lruWeight= */ 0.3f);
|
|
||||||
|
|
||||||
ProcessList list = new ProcessList();
|
|
||||||
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
|
||||||
ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
|
||||||
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
|
|
||||||
processList.add(unknownAdj1);
|
|
||||||
ProcessRecord unknownAdj2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
|
||||||
Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
|
|
||||||
processList.add(unknownAdj2);
|
|
||||||
ProcessRecord unknownAdj3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
|
||||||
Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
|
|
||||||
processList.add(unknownAdj3);
|
|
||||||
ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
|
|
||||||
Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
|
|
||||||
processList.add(foregroundAdj);
|
|
||||||
ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
|
|
||||||
Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
|
|
||||||
processList.add(serviceAdj);
|
|
||||||
ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
|
|
||||||
Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
|
|
||||||
processList.add(systemAdj);
|
|
||||||
|
|
||||||
// 6 Processes but only 3 in eligible for cache so no re-ranking.
|
|
||||||
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
|
||||||
|
|
||||||
// All positions unchanged.
|
|
||||||
assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
|
|
||||||
foregroundAdj, serviceAdj, systemAdj);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void reRankLruCachedApps_notEnoughNonServiceProcesses() throws InterruptedException {
|
|
||||||
setConfig(/* numberToReRank= */ 4,
|
setConfig(/* numberToReRank= */ 4,
|
||||||
|
/* preserveTopNApps= */ 0,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
/* usesWeight= */ 1.0f,
|
/* usesWeight= */ 1.0f,
|
||||||
/* pssWeight= */ 0.0f,
|
/* pssWeight= */ 0.0f,
|
||||||
/* lruWeight= */ 0.0f);
|
/* lruWeight= */ 0.0f);
|
||||||
|
|
||||||
ProcessList list = new ProcessList();
|
ProcessList list = new ProcessList();
|
||||||
list.setLruProcessServiceStartLSP(4);
|
|
||||||
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
processList.add(used1000);
|
processList.add(used1000);
|
||||||
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
processList.add(used2000);
|
processList.add(used2000);
|
||||||
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
processList.add(used10);
|
processList.add(used10);
|
||||||
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
|
||||||
Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
processList.add(used20);
|
processList.add(foregroundAdj);
|
||||||
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
|
||||||
Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
processList.add(used500);
|
processList.add(serviceAdj);
|
||||||
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
|
||||||
Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
processList.add(used200);
|
processList.add(systemAdj);
|
||||||
|
list.setLruProcessServiceStartLSP(processList.size());
|
||||||
|
|
||||||
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
// All positions unchanged.
|
// 6 processes, only 3 in eligible for cache, so only those are re-ranked.
|
||||||
assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
|
assertThat(processList).containsExactly(used10, used1000, used2000,
|
||||||
used200);
|
foregroundAdj, serviceAdj, systemAdj).inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setConfig(int numberToReRank, float useWeight, float pssWeight, float lruWeight)
|
@Test
|
||||||
|
public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException {
|
||||||
|
setConfig(/* numberToReRank= */ 4,
|
||||||
|
/* preserveTopNApps= */ 0,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
|
/* usesWeight= */ 1.0f,
|
||||||
|
/* pssWeight= */ 0.0f,
|
||||||
|
/* lruWeight= */ 0.0f);
|
||||||
|
|
||||||
|
ProcessList list = new ProcessList();
|
||||||
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
|
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
|
processList.add(used1000);
|
||||||
|
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
|
processList.add(used2000);
|
||||||
|
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
|
processList.add(used10);
|
||||||
|
ProcessRecord service1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
|
processList.add(service1);
|
||||||
|
ProcessRecord service2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
|
processList.add(service2);
|
||||||
|
ProcessRecord service3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
|
processList.add(service3);
|
||||||
|
list.setLruProcessServiceStartLSP(3);
|
||||||
|
|
||||||
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
|
// Services unchanged, rest re-ranked.
|
||||||
|
assertThat(processList).containsExactly(used10, used1000, used2000, service1, service2,
|
||||||
|
service3).inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException {
|
||||||
|
setConfig(/* numberToReRank= */ 6,
|
||||||
|
/* preserveTopNApps= */ 0,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
|
/* usesWeight= */ 1.0f,
|
||||||
|
/* pssWeight= */ 0.0f,
|
||||||
|
/* lruWeight= */ 0.0f);
|
||||||
|
|
||||||
|
ProcessList set1List = new ProcessList();
|
||||||
|
ArrayList<ProcessRecord> set1ProcessList = set1List.getLruProcessesLSP();
|
||||||
|
ProcessRecord set1Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
|
set1ProcessList.add(set1Used1000);
|
||||||
|
ProcessRecord set1Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
|
set1ProcessList.add(set1Used2000);
|
||||||
|
ProcessRecord set1Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
|
set1ProcessList.add(set1Used10);
|
||||||
|
ProcessRecord set1Uses20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
|
set1ProcessList.add(set1Uses20);
|
||||||
|
ProcessRecord set1Uses500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
|
set1ProcessList.add(set1Uses500);
|
||||||
|
ProcessRecord set1Uses200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
|
set1ProcessList.add(set1Uses200);
|
||||||
|
set1List.setLruProcessServiceStartLSP(set1ProcessList.size());
|
||||||
|
|
||||||
|
mCacheOomRanker.reRankLruCachedAppsLSP(set1ProcessList,
|
||||||
|
set1List.getLruProcessServiceStartLOSP());
|
||||||
|
assertThat(set1ProcessList).containsExactly(set1Used10, set1Uses20, set1Uses200,
|
||||||
|
set1Uses500, set1Used1000, set1Used2000).inOrder();
|
||||||
|
|
||||||
|
ProcessList set2List = new ProcessList();
|
||||||
|
ArrayList<ProcessRecord> set2ProcessList = set2List.getLruProcessesLSP();
|
||||||
|
ProcessRecord set2Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
|
set2ProcessList.add(set2Used1000);
|
||||||
|
ProcessRecord set2Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
|
set2ProcessList.add(set2Used2000);
|
||||||
|
ProcessRecord set2Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
|
set2ProcessList.add(set2Used10);
|
||||||
|
ProcessRecord set2ForegroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
|
||||||
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
|
set2ProcessList.add(set2ForegroundAdj);
|
||||||
|
ProcessRecord set2ServiceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
|
set2ProcessList.add(set2ServiceAdj);
|
||||||
|
ProcessRecord set2SystemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
|
set2ProcessList.add(set2SystemAdj);
|
||||||
|
set2List.setLruProcessServiceStartLSP(set2ProcessList.size());
|
||||||
|
|
||||||
|
mCacheOomRanker.reRankLruCachedAppsLSP(set2ProcessList,
|
||||||
|
set2List.getLruProcessServiceStartLOSP());
|
||||||
|
assertThat(set2ProcessList).containsExactly(set2Used10, set2Used1000, set2Used2000,
|
||||||
|
set2ForegroundAdj, set2ServiceAdj, set2SystemAdj).inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException {
|
||||||
|
setConfig(/* numberToReRank= */ 6,
|
||||||
|
/* preserveTopNApps= */ 3,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
|
/* usesWeight= */ 1.0f,
|
||||||
|
/* pssWeight= */ 0.0f,
|
||||||
|
/* lruWeight= */ 0.0f);
|
||||||
|
|
||||||
|
ProcessList list = new ProcessList();
|
||||||
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
|
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
|
processList.add(used1000);
|
||||||
|
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
|
processList.add(used2000);
|
||||||
|
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
|
processList.add(used10);
|
||||||
|
// Preserving the top 3 processes, so these should not be re-ranked.
|
||||||
|
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
|
processList.add(used20);
|
||||||
|
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
|
processList.add(used500);
|
||||||
|
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
|
processList.add(used200);
|
||||||
|
list.setLruProcessServiceStartLSP(processList.size());
|
||||||
|
|
||||||
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
|
// First 3 ordered by uses, then last processes position unchanged.
|
||||||
|
assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
|
||||||
|
used200).inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void reRankLruCachedApps_preservesTopNApps_allAppsUnchanged()
|
||||||
|
throws InterruptedException {
|
||||||
|
setConfig(/* numberToReRank= */ 6,
|
||||||
|
/* preserveTopNApps= */ 100,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
|
/* usesWeight= */ 1.0f,
|
||||||
|
/* pssWeight= */ 0.0f,
|
||||||
|
/* lruWeight= */ 0.0f);
|
||||||
|
|
||||||
|
ProcessList list = new ProcessList();
|
||||||
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
|
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
|
processList.add(used1000);
|
||||||
|
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
|
processList.add(used2000);
|
||||||
|
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
|
processList.add(used10);
|
||||||
|
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
|
processList.add(used20);
|
||||||
|
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
|
processList.add(used500);
|
||||||
|
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
|
processList.add(used200);
|
||||||
|
list.setLruProcessServiceStartLSP(processList.size());
|
||||||
|
|
||||||
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
|
// Nothing reordered, as we preserve the top 100 apps.
|
||||||
|
assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
|
||||||
|
used200).inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault()
|
||||||
|
throws InterruptedException {
|
||||||
|
setConfig(/* numberToReRank= */ 6,
|
||||||
|
/* preserveTopNApps= */ -100,
|
||||||
|
/* useFrequentRss= */ true,
|
||||||
|
/* rssUpdateRateMs= */ 0,
|
||||||
|
/* usesWeight= */ 1.0f,
|
||||||
|
/* pssWeight= */ 0.0f,
|
||||||
|
/* lruWeight= */ 0.0f);
|
||||||
|
|
||||||
|
ProcessList list = new ProcessList();
|
||||||
|
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
|
||||||
|
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
|
||||||
|
processList.add(used1000);
|
||||||
|
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
|
||||||
|
processList.add(used2000);
|
||||||
|
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
|
||||||
|
processList.add(used10);
|
||||||
|
// Negative preserveTopNApps interpreted as the default (3), so the last three are unranked.
|
||||||
|
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
|
||||||
|
processList.add(used20);
|
||||||
|
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
|
||||||
|
processList.add(used500);
|
||||||
|
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
|
||||||
|
NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
|
||||||
|
processList.add(used200);
|
||||||
|
list.setLruProcessServiceStartLSP(processList.size());
|
||||||
|
|
||||||
|
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
|
||||||
|
|
||||||
|
// First 3 apps re-ranked, as preserveTopNApps is interpreted as 3.
|
||||||
|
assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
|
||||||
|
used200).inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setConfig(int numberToReRank, int preserveTopNApps, boolean useFrequentRss,
|
||||||
|
long rssUpdateRateMs, float usesWeight, float pssWeight, float lruWeight)
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
mExecutor.init(4);
|
mExecutor.init(4);
|
||||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
CacheOomRanker.KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK,
|
CacheOomRanker.KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK,
|
||||||
Integer.toString(numberToReRank),
|
Integer.toString(numberToReRank),
|
||||||
false);
|
false);
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
|
CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
|
||||||
|
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,
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
|
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
|
||||||
Float.toString(lruWeight),
|
Float.toString(lruWeight),
|
||||||
@@ -364,17 +728,19 @@ public class CacheOomRankerTest {
|
|||||||
false);
|
false);
|
||||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
|
||||||
CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT,
|
CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT,
|
||||||
Float.toString(useWeight),
|
Float.toString(usesWeight),
|
||||||
false);
|
false);
|
||||||
mExecutor.waitForLatch();
|
mExecutor.waitForLatch();
|
||||||
assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
|
assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
|
||||||
|
assertThat(mCacheOomRanker.mUseFrequentRss).isEqualTo(useFrequentRss);
|
||||||
|
assertThat(mCacheOomRanker.mRssUpdateRateMs).isEqualTo(rssUpdateRateMs);
|
||||||
assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
|
assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
|
||||||
assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(useWeight);
|
assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight);
|
||||||
assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
|
assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProcessRecord nextProcessRecord(int setAdj, long lastActivityTime, long lastRss,
|
private ProcessRecord nextProcessRecord(int setAdj, long lastActivityTime, long lastRss,
|
||||||
int returnedToCacheCount) {
|
int wentToForegroundCount) {
|
||||||
ApplicationInfo ai = new ApplicationInfo();
|
ApplicationInfo ai = new ApplicationInfo();
|
||||||
ai.packageName = "a.package.name" + mNextPackageName++;
|
ai.packageName = "a.package.name" + mNextPackageName++;
|
||||||
ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++);
|
ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++);
|
||||||
@@ -382,14 +748,20 @@ public class CacheOomRankerTest {
|
|||||||
app.info.uid = mNextPackageUid++;
|
app.info.uid = mNextPackageUid++;
|
||||||
// Exact value does not mater, it can be any state for which compaction is allowed.
|
// Exact value does not mater, it can be any state for which compaction is allowed.
|
||||||
app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
|
app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
|
||||||
app.mState.setSetAdj(setAdj);
|
app.mState.setCurAdj(setAdj);
|
||||||
app.setLastActivityTime(lastActivityTime);
|
app.setLastActivityTime(lastActivityTime);
|
||||||
app.mProfile.setLastRss(lastRss);
|
mPidToRss.put(app.getPid(), lastRss);
|
||||||
app.mState.setCached(false);
|
app.mState.setCached(false);
|
||||||
for (int i = 0; i < returnedToCacheCount; ++i) {
|
for (int i = 0; i < wentToForegroundCount; ++i) {
|
||||||
app.mState.setCached(false);
|
app.mState.setSetProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
|
||||||
app.mState.setCached(true);
|
app.mState.setSetProcState(ActivityManager.PROCESS_STATE_CACHED_RECENT);
|
||||||
}
|
}
|
||||||
|
// Sets the thread returned by ProcessRecord#getThread, which we use to check whether the
|
||||||
|
// app is currently launching.
|
||||||
|
ProcessStatsService processStatsService = new ProcessStatsService(
|
||||||
|
mock(ActivityManagerService.class), new File(Environment.getDataSystemCeDirectory(),
|
||||||
|
"procstats"));
|
||||||
|
app.makeActive(mock(IApplicationThread.class), processStatsService);
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user