Rate-limit FGS notification visibility deferral
Apps that transition services in and out of the foreground mode frequently won't see deferred visibility on each transition. There is now a minimum time after any such deferral, during which a new transition to the FGS mode will always show its notification immediately. Bug: 183428469 Test: atest CtsAppTestCases:ServiceTest Change-Id: I90e50357290203adac23ae4508cf72daab2c150a
This commit is contained in:
@@ -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<ServiceRecord> 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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user