diff options
6 files changed, 216 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 2643bed44e2e..2c0514d7d0aa 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2894,6 +2894,12 @@ public final class ActiveServices { s.isNotAppComponentUsage = true; } + if (s.app != null && s.app.mState != null + && s.app.mState.getCurProcState() <= PROCESS_STATE_TOP + && (flags & Context.BIND_ALMOST_PERCEPTIBLE) != 0) { + s.lastTopAlmostPerceptibleBindRequestUptimeMs = SystemClock.uptimeMillis(); + } + if (s.app != null) { updateServiceClientActivitiesLocked(s.app.mServices, c, true); } @@ -4798,6 +4804,10 @@ public final class ActiveServices { if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.updateIsAllowedBgActivityStartsByBinding(); } + // And for almost perceptible exceptions. + if ((c.flags & Context.BIND_ALMOST_PERCEPTIBLE) != 0) { + psr.updateHasTopStartedAlmostPerceptibleServices(); + } if (s.app != null) { updateServiceClientActivitiesLocked(s.app.mServices, c, true); } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 39a670ea7dbe..af9e410db6eb 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -108,6 +108,8 @@ final class ActivityManagerConstants extends ContentObserver { 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"; + static final String KEY_TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION = + "top_to_almost_perceptible_grace_duration"; static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold"; static final String KEY_MIN_CRASH_INTERVAL = "min_crash_interval"; static final String KEY_PROCESS_CRASH_COUNT_RESET_INTERVAL = @@ -170,6 +172,7 @@ final class ActivityManagerConstants extends ContentObserver { 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; + private static final long DEFAULT_TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION = 15 * 1000; private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000; private static final int DEFAULT_MIN_CRASH_INTERVAL = 2 * 60 * 1000; private static final int DEFAULT_MAX_PHANTOM_PROCESSES = 32; @@ -223,6 +226,8 @@ final class ActivityManagerConstants extends ContentObserver { private static final int DEFAULT_SERVICE_START_FOREGROUND_ANR_DELAY_MS = 10 * 1000; + private static final long DEFAULT_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS = 15 * 1000; + // Flag stored in the DeviceConfig API. /** * Maximum number of cached processes. @@ -321,6 +326,9 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_SERVICE_START_FOREGROUND_ANR_DELAY_MS = "service_start_foreground_anr_delay_ms"; + private static final String KEY_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS = + "service_bind_almost_perceptible_timeout_ms"; + // Maximum number of cached processes we will allow. public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES; @@ -464,6 +472,13 @@ final class ActivityManagerConstants extends ContentObserver { public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION; /** + * Allow app just leaving TOP with an already running ALMOST_PERCEPTIBLE service to stay in + * a higher adj value for this long. + */ + public long TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION = + DEFAULT_TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION; + + /** * The minimum time we allow between crashes, for us to consider this * application to be bad and stop its services and reject broadcasts. * A reasonable interval here would be anything between 1-3 minutes. @@ -654,6 +669,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_SERVICE_START_FOREGROUND_ANR_DELAY_MS; /** + * How long the grace period is from starting an almost perceptible service to a successful + * binding before we stop considering it an almost perceptible service. + */ + volatile long mServiceBindAlmostPerceptibleTimeoutMs = + DEFAULT_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS; + + /** * Defines component aliases. Format * ComponentName ":" ComponentName ( "," ComponentName ":" ComponentName )* */ @@ -942,6 +964,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_SERVICE_START_FOREGROUND_ANR_DELAY_MS: updateServiceStartForegroundAnrDealyMs(); break; + case KEY_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS: + updateServiceBindAlmostPerceptibleTimeoutMs(); + break; case KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED: updateNoKillCachedProcessesUntilBootCompleted(); break; @@ -1170,6 +1195,9 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_MEMORY_INFO_THROTTLE_TIME); TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION, DEFAULT_TOP_TO_FGS_GRACE_DURATION); + TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION = mParser.getDurationMillis( + KEY_TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION, + DEFAULT_TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION); MIN_CRASH_INTERVAL = mParser.getInt(KEY_MIN_CRASH_INTERVAL, DEFAULT_MIN_CRASH_INTERVAL); PENDINGINTENT_WARNING_THRESHOLD = mParser.getInt(KEY_PENDINGINTENT_WARNING_THRESHOLD, @@ -1432,6 +1460,14 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_SERVICE_START_FOREGROUND_ANR_DELAY_MS); } + private void updateServiceBindAlmostPerceptibleTimeoutMs() { + mServiceBindAlmostPerceptibleTimeoutMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS, + DEFAULT_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS); + } + + private long[] parseLongArray(@NonNull String key, @NonNull long[] def) { final String val = DeviceConfig.getString(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, key, null); @@ -1647,6 +1683,8 @@ final class ActivityManagerConstants extends ContentObserver { 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.print(" "); pw.print(KEY_TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION); pw.print("="); + pw.println(TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION); pw.print(" "); pw.print(KEY_MIN_CRASH_INTERVAL); pw.print("="); pw.println(MIN_CRASH_INTERVAL); pw.print(" "); pw.print(KEY_PROCESS_CRASH_COUNT_RESET_INTERVAL); pw.print("="); @@ -1716,6 +1754,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mServiceStartForegroundTimeoutMs); pw.print(" "); pw.print(KEY_SERVICE_START_FOREGROUND_ANR_DELAY_MS); pw.print("="); pw.println(mServiceStartForegroundAnrDelayMs); + pw.print(" "); pw.print(KEY_SERVICE_BIND_ALMOST_PERCEPTIBLE_TIMEOUT_MS); + pw.print("="); pw.println(mServiceBindAlmostPerceptibleTimeoutMs); 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 d6351527de6d..02206ffc7bef 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1674,6 +1674,24 @@ public class OomAdjuster { } } + // If the app was recently in the foreground and has expedited jobs running, + // allow it to get a higher rank in memory for some time, compared to other EJS and even + // foreground services so that it can finish performing any persistence/processing of + // in-memory state. + if (psr.hasTopStartedAlmostPerceptibleServices() + && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + && (state.getLastTopTime() + + mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION > now + || state.getSetProcState() <= PROCESS_STATE_TOP)) { + adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ; + // This shall henceforth be called the "EJ" exemption, despite utilizing the + // ALMOST_PERCEPTIBLE flag to work. + state.setAdjType("top-ej-act"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app); + } + } + if (adj > ProcessList.PERCEPTIBLE_APP_ADJ || procState > PROCESS_STATE_TRANSIENT_BACKGROUND) { if (state.getForcingToImportant() != null) { diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java index 8f77b87f5308..6b748193c123 100644 --- a/services/core/java/com/android/server/am/ProcessServiceRecord.java +++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java @@ -19,6 +19,7 @@ package com.android.server.am; import android.app.ActivityManager; import android.content.Context; import android.os.IBinder; +import android.os.SystemClock; import android.util.ArrayMap; import android.util.ArraySet; @@ -42,6 +43,18 @@ final class ProcessServiceRecord { private boolean mHasForegroundServices; /** + * Running any services that are almost perceptible (started with + * {@link Context#BIND_ALMOST_PERCEPTIBLE} while the app was on TOP)? + */ + private boolean mHasTopStartedAlmostPerceptibleServices; + + /** + * The latest value of {@link ServiceRecord#lastTopAlmostPerceptibleBindRequestUptimeMs} among + * the currently running services. + */ + private long mLastTopStartedAlmostPerceptibleBindRequestUptimeMs; + + /** * Service that applied current connectionGroup/Importance. */ private ServiceRecord mConnectionService; @@ -146,6 +159,46 @@ final class ProcessServiceRecord { mRepFgServiceTypes = foregroundServiceTypes; } + void updateHasTopStartedAlmostPerceptibleServices() { + mHasTopStartedAlmostPerceptibleServices = false; + mLastTopStartedAlmostPerceptibleBindRequestUptimeMs = 0; + for (int s = mServices.size() - 1; s >= 0; --s) { + final ServiceRecord sr = mServices.valueAt(s); + mLastTopStartedAlmostPerceptibleBindRequestUptimeMs = Math.max( + mLastTopStartedAlmostPerceptibleBindRequestUptimeMs, + sr.lastTopAlmostPerceptibleBindRequestUptimeMs); + if (!mHasTopStartedAlmostPerceptibleServices && isAlmostPerceptible(sr)) { + mHasTopStartedAlmostPerceptibleServices = true; + } + } + } + + private boolean isAlmostPerceptible(ServiceRecord record) { + if (record.lastTopAlmostPerceptibleBindRequestUptimeMs <= 0) { + return false; + } + final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = + record.getConnections(); + for (int m = serviceConnections.size() - 1; m >= 0; --m) { + final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(m); + + for (int c = clist.size() - 1; c >= 0; --c) { + final ConnectionRecord cr = clist.get(c); + if ((cr.flags & Context.BIND_ALMOST_PERCEPTIBLE) != 0) { + return true; + } + } + } + return false; + } + + boolean hasTopStartedAlmostPerceptibleServices() { + return mHasTopStartedAlmostPerceptibleServices + || (mLastTopStartedAlmostPerceptibleBindRequestUptimeMs > 0 + && SystemClock.uptimeMillis() - mLastTopStartedAlmostPerceptibleBindRequestUptimeMs + < mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs); + } + ServiceRecord getConnectionService() { return mConnectionService; } @@ -243,6 +296,14 @@ final class ProcessServiceRecord { if (added && record.serviceInfo != null) { mApp.getWindowProcessController().onServiceStarted(record.serviceInfo); } + if (record.lastTopAlmostPerceptibleBindRequestUptimeMs > 0) { + mLastTopStartedAlmostPerceptibleBindRequestUptimeMs = Math.max( + mLastTopStartedAlmostPerceptibleBindRequestUptimeMs, + record.lastTopAlmostPerceptibleBindRequestUptimeMs); + if (!mHasTopStartedAlmostPerceptibleServices) { + mHasTopStartedAlmostPerceptibleServices = isAlmostPerceptible(record); + } + } return added; } @@ -253,7 +314,11 @@ final class ProcessServiceRecord { * @return true if the service was removed, false otherwise. */ boolean stopService(ServiceRecord record) { - return mServices.remove(record); + final boolean removed = mServices.remove(record); + if (record.lastTopAlmostPerceptibleBindRequestUptimeMs > 0) { + updateHasTopStartedAlmostPerceptibleServices(); + } + return removed; } /** @@ -261,6 +326,7 @@ final class ProcessServiceRecord { */ void stopAllServices() { mServices.clear(); + updateHasTopStartedAlmostPerceptibleServices(); } /** @@ -408,6 +474,13 @@ final class ProcessServiceRecord { pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices); pw.print(" forcingToImportant="); pw.println(mApp.mState.getForcingToImportant()); } + if (mHasTopStartedAlmostPerceptibleServices + || mLastTopStartedAlmostPerceptibleBindRequestUptimeMs > 0) { + pw.print(prefix); pw.print("mHasTopStartedAlmostPerceptibleServices="); + pw.print(mHasTopStartedAlmostPerceptibleServices); + pw.print(" mLastTopStartedAlmostPerceptibleBindRequestUptimeMs="); + pw.println(mLastTopStartedAlmostPerceptibleBindRequestUptimeMs); + } if (mHasClientActivities || mHasAboveClient || mTreatLikeActivity) { pw.print(prefix); pw.print("hasClientActivities="); pw.print(mHasClientActivities); pw.print(" hasAboveClient="); pw.print(mHasAboveClient); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 795311f019a4..639f56cb12dd 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -24,6 +24,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.IApplicationThread; import android.app.Notification; @@ -141,6 +142,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN int pendingConnectionGroup; // To be filled in to ProcessRecord once it connects int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects + /** + * The last time (in uptime timebase) a bind request was made with BIND_ALMOST_PERCEPTIBLE for + * this service while on TOP. + */ + long lastTopAlmostPerceptibleBindRequestUptimeMs; + // any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag? private boolean mIsAllowedBgActivityStartsByBinding; // is this service currently allowed to start activities from background by providing @@ -713,6 +720,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } + @NonNull ArrayMap<IBinder, ArrayList<ConnectionRecord>> getConnections() { return connections; } 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 50a0a687603c..c747a5fd982b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -63,6 +63,7 @@ import static com.android.server.am.ProcessList.UNKNOWN_ADJ; import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalAnswers.answer; import static org.mockito.Mockito.any; @@ -444,7 +445,7 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test - public void testUpdateOomAdj_DoOne_PerceptibleRecent() { + public void testUpdateOomAdj_DoOne_PerceptibleRecent_FgService() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, 0); @@ -458,6 +459,70 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_DoOne_PerceptibleRecent_AlmostPerceptibleService() { + // Grace period allows the adjustment. + { + ProcessRecord system = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true)); + long nowUptime = SystemClock.uptimeMillis(); + app.mState.setLastTopTime(nowUptime); + // Simulate the system starting and binding to a service in the app. + ServiceRecord s = bindService(app, system, + null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime; + s.getConnections().clear(); + app.mServices.updateHasTopStartedAlmostPerceptibleServices(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE); + + assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, app.mState.getSetAdj()); + } + + // Out of grace period but valid binding allows the adjustment. + { + ProcessRecord system = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true)); + long nowUptime = SystemClock.uptimeMillis(); + app.mState.setLastTopTime(nowUptime); + // Simulate the system starting and binding to a service in the app. + ServiceRecord s = bindService(app, system, + null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + s.lastTopAlmostPerceptibleBindRequestUptimeMs = + nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + app.mServices.updateHasTopStartedAlmostPerceptibleServices(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE); + + assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, app.mState.getSetAdj()); + } + + // Out of grace period and no valid binding so no adjustment. + { + ProcessRecord system = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true)); + long nowUptime = SystemClock.uptimeMillis(); + app.mState.setLastTopTime(nowUptime); + // Simulate the system starting and binding to a service in the app. + ServiceRecord s = bindService(app, system, + null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + s.lastTopAlmostPerceptibleBindRequestUptimeMs = + nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + s.getConnections().clear(); + app.mServices.updateHasTopStartedAlmostPerceptibleServices(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE); + + assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, app.mState.getSetAdj()); + } + } + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoOne_Toast() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); |