diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 9d4174dfcc16f..9e95e5fb0a40e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -146,6 +146,9 @@ final class ActivityManagerConstants extends ContentObserver { */ static final String KEY_NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms"; + static final String KEY_USE_TIERED_CACHED_ADJ = "use_tiered_cached_adj"; + static final String KEY_TIERED_CACHED_ADJ_DECAY_TIME = "tiered_cached_adj_decay_time"; + private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; @@ -201,6 +204,9 @@ final class ActivityManagerConstants extends ContentObserver { static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000; + private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false; + private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000; + /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ @@ -1011,6 +1017,12 @@ final class ActivityManagerConstants extends ContentObserver { public volatile long mShortFgsAnrExtraWaitDuration = DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION; + /** @see #KEY_USE_TIERED_CACHED_ADJ */ + public boolean USE_TIERED_CACHED_ADJ = DEFAULT_USE_TIERED_CACHED_ADJ; + + /** @see #KEY_TIERED_CACHED_ADJ_DECAY_TIME */ + public long TIERED_CACHED_ADJ_DECAY_TIME = DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME; + private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = new OnPropertiesChangedListener() { @Override @@ -1179,6 +1191,10 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_MAX_PREVIOUS_TIME: updateMaxPreviousTime(); break; + case KEY_USE_TIERED_CACHED_ADJ: + case KEY_TIERED_CACHED_ADJ_DECAY_TIME: + updateUseTieredCachedAdj(); + break; default: break; } @@ -1924,6 +1940,17 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION); } + private void updateUseTieredCachedAdj() { + USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_USE_TIERED_CACHED_ADJ, + DEFAULT_USE_TIERED_CACHED_ADJ); + TIERED_CACHED_ADJ_DECAY_TIME = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_TIERED_CACHED_ADJ_DECAY_TIME, + DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME); + } + @NeverCompile // Avoid size overhead of debugging code. void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " @@ -2108,6 +2135,11 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" "); pw.print(KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION); pw.print("="); pw.println(mShortFgsAnrExtraWaitDuration); + pw.print(" "); pw.print(KEY_USE_TIERED_CACHED_ADJ); + pw.print("="); pw.println(USE_TIERED_CACHED_ADJ); + pw.print(" "); pw.print(KEY_TIERED_CACHED_ADJ_DECAY_TIME); + pw.print("="); pw.println(TIERED_CACHED_ADJ_DECAY_TIME); + pw.println(); if (mOverrideMaxCachedProcesses >= 0) { pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index baa2f8e46dd89..b68d993f2b701 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1073,135 +1073,165 @@ public class OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void assignCachedAdjIfNecessary(ArrayList lruList) { final int numLru = lruList.size(); + if (mConstants.USE_TIERED_CACHED_ADJ) { + final long now = SystemClock.uptimeMillis(); + for (int i = numLru - 1; i >= 0; i--) { + ProcessRecord app = lruList.get(i); + final ProcessStateRecord state = app.mState; + final ProcessCachedOptimizerRecord opt = app.mOptRecord; + if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj() + >= UNKNOWN_ADJ) { + final ProcessServiceRecord psr = app.mServices; + int targetAdj = CACHED_APP_MIN_ADJ; - // First update the OOM adjustment for each of the - // application processes based on their current state. - int curCachedAdj = CACHED_APP_MIN_ADJ; - int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2); - int curCachedImpAdj = 0; - int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS; - int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2); + if (opt != null && opt.isFreezeExempt()) { + // BIND_WAIVE_PRIORITY and the like get oom_adj 900 + targetAdj += 0; + } else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ) + && (state.getLastStateTime() + + mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) { + // Older cached apps get 950 + targetAdj += 50; + } else { + // Newer cached apps get 910 + targetAdj += 10; + } + state.setCurRawAdj(targetAdj); + state.setCurAdj(psr.modifyRawOomAdj(targetAdj)); + } + } + } else { + // First update the OOM adjustment for each of the + // application processes based on their current state. + int curCachedAdj = CACHED_APP_MIN_ADJ; + int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2); + int curCachedImpAdj = 0; + int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS; + int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2); - final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES; - final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - - emptyProcessLimit; - // Let's determine how many processes we have running vs. - // how many slots we have for background processes; we may want - // to put multiple processes in a slot of there are enough of - // them. - int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs; - if (numEmptyProcs > cachedProcessLimit) { - // If there are more empty processes than our limit on cached - // processes, then use the cached process limit for the factor. - // This ensures that the really old empty processes get pushed - // down to the bottom, so if we are running low on memory we will - // have a better chance at keeping around more cached processes - // instead of a gazillion empty processes. - numEmptyProcs = cachedProcessLimit; - } - int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1) - / mNumSlots; - if (cachedFactor < 1) cachedFactor = 1; + final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES; + final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES + - emptyProcessLimit; + // Let's determine how many processes we have running vs. + // how many slots we have for background processes; we may want + // to put multiple processes in a slot of there are enough of + // them. + int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs; + if (numEmptyProcs > cachedProcessLimit) { + // If there are more empty processes than our limit on cached + // processes, then use the cached process limit for the factor. + // This ensures that the really old empty processes get pushed + // down to the bottom, so if we are running low on memory we will + // have a better chance at keeping around more cached processes + // instead of a gazillion empty processes. + numEmptyProcs = cachedProcessLimit; + } + int cachedFactor = (mNumCachedHiddenProcs > 0 + ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1) + / mNumSlots; + if (cachedFactor < 1) cachedFactor = 1; - int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots; - if (emptyFactor < 1) emptyFactor = 1; + int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots; + if (emptyFactor < 1) emptyFactor = 1; - int stepCached = -1; - int stepEmpty = -1; - int lastCachedGroup = 0; - int lastCachedGroupImportance = 0; - int lastCachedGroupUid = 0; + int stepCached = -1; + int stepEmpty = -1; + int lastCachedGroup = 0; + int lastCachedGroupImportance = 0; + int lastCachedGroupUid = 0; - for (int i = numLru - 1; i >= 0; i--) { - ProcessRecord app = lruList.get(i); - final ProcessStateRecord state = app.mState; - // If we haven't yet assigned the final cached adj - // to the process, do that now. - if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj() + + for (int i = numLru - 1; i >= 0; i--) { + ProcessRecord app = lruList.get(i); + final ProcessStateRecord state = app.mState; + // If we haven't yet assigned the final cached adj + // to the process, do that now. + if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj() >= UNKNOWN_ADJ) { - final ProcessServiceRecord psr = app.mServices; - switch (state.getCurProcState()) { - case PROCESS_STATE_CACHED_ACTIVITY: - case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: - case ActivityManager.PROCESS_STATE_CACHED_RECENT: - // Figure out the next cached level, taking into account groups. - boolean inGroup = false; - final int connectionGroup = psr.getConnectionGroup(); - if (connectionGroup != 0) { - final int connectionImportance = psr.getConnectionImportance(); - if (lastCachedGroupUid == app.uid + final ProcessServiceRecord psr = app.mServices; + switch (state.getCurProcState()) { + case PROCESS_STATE_CACHED_ACTIVITY: + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + case ActivityManager.PROCESS_STATE_CACHED_RECENT: + // Figure out the next cached level, taking into account groups. + boolean inGroup = false; + final int connectionGroup = psr.getConnectionGroup(); + if (connectionGroup != 0) { + final int connectionImportance = psr.getConnectionImportance(); + if (lastCachedGroupUid == app.uid && lastCachedGroup == connectionGroup) { - // This is in the same group as the last process, just tweak - // adjustment by importance. - if (connectionImportance > lastCachedGroupImportance) { - lastCachedGroupImportance = connectionImportance; - if (curCachedAdj < nextCachedAdj + // This is in the same group as the last process, just tweak + // adjustment by importance. + if (connectionImportance > lastCachedGroupImportance) { + lastCachedGroupImportance = connectionImportance; + if (curCachedAdj < nextCachedAdj && curCachedAdj < CACHED_APP_MAX_ADJ) { - curCachedImpAdj++; + curCachedImpAdj++; + } + } + inGroup = true; + } else { + lastCachedGroupUid = app.uid; + lastCachedGroup = connectionGroup; + lastCachedGroupImportance = connectionImportance; + } + } + if (!inGroup && curCachedAdj != nextCachedAdj) { + stepCached++; + curCachedImpAdj = 0; + if (stepCached >= cachedFactor) { + stepCached = 0; + curCachedAdj = nextCachedAdj; + nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2; + if (nextCachedAdj > CACHED_APP_MAX_ADJ) { + nextCachedAdj = CACHED_APP_MAX_ADJ; } } - inGroup = true; - } else { - lastCachedGroupUid = app.uid; - lastCachedGroup = connectionGroup; - lastCachedGroupImportance = connectionImportance; } - } - if (!inGroup && curCachedAdj != nextCachedAdj) { - stepCached++; - curCachedImpAdj = 0; - if (stepCached >= cachedFactor) { - stepCached = 0; - curCachedAdj = nextCachedAdj; - nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2; - if (nextCachedAdj > CACHED_APP_MAX_ADJ) { - nextCachedAdj = CACHED_APP_MAX_ADJ; + // This process is a cached process holding activities... + // assign it the next cached value for that type, and then + // step that cached level. + state.setCurRawAdj(curCachedAdj + curCachedImpAdj); + state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj)); + if (DEBUG_LRU) { + Slog.d(TAG_LRU, "Assigning activity LRU #" + i + + " adj: " + state.getCurAdj() + + " (curCachedAdj=" + curCachedAdj + + " curCachedImpAdj=" + curCachedImpAdj + ")"); + } + break; + default: + // Figure out the next cached level. + if (curEmptyAdj != nextEmptyAdj) { + stepEmpty++; + if (stepEmpty >= emptyFactor) { + stepEmpty = 0; + curEmptyAdj = nextEmptyAdj; + nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2; + if (nextEmptyAdj > CACHED_APP_MAX_ADJ) { + nextEmptyAdj = CACHED_APP_MAX_ADJ; + } } } - } - // This process is a cached process holding activities... - // assign it the next cached value for that type, and then - // step that cached level. - state.setCurRawAdj(curCachedAdj + curCachedImpAdj); - state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj)); - if (DEBUG_LRU) { - Slog.d(TAG_LRU, "Assigning activity LRU #" + i - + " adj: " + state.getCurAdj() - + " (curCachedAdj=" + curCachedAdj - + " curCachedImpAdj=" + curCachedImpAdj + ")"); - } - break; - default: - // Figure out the next cached level. - if (curEmptyAdj != nextEmptyAdj) { - stepEmpty++; - if (stepEmpty >= emptyFactor) { - stepEmpty = 0; - curEmptyAdj = nextEmptyAdj; - nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2; - if (nextEmptyAdj > CACHED_APP_MAX_ADJ) { - nextEmptyAdj = CACHED_APP_MAX_ADJ; - } + // For everything else, assign next empty cached process + // level and bump that up. Note that this means that + // long-running services that have dropped down to the + // cached level will be treated as empty (since their process + // state is still as a service), which is what we want. + state.setCurRawAdj(curEmptyAdj); + state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj)); + if (DEBUG_LRU) { + Slog.d(TAG_LRU, "Assigning empty LRU #" + i + + " adj: " + state.getCurAdj() + + " (curEmptyAdj=" + curEmptyAdj + + ")"); } - } - // For everything else, assign next empty cached process - // level and bump that up. Note that this means that - // long-running services that have dropped down to the - // cached level will be treated as empty (since their process - // state is still as a service), which is what we want. - state.setCurRawAdj(curEmptyAdj); - state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj)); - if (DEBUG_LRU) { - Slog.d(TAG_LRU, "Assigning empty LRU #" + i - + " adj: " + state.getCurAdj() + " (curEmptyAdj=" + curEmptyAdj - + ")"); - } - break; + break; + } } } } } - private long mNextNoKillDebugMessageTime; private double mLastFreeSwapPercent = 1.00; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index dc7f3cdc1f7f5..485ce33dfb7d5 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -143,8 +143,8 @@ public class MockingOomAdjusterTests { private static final String MOCKAPP5_PROCESSNAME = "test #5"; private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5"; private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE; - private static final int FIRST_CACHED_ADJ = ProcessList.CACHED_APP_MIN_ADJ - + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; + private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; private static Context sContext; private static PackageManagerInternal sPackageManagerInternal; private static ActivityManagerService sService; @@ -208,6 +208,9 @@ public class MockingOomAdjusterTests { new ActiveUids(sService, false)); sService.mOomAdjuster.mAdjSeq = 10000; sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); + if (sService.mConstants.USE_TIERED_CACHED_ADJ) { + sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10; + } } @AfterClass @@ -834,7 +837,7 @@ public class MockingOomAdjusterTests { updateOomAdj(client, app); doReturn(null).when(sService).getTopApp(); - assertProcStates(app, PROCESS_STATE_SERVICE, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND); + assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); } @SuppressWarnings("GuardedBy") @@ -882,7 +885,7 @@ public class MockingOomAdjusterTests { sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND); + assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); } @SuppressWarnings("GuardedBy") @@ -1286,7 +1289,7 @@ public class MockingOomAdjusterTests { bindProvider(app, app, null, null, false); updateOomAdj(app); - assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND); + assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); } @SuppressWarnings("GuardedBy") @@ -1301,7 +1304,7 @@ public class MockingOomAdjusterTests { sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client); - assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND); + assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); } @SuppressWarnings("GuardedBy") @@ -2378,8 +2381,12 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); final int userOwner = 0; final int userOther = 1; - final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2; + final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ + ? CACHED_APP_MIN_ADJ + 10 + : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; + final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ + ? CACHED_APP_MIN_ADJ + 10 + : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2; doReturn(userOwner).when(sService.mUserController).getCurrentUserId(); final ArrayList lru = sService.mProcessList.getLruProcessesLOSP();