diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index d9a49aa523654..53df11ea23742 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -173,6 +173,8 @@ public class PowerExemptionManager { public static final int REASON_ALLOWLISTED_PACKAGE = 65; /** @hide */ public static final int REASON_APPOP = 66; + /** @hide */ + public static final int REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD = 67; /* BG-FGS-launch is allowed by temp-allow-list or system-allow-list. Reason code for temp and system allow list starts here. @@ -328,6 +330,7 @@ public class PowerExemptionManager { REASON_EXEMPTED_PACKAGE, REASON_ALLOWLISTED_PACKAGE, REASON_APPOP, + REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD, // temp and system allow list reasons. REASON_GEOFENCING, REASON_PUSH_MESSAGING, @@ -576,6 +579,8 @@ public class PowerExemptionManager { return "ALLOWLISTED_PACKAGE"; case REASON_APPOP: return "APPOP"; + case REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD: + return "ACTIVITY_VISIBILITY_GRACE_PERIOD"; case REASON_GEOFENCING: return "GEOFENCING"; case REASON_PUSH_MESSAGING: diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 673749c083182..7197cfa53a9ae 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; +import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER; import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE; import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; @@ -5557,6 +5558,14 @@ public final class ActiveServices { && instr.mHasBackgroundForegroundServiceStartsPermission) { return REASON_INSTR_BACKGROUND_FGS_PERMISSION; } + final long lastInvisibleTime = app.mState.getLastInvisibleTime(); + if (lastInvisibleTime > 0 && lastInvisibleTime < Long.MAX_VALUE) { + final long sinceLastInvisible = SystemClock.elapsedRealtime() + - lastInvisibleTime; + if (sinceLastInvisible < mAm.mConstants.mFgToBgFgsGraceDuration) { + return REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; + } + } } } return null; diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index ba8f1906b0e1c..5859cea2d2845 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -95,6 +95,7 @@ final class ActivityManagerConstants extends ContentObserver { "process_crash_count_reset_interval"; static final String KEY_PROCESS_CRASH_COUNT_LIMIT = "process_crash_count_limit"; static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration"; + static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; @@ -133,6 +134,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final int DEFAULT_PROCESS_CRASH_COUNT_RESET_INTERVAL = 12 * 60 * 60 * 1000; private static final int DEFAULT_PROCESS_CRASH_COUNT_LIMIT = 12; private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 10 * 1000; + private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000; // Flag stored in the DeviceConfig API. @@ -388,6 +390,12 @@ final class ActivityManagerConstants extends ContentObserver { */ volatile long mBootTimeTempAllowlistDuration = DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION; + /** + * The grace period in milliseconds to allow a process to start FGS from background after + * switching from foreground to background; currently it's only applicable to its activities. + */ + volatile long mFgToBgFgsGraceDuration = DEFAULT_FG_TO_BG_FGS_GRACE_DURATION; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -575,6 +583,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION: updateBootTimeTempAllowListDuration(); break; + case KEY_FG_TO_BG_FGS_GRACE_DURATION: + updateFgToBgFgsGraceDuration(); + break; default: break; } @@ -851,6 +862,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION); } + private void updateFgToBgFgsGraceDuration() { + mFgToBgFgsGraceDuration = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FG_TO_BG_FGS_GRACE_DURATION, + DEFAULT_FG_TO_BG_FGS_GRACE_DURATION); + } + private void updateImperceptibleKillExemptions() { IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear(); IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages); @@ -1051,6 +1069,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(MAX_PHANTOM_PROCESSES); pw.print(" "); pw.print(KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION); pw.print("="); pw.println(mBootTimeTempAllowlistDuration); + pw.print(" "); pw.print(KEY_FG_TO_BG_FGS_GRACE_DURATION); pw.print("="); + pw.println(mFgToBgFgsGraceDuration); 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 939d35fdb5ef8..5ae65ef0e4bee 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1367,6 +1367,7 @@ public final class OomAdjuster { ProcessRecord app; int adj; boolean foregroundActivities; + boolean mHasVisibleActivities; int procState; int schedGroup; int appUid; @@ -1375,10 +1376,12 @@ public final class OomAdjuster { ProcessStateRecord mState; void initialize(ProcessRecord app, int adj, boolean foregroundActivities, - int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) { + boolean hasVisibleActivities, int procState, int schedGroup, int appUid, + int logUid, int processStateCurTop) { this.app = app; this.adj = adj; this.foregroundActivities = foregroundActivities; + this.mHasVisibleActivities = hasVisibleActivities; this.procState = procState; this.schedGroup = schedGroup; this.appUid = appUid; @@ -1411,6 +1414,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = true; } @Override @@ -1436,6 +1440,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = false; } @Override @@ -1468,6 +1473,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = false; } @Override @@ -1480,6 +1486,7 @@ public final class OomAdjuster { "Raise procstate to cached activity: " + app); } } + mHasVisibleActivities = false; } } @@ -1591,12 +1598,14 @@ public final class OomAdjuster { int capability = 0; boolean foregroundActivities = false; + boolean hasVisibleActivities = false; if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) { // The last app on the list is the foreground app. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_TOP_APP; state.setAdjType("top-activity"); foregroundActivities = true; + hasVisibleActivities = true; procState = PROCESS_STATE_CUR_TOP; state.bumpAllowStartFgsState(PROCESS_STATE_TOP); if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { @@ -1672,11 +1681,12 @@ public final class OomAdjuster { // Examine all activities if not already foreground. if (!foregroundActivities && state.getCachedHasActivities()) { state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback, - adj, foregroundActivities, procState, schedGroup, appUid, logUid, - PROCESS_STATE_CUR_TOP); + adj, foregroundActivities, hasVisibleActivities, procState, schedGroup, + appUid, logUid, PROCESS_STATE_CUR_TOP); adj = state.getCachedAdj(); foregroundActivities = state.getCachedForegroundActivities(); + hasVisibleActivities = state.getCachedHasVisibleActivities(); procState = state.getCachedProcState(); schedGroup = state.getCachedSchedGroup(); } @@ -2450,6 +2460,7 @@ public final class OomAdjuster { state.setCurrentSchedulingGroup(schedGroup); state.setCurProcState(procState); state.setCurRawProcState(procState); + state.updateLastInvisibleTime(hasVisibleActivities); state.setHasForegroundActivities(foregroundActivities); state.setCompletedAdjSeq(mAdjSeq); state.setAllowStartFgs(); diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 6d783fc63901b..d97d343a1960e 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -43,6 +43,7 @@ import static android.os.Process.SYSTEM_UID; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ProcessRecord.TAG; +import android.annotation.ElapsedRealtimeLong; import android.app.ActivityManager; import android.content.ComponentName; import android.os.Binder; @@ -386,6 +387,16 @@ final class ProcessStateRecord { @GuardedBy("mService") private boolean mReachable; + /** + * The most recent time when the last visible activity within this process became invisible. + * + *
It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is + * any visible activities within this process at this moment.
+ */ + @GuardedBy("mService") + @ElapsedRealtimeLong + private long mLastInvisibleTime; + // Below are the cached task info for OomAdjuster only private static final int VALUE_INVALID = -1; private static final int VALUE_FALSE = 0; @@ -1040,18 +1051,19 @@ final class ProcessStateRecord { @GuardedBy("mService") void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback, - int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid, - int logUid, int processCurTop) { + int adj, boolean foregroundActivities, boolean hasVisibleActivities, int procState, + int schedGroup, int appUid, int logUid, int processCurTop) { if (mCachedAdj != ProcessList.INVALID_ADJ) { return; } - callback.initialize(mApp, adj, foregroundActivities, procState, schedGroup, appUid, logUid, - processCurTop); + callback.initialize(mApp, adj, foregroundActivities, hasVisibleActivities, procState, + schedGroup, appUid, logUid, processCurTop); final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX, mApp.getWindowProcessController().computeOomAdjFromActivities(callback)); mCachedAdj = callback.adj; mCachedForegroundActivities = callback.foregroundActivities; + mCachedHasVisibleActivities = callback.mHasVisibleActivities ? VALUE_TRUE : VALUE_FALSE; mCachedProcState = callback.procState; mCachedSchedGroup = callback.schedGroup; @@ -1263,6 +1275,21 @@ final class ProcessStateRecord { return mAllowStartFgs; } + @GuardedBy("mService") + void updateLastInvisibleTime(boolean hasVisibleActivities) { + if (hasVisibleActivities) { + mLastInvisibleTime = Long.MAX_VALUE; + } else if (mLastInvisibleTime == Long.MAX_VALUE) { + mLastInvisibleTime = SystemClock.elapsedRealtime(); + } + } + + @GuardedBy("mService") + @ElapsedRealtimeLong + long getLastInvisibleTime() { + return mLastInvisibleTime; + } + @GuardedBy({"mService", "mProcLock"}) void dump(PrintWriter pw, String prefix, long nowUptime) { if (mReportedInteraction || mFgInteractionTime != 0) { @@ -1340,6 +1367,15 @@ final class ProcessStateRecord { TimeUtils.formatDuration(mLastTopTime, nowUptime, pw); pw.println(); } + if (mLastInvisibleTime > 0 && mLastInvisibleTime < Long.MAX_VALUE) { + pw.print(prefix); pw.print("lastInvisibleTime="); + final long elapsedRealtimeNow = SystemClock.elapsedRealtime(); + final long currentTimeNow = System.currentTimeMillis(); + final long lastInvisibleCurrentTime = + currentTimeNow - elapsedRealtimeNow + mLastInvisibleTime; + TimeUtils.dumpTimeWithDelta(pw, lastInvisibleCurrentTime, currentTimeNow); + pw.println(); + } if (mHasStartedServices) { pw.print(prefix); pw.print("hasStartedServices="); pw.println(mHasStartedServices); }