diff options
| author | 2018-09-05 18:52:35 -0700 | |
|---|---|---|
| committer | 2018-09-14 15:52:40 -0700 | |
| commit | e5bfeee745df5d411cc624fdf600721bc54b28a7 (patch) | |
| tree | 73faef3cbc79543973bc5e6b389e38f9780a2ebf | |
| parent | 2518b2899ca62f2135adbd1fc512fe34e9ef6b93 (diff) | |
Keep app in high memory adjust if moving from TOP to FGS
If an app was in the TOP state and immediately moved
to a foreground service state, then try harder to keep it
in memory for another 20 seconds before releasing the
memory to bound foreground services. Using an oom_adj value
of 50 which is between "fore" and "vis". So BFGS apps might
get killed ("vis") before this recently FGS'ed app. That
way any app that has a lot of state in memory that it needs
to persist before getting killed has a chance to do so.
Also bind NotificationListeners with a special bind flag
to rank them below FGS (perceptible adj) so that they
can be killed before any other foreground services or
bound services get killed.
Bug: 110969183
Test: Manual for now. Take a bunch of portrait
pictures and hit home, while being under memory
pressure with a bunch of notification listeners.
Change-Id: Ie8934c2331afe6450c582b209869aeca7272f58a
7 files changed, 77 insertions, 3 deletions
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ddd12a546650..35deb0886c3d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -327,6 +327,15 @@ public abstract class Context { public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080; /** + * Flag for {@link #bindService}: If binding from something better than perceptible, + * still set the adjust below perceptible. This would be used for bound services that can + * afford to be evicted when under extreme memory pressure, but should be restarted as soon + * as possible. + * @hide + */ + public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x0100; + + /** * @hide Flag for {@link #bindService}: allows binding to a service provided * by an instant app. Note that the caller may not have access to the instant * app providing the service which is a violation of the instant app sandbox. diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 2c8f2fcd8af1..3a0289cbfd63 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -68,6 +68,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_BOUND_SERVICE_CRASH_MAX_RETRY = "service_crash_max_retry"; static final String KEY_PROCESS_START_ASYNC = "process_start_async"; static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time"; + static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; @@ -97,6 +98,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final int DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY = 16; private static final boolean DEFAULT_PROCESS_START_ASYNC = true; private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; + private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; // Maximum number of cached processes we will allow. public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES; @@ -212,6 +214,10 @@ final class ActivityManagerConstants extends ContentObserver { // throttle requests from apps. public long MEMORY_INFO_THROTTLE_TIME = DEFAULT_MEMORY_INFO_THROTTLE_TIME; + // Allow app just moving from TOP to FOREGROUND_SERVICE to stay in a higher adj value for + // this long. + public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION; + // Indicates whether the activity starts logging is enabled. // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED boolean mFlagActivityStartsLoggingEnabled; @@ -355,6 +361,8 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_PROCESS_START_ASYNC); MEMORY_INFO_THROTTLE_TIME = mParser.getLong(KEY_MEMORY_INFO_THROTTLE_TIME, DEFAULT_MEMORY_INFO_THROTTLE_TIME); + TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION, + DEFAULT_TOP_TO_FGS_GRACE_DURATION); updateMaxCachedProcesses(); } @@ -438,6 +446,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(FLAG_PROCESS_START_ASYNC); pw.print(" "); pw.print(KEY_MEMORY_INFO_THROTTLE_TIME); pw.print("="); pw.println(MEMORY_INFO_THROTTLE_TIME); + pw.print(" "); pw.print(KEY_TOP_TO_FGS_GRACE_DURATION); pw.print("="); + pw.println(TOP_TO_FGS_GRACE_DURATION); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2ee598fee535..104524d6b18f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17776,6 +17776,19 @@ public class ActivityManagerService extends IActivityManager.Stub } } + // If the app was recently in the foreground and moved to a foreground service status, + // allow it to get a higher rank in memory for some time, compared to other foreground + // services so that it can finish performing any persistence/processing of in-memory state. + if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now + || app.setProcState <= ActivityManager.PROCESS_STATE_TOP)) { + adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ; + app.adjType = "fg-service-act"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app); + } + } + if (adj > ProcessList.PERCEPTIBLE_APP_ADJ || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) { if (app.forcingToImportant != null) { @@ -18043,6 +18056,10 @@ public class ActivityManagerService extends IActivityManager.Stub cr.trackProcState(procState, mAdjSeq, now); trackedProcState = true; } + } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0 + && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ + && adj > ProcessList.PERCEPTIBLE_APP_ADJ + 1) { + newAdj = ProcessList.PERCEPTIBLE_APP_ADJ + 1; } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ && adj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -19042,6 +19059,8 @@ public class ActivityManagerService extends IActivityManager.Stub // Must be called before updating setProcState maybeUpdateUsageStatsLocked(app, nowElapsed); + maybeUpdateLastTopTime(app, now); + app.setProcState = app.curProcState; if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { app.notCachedSinceIdle = false; @@ -19266,6 +19285,13 @@ public class ActivityManagerService extends IActivityManager.Stub } } + private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) { + if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP + && app.curProcState > ActivityManager.PROCESS_STATE_TOP) { + app.lastTopTime = nowUptime; + } + } + private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { if (proc.thread != null && proc.baseProcessTracker != null) { proc.baseProcessTracker.setState( diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 784d62e51966..3ac7885eba37 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -104,6 +104,11 @@ public final class ProcessList { static final int VISIBLE_APP_ADJ = 100; static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1; + // This is a process that was recently TOP and moved to FGS. Continue to treat it almost + // like a foreground app for a while. + // @see TOP_TO_FGS_GRACE_PERIOD + static final int PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50; + // This is the process running the current foreground app. We'd really // rather not kill it! static final int FOREGROUND_APP_ADJ = 0; diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 144f18b219dc..d3dc0f34cbf1 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -198,6 +198,7 @@ final class ProcessRecord implements WindowProcessListener { long lastRequestedGc; // When we last asked the app to do a gc long lastLowMemory; // When we last told the app that memory is low long lastProviderTime; // The last time someone else was using a provider in this process. + long lastTopTime; // The last time the process was in the TOP state or greater. boolean reportLowMemory; // Set to true when waiting to report low mem boolean empty; // Is this an empty background process? boolean cached; // Is this a cached process? @@ -415,6 +416,11 @@ final class ProcessRecord implements WindowProcessListener { TimeUtils.formatDuration(lastProviderTime, nowUptime, pw); pw.println(); } + if (lastTopTime > 0) { + pw.print(prefix); pw.print("lastTopTime="); + TimeUtils.formatDuration(lastTopTime, nowUptime, pw); + pw.println(); + } if (hasStartedServices) { pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices); } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 340ae0ad637a..fc51fb090d83 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -178,6 +178,10 @@ abstract public class ManagedServices { } } + protected int getBindFlags() { + return BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT; + } + protected void onServiceRemovedLocked(ManagedServiceInfo removed) { } private ManagedServiceInfo newServiceInfo(IInterface service, @@ -1063,9 +1067,9 @@ abstract public class ManagedServices { } }; if (!mContext.bindServiceAsUser(intent, - serviceConnection, - BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT, - new UserHandle(userid))) { + serviceConnection, + getBindFlags(), + new UserHandle(userid))) { mServicesBound.remove(servicesBindingTag); Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent); return; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ce71dd2ec9ad..608e403ff511 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -33,6 +33,10 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; +import static android.content.Context.BIND_ADJUST_BELOW_PERCEPTIBLE; +import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT; +import static android.content.Context.BIND_AUTO_CREATE; +import static android.content.Context.BIND_FOREGROUND_SERVICE; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -6689,6 +6693,16 @@ public class NotificationManagerService extends SystemService { } @Override + protected int getBindFlags() { + // Most of the same flags as the base, but also add BIND_ADJUST_BELOW_PERCEPTIBLE + // because too many 3P apps could be kept in memory as notification listeners and + // cause extreme memory pressure. + // TODO: Change the binding lifecycle of NotificationListeners to avoid this situation. + return BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE + | BIND_ADJUST_BELOW_PERCEPTIBLE | BIND_ALLOW_WHITELIST_MANAGEMENT; + } + + @Override protected Config getConfig() { Config c = new Config(); c.caption = "notification listener"; |