diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7f8d944cf6681..6809ad4ea378f 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -131,6 +131,7 @@ import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.util.SparseLongArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.webkit.WebViewZygote; @@ -224,6 +225,11 @@ public final class ActiveServices { */ final ArrayList mPendingFgsNotifications = new ArrayList<>(); + /** + * Uptime at which a given uid becomes eliglible again for FGS notification deferral + */ + final SparseLongArray mFgsDeferralEligible = new SparseLongArray(); + /** * Map of services that are asked to be brought up (start/binding) but not ready to. */ @@ -1965,11 +1971,30 @@ public final class ActiveServices { } } + private boolean withinFgsDeferRateLimit(final int uid, final long now) { + final long eligible = mFgsDeferralEligible.get(uid, 0L); + if (DEBUG_FOREGROUND_SERVICE) { + if (now < eligible) { + Slog.d(TAG_SERVICE, "FGS transition for uid " + uid + + " within rate limit, showing immediately"); + } + } + return now < eligible; + } + // TODO: remove as part of fixing b/173627642 @SuppressWarnings("AndroidFrameworkCompatChange") private void postFgsNotificationLocked(ServiceRecord r) { + final int uid = r.appInfo.uid; + final long now = SystemClock.uptimeMillis(); final boolean isLegacyApp = (r.appInfo.targetSdkVersion < Build.VERSION_CODES.S); - boolean showNow = !mAm.mConstants.mFlagFgsNotificationDeferralEnabled; + + boolean showNow = withinFgsDeferRateLimit(uid, now); + if (!showNow) { + final boolean showLegacyNow = isLegacyApp + && mAm.mConstants.mFlagFgsNotificationDeferralApiGated; + showNow = !mAm.mConstants.mFlagFgsNotificationDeferralEnabled || showLegacyNow; + } if (!showNow) { // Legacy apps' FGS notifications are not deferred unless the relevant // DeviceConfig element has been set @@ -2014,8 +2039,6 @@ public final class ActiveServices { } // schedule the actual notification post - final int uid = r.appInfo.uid; - final long now = SystemClock.uptimeMillis(); long when = now + mAm.mConstants.mFgsNotificationDeferralInterval; // If there are already deferred FGS notifications for this app, // inherit that deferred-show timestamp @@ -2033,6 +2056,9 @@ public final class ActiveServices { when = Math.min(when, pending.fgDisplayTime); } } + + final long nextEligible = when + mAm.mConstants.mFgsNotificationDeferralExclusionTime; + mFgsDeferralEligible.put(uid, nextEligible); r.fgDisplayTime = when; mPendingFgsNotifications.add(r); if (DEBUG_FOREGROUND_SERVICE) { diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index e16f4c9905e8c..c8363dd590058 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -199,6 +199,15 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_DEFERRED_FGS_NOTIFICATION_INTERVAL = "deferred_fgs_notification_interval"; + /** + * Time in milliseconds; once an FGS notification for a given uid has been + * deferred, no subsequent FGS notification from that uid will be deferred + * until this amount of time has passed. Default is two minutes + * (2 * 60 * 1000) unless overridden. + */ + private static final String KEY_DEFERRED_FGS_NOTIFICATION_EXCLUSION_TIME = + "deferred_fgs_notification_exclusion_time"; + // Maximum number of cached processes we will allow. public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES; @@ -398,6 +407,10 @@ final class ActivityManagerConstants extends ContentObserver { // the foreground state. volatile long mFgsNotificationDeferralInterval = 10_000; + // Rate limit: minimum time after an app's FGS notification is deferred + // before another FGS notifiction from that app can be deferred. + volatile long mFgsNotificationDeferralExclusionTime = 2 * 60 * 1000L; + /* * At boot time, broadcast receiver ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED and * ACTION_PRE_BOOT_COMPLETED are temp allowlisted to start FGS for a duration of time in @@ -580,6 +593,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_DEFERRED_FGS_NOTIFICATION_INTERVAL: updateFgsNotificationDeferralInterval(); break; + case KEY_DEFERRED_FGS_NOTIFICATION_EXCLUSION_TIME: + updateFgsNotificationDeferralExclusionTime(); + break; case KEY_OOMADJ_UPDATE_POLICY: updateOomAdjUpdatePolicy(); break; @@ -874,6 +890,13 @@ final class ActivityManagerConstants extends ContentObserver { /*default value*/ 10_000L); } + private void updateFgsNotificationDeferralExclusionTime() { + mFgsNotificationDeferralExclusionTime = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_DEFERRED_FGS_NOTIFICATION_EXCLUSION_TIME, + /*default value*/ 2 * 60 * 1000L); + } + private void updateOomAdjUpdatePolicy() { OOMADJ_UPDATE_QUICK = DeviceConfig.getInt( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,