diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 5fe87199f1b84..18e0155cfd345 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -650,6 +650,38 @@ final class ActivityManagerConstants extends ContentObserver { // initialized in the constructor. public int CUR_MAX_EMPTY_PROCESSES; + + /** @see #mNoKillCachedProcessesUntilBootCompleted */ + private static final String KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED = + "no_kill_cached_processes_until_boot_completed"; + + /** @see #mNoKillCachedProcessesPostBootCompletedDurationMillis */ + private static final String KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS = + "no_kill_cached_processes_post_boot_completed_duration_millis"; + + /** @see #mNoKillCachedProcessesUntilBootCompleted */ + private static final boolean DEFAULT_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED = false; + + /** @see #mNoKillCachedProcessesPostBootCompletedDurationMillis */ + private static final long + DEFAULT_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS = 0; + + /** + * If true, do not kill excessive cached processes proactively, until user-0 is unlocked. + * @see #mNoKillCachedProcessesPostBootCompletedDurationMillis + */ + volatile boolean mNoKillCachedProcessesUntilBootCompleted = + DEFAULT_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED; + + /** + * Do not kill excessive cached processes proactively, for this duration after each user is + * unlocked. + * Note we don't proactively kill extra cached processes after this. The next oomadjuster pass + * will naturally do it. + */ + volatile long mNoKillCachedProcessesPostBootCompletedDurationMillis = + DEFAULT_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS; + // The number of empty apps at which we don't consider it necessary to do // memory trimming. public int CUR_TRIM_EMPTY_PROCESSES = computeEmptyProcessLimit(MAX_CACHED_PROCESSES) / 2; @@ -659,6 +691,14 @@ final class ActivityManagerConstants extends ContentObserver { public int CUR_TRIM_CACHED_PROCESSES = (MAX_CACHED_PROCESSES - computeEmptyProcessLimit(MAX_CACHED_PROCESSES)) / 3; + /** @see #mNoKillCachedProcessesUntilBootCompleted */ + private static final String KEY_MAX_EMPTY_TIME_MILLIS = + "max_empty_time_millis"; + + private static final long DEFAULT_MAX_EMPTY_TIME_MILLIS = 30 * 60 * 1000; + + volatile long mMaxEmptyTimeMillis = DEFAULT_MAX_EMPTY_TIME_MILLIS; + /** * Packages that can't be killed even if it's requested to be killed on imperceptible. */ @@ -869,6 +909,15 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_DEFER_BOOT_COMPLETED_BROADCAST: updateDeferBootCompletedBroadcast(); break; + case KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED: + updateNoKillCachedProcessesUntilBootCompleted(); + break; + case KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS: + updateNoKillCachedProcessesPostBootCompletedDurationMillis(); + break; + case KEY_MAX_EMPTY_TIME_MILLIS: + updateMaxEmptyTimeMillis(); + break; default: break; } @@ -1288,6 +1337,27 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST); } + private void updateNoKillCachedProcessesUntilBootCompleted() { + mNoKillCachedProcessesUntilBootCompleted = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED, + DEFAULT_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED); + } + + private void updateNoKillCachedProcessesPostBootCompletedDurationMillis() { + mNoKillCachedProcessesPostBootCompletedDurationMillis = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS, + DEFAULT_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS); + } + + private void updateMaxEmptyTimeMillis() { + mMaxEmptyTimeMillis = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_MAX_EMPTY_TIME_MILLIS, + DEFAULT_MAX_EMPTY_TIME_MILLIS); + } + private long[] parseLongArray(@NonNull String key, @NonNull long[] def) { final String val = DeviceConfig.getString(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, key, null); @@ -1562,6 +1632,12 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mComponentAliasOverrides); pw.print(" "); pw.print(KEY_DEFER_BOOT_COMPLETED_BROADCAST); pw.print("="); pw.println(mDeferBootCompletedBroadcast); + pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED); + pw.print("="); pw.println(mNoKillCachedProcessesUntilBootCompleted); + pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS); + pw.print("="); pw.println(mNoKillCachedProcessesPostBootCompletedDurationMillis); + pw.print(" "); pw.print(KEY_MAX_EMPTY_TIME_MILLIS); + pw.print("="); pw.println(mMaxEmptyTimeMillis); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 6a211d31aa498..e0d333ee8f6a3 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -750,7 +750,7 @@ public class OomAdjuster { } final long now = SystemClock.uptimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); - final long oldTime = now - ProcessList.MAX_EMPTY_TIME; + final long oldTime = now - mConstants.mMaxEmptyTimeMillis; final boolean fullUpdate = processes == null; ActiveUids activeUids = uids; ArrayList activeProcesses = fullUpdate ? mProcessList.getLruProcessesLOSP() @@ -1031,15 +1031,25 @@ public class OomAdjuster { } } + private long mNextNoKillDebugMessageTime; + @GuardedBy({"mService", "mProcLock"}) private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed, final long oldTime, final ActiveUids activeUids) { ArrayList lruList = mProcessList.getLruProcessesLOSP(); final int numLru = lruList.size(); - final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES; - final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - - emptyProcessLimit; + final boolean doKillExcessiveProcesses = shouldKillExcessiveProcesses(now); + if (!doKillExcessiveProcesses) { + if (mNextNoKillDebugMessageTime < now) { + Slog.d(TAG, "Not killing cached processes"); // STOPSHIP Remove it b/222365734 + mNextNoKillDebugMessageTime = now + 5000; // Every 5 seconds + } + } + final int emptyProcessLimit = doKillExcessiveProcesses + ? mConstants.CUR_MAX_EMPTY_PROCESSES : Integer.MAX_VALUE; + final int cachedProcessLimit = doKillExcessiveProcesses + ? (mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit) : Integer.MAX_VALUE; int lastCachedGroup = 0; int lastCachedGroupUid = 0; int numCached = 0; @@ -1089,7 +1099,7 @@ public class OomAdjuster { case PROCESS_STATE_CACHED_EMPTY: if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES && app.getLastActivityTime() < oldTime) { - app.killLocked("empty for " + ((oldTime + ProcessList.MAX_EMPTY_TIME + app.killLocked("empty for " + ((now - app.getLastActivityTime()) / 1000) + "s", "empty for too long", ApplicationExitInfo.REASON_OTHER, @@ -1258,6 +1268,25 @@ public class OomAdjuster { } } + /** + * Return true if we should kill excessive cached/empty processes. + */ + private boolean shouldKillExcessiveProcesses(long nowUptime) { + final long lastUserUnlockingUptime = mService.mUserController.getLastUserUnlockingUptime(); + + if (lastUserUnlockingUptime == 0) { + // No users have been unlocked. + return !mConstants.mNoKillCachedProcessesUntilBootCompleted; + } + final long noKillCachedProcessesPostBootCompletedDurationMillis = + mConstants.mNoKillCachedProcessesPostBootCompletedDurationMillis; + if ((lastUserUnlockingUptime + noKillCachedProcessesPostBootCompletedDurationMillis) + > nowUptime) { + return false; + } + return true; + } + private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback = new ComputeOomAdjWindowCallback(); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 636b7f2abe504..6e14203774a10 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -293,9 +293,6 @@ public final class ProcessList { // without empty apps being able to push them out of memory. static final int MIN_CACHED_APPS = 2; - // We allow empty processes to stick around for at most 30 minutes. - static final long MAX_EMPTY_TIME = 30 * 60 * 1000; - // Threshold of number of cached+empty where we consider memory critical. static final int TRIM_CRITICAL_THRESHOLD = 3; diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 1095cf0a0d30c..ad176556ba653 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -399,6 +399,9 @@ class UserController implements Handler.Callback { @GuardedBy("mLock") private @StopUserOnSwitch int mStopUserOnSwitch = STOP_USER_ON_SWITCH_DEFAULT; + /** @see #getLastUserUnlockingUptime */ + private volatile long mLastUserUnlockingUptime = 0; + UserController(ActivityManagerService service) { this(new Injector(service)); } @@ -661,6 +664,8 @@ class UserController implements Handler.Callback { uss.mUnlockProgress.setProgress(20); + mLastUserUnlockingUptime = SystemClock.uptimeMillis(); + // Dispatch unlocked to system services; when fully dispatched, // that calls through to the next "unlocked" phase mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget(); @@ -2753,6 +2758,7 @@ class UserController implements Handler.Callback { if (mSwitchingToSystemUserMessage != null) { pw.println(" mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage); } + pw.println(" mLastUserUnlockingUptime:" + mLastUserUnlockingUptime); } } @@ -3077,6 +3083,14 @@ class UserController implements Handler.Callback { return bOptions; } + /** + * Uptime when any user was being unlocked most recently. 0 if no users have been unlocked + * yet. To avoid lock contention (since it's used by OomAdjuster), it's volatile internally. + */ + public long getLastUserUnlockingUptime() { + return mLastUserUnlockingUptime; + } + /** * Helper class to store user journey and session id. *