From 6177962389ee9c7954dc0687e77785b949361110 Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Tue, 2 Mar 2021 13:46:26 -0800 Subject: [PATCH] Separating quota window for pre-S apps Decoupling the window gives us more flexibility to change the quotas for pre-S apps. It also allows us to restore the pre-S behavior so we do not break any apps unexpectedly. Test: atest CtsAlarmManagerTestCases atest FrameworksMockingServicesTests:com.android.server.alarm Bug: 171306433 Change-Id: I8ee190c23a078db50aa7c98ce6f56f8ae601c01b --- .../server/alarm/AlarmManagerService.java | 98 +++++++++++++------ .../server/alarm/AlarmManagerServiceTest.java | 33 ++++++- 2 files changed, 98 insertions(+), 33 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 2b2918c0a6f01..33f6e0651abca 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -24,6 +24,7 @@ import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; import static android.app.AlarmManager.FLAG_IDLE_UNTIL; import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; +import static android.app.AlarmManager.INTERVAL_DAY; import static android.app.AlarmManager.INTERVAL_HOUR; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; @@ -88,7 +89,6 @@ import android.provider.Settings; import android.system.Os; import android.text.TextUtils; import android.text.format.DateFormat; -import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; @@ -168,8 +168,7 @@ public class AlarmManagerService extends SystemService { static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; static final int TICK_HISTORY_DEPTH = 10; - static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000; - static final long INDEFINITE_DELAY = 365 * MILLIS_IN_DAY; + static final long INDEFINITE_DELAY = 365 * INTERVAL_DAY; // Indices into the KEYS_APP_STANDBY_QUOTAS array. static final int ACTIVE_INDEX = 0; @@ -423,13 +422,17 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota"; - private static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window"; + + @VisibleForTesting + static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window"; + @VisibleForTesting + static final String KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW = "allow_while_idle_compat_window"; private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps"; private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; - private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS; + private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY; private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000; private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000; private static final int DEFAULT_MAX_ALARMS_PER_UID = 500; @@ -445,19 +448,21 @@ public class AlarmManagerService extends SystemService { 0 // Never }; private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1; - private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY; + private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = INTERVAL_DAY; private static final boolean DEFAULT_LAZY_BATCHING = true; private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true; /** - * Default quota for pre-S apps. Enough to accommodate the existing policy of an alarm + * Default quota for pre-S apps. The same as allowing an alarm slot once * every ALLOW_WHILE_IDLE_LONG_DELAY, which was 9 minutes. */ - private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 7; + private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 1; private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72; private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour. + private static final long DEFAULT_ALLOW_WHILE_IDLE_COMPAT_WINDOW = 9 * 60 * 1000; // 9 mins. + // TODO (b/171306433): Change to true by default. private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false; @@ -495,9 +500,14 @@ public class AlarmManagerService extends SystemService { public int ALLOW_WHILE_IDLE_COMPAT_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA; /** - * The window used for enforcing {@link #ALLOW_WHILE_IDLE_QUOTA} and - * {@link #ALLOW_WHILE_IDLE_COMPAT_QUOTA}. Can be configured, but only recommended for - * testing. + * The window used for enforcing {@link #ALLOW_WHILE_IDLE_COMPAT_QUOTA}. + * Can be configured, but only recommended for testing. + */ + public long ALLOW_WHILE_IDLE_COMPAT_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_COMPAT_WINDOW; + + /** + * The window used for enforcing {@link #ALLOW_WHILE_IDLE_COMPAT_QUOTA}. + * Can be configured, but only recommended for testing. */ public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW; @@ -561,7 +571,7 @@ public class AlarmManagerService extends SystemService { ALLOW_WHILE_IDLE_QUOTA = properties.getInt(KEY_ALLOW_WHILE_IDLE_QUOTA, DEFAULT_ALLOW_WHILE_IDLE_QUOTA); if (ALLOW_WHILE_IDLE_QUOTA <= 0) { - Slog.w(TAG, "Cannot have allow-while-idle quota lower than 1."); + Slog.w(TAG, "Must have positive allow_while_idle quota"); ALLOW_WHILE_IDLE_QUOTA = 1; } break; @@ -570,22 +580,38 @@ public class AlarmManagerService extends SystemService { KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA); if (ALLOW_WHILE_IDLE_COMPAT_QUOTA <= 0) { - Slog.w(TAG, "Cannot have quota lower than 1."); + Slog.w(TAG, "Must have positive allow_while_idle_compat quota"); ALLOW_WHILE_IDLE_COMPAT_QUOTA = 1; } break; case KEY_ALLOW_WHILE_IDLE_WINDOW: ALLOW_WHILE_IDLE_WINDOW = properties.getLong( KEY_ALLOW_WHILE_IDLE_WINDOW, DEFAULT_ALLOW_WHILE_IDLE_WINDOW); - if (ALLOW_WHILE_IDLE_WINDOW > DEFAULT_ALLOW_WHILE_IDLE_WINDOW) { + + if (ALLOW_WHILE_IDLE_WINDOW > INTERVAL_HOUR) { Slog.w(TAG, "Cannot have allow_while_idle_window > " - + DEFAULT_ALLOW_WHILE_IDLE_WINDOW); - ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW; - } else if (ALLOW_WHILE_IDLE_WINDOW < DEFAULT_ALLOW_WHILE_IDLE_WINDOW) { + + INTERVAL_HOUR); + ALLOW_WHILE_IDLE_WINDOW = INTERVAL_HOUR; + } else if (ALLOW_WHILE_IDLE_WINDOW != DEFAULT_ALLOW_WHILE_IDLE_WINDOW) { Slog.w(TAG, "Using a non-default allow_while_idle_window = " + ALLOW_WHILE_IDLE_WINDOW); } break; + case KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW: + ALLOW_WHILE_IDLE_COMPAT_WINDOW = properties.getLong( + KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW, + DEFAULT_ALLOW_WHILE_IDLE_COMPAT_WINDOW); + + if (ALLOW_WHILE_IDLE_COMPAT_WINDOW > INTERVAL_HOUR) { + Slog.w(TAG, "Cannot have allow_while_idle_compat_window > " + + INTERVAL_HOUR); + ALLOW_WHILE_IDLE_COMPAT_WINDOW = INTERVAL_HOUR; + } else if (ALLOW_WHILE_IDLE_COMPAT_WINDOW + != DEFAULT_ALLOW_WHILE_IDLE_COMPAT_WINDOW) { + Slog.w(TAG, "Using a non-default allow_while_idle_compat_window = " + + ALLOW_WHILE_IDLE_COMPAT_WINDOW); + } + break; case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION: ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong( KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, @@ -717,6 +743,9 @@ public class AlarmManagerService extends SystemService { TimeUtils.formatDuration(LISTENER_TIMEOUT, pw); pw.println(); + pw.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA); + pw.println(); + pw.print(KEY_ALLOW_WHILE_IDLE_WINDOW); pw.print("="); TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw); @@ -725,7 +754,9 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, ALLOW_WHILE_IDLE_COMPAT_QUOTA); pw.println(); - pw.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA); + pw.print(KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW); + pw.print("="); + TimeUtils.formatDuration(ALLOW_WHILE_IDLE_COMPAT_WINDOW, pw); pw.println(); pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION); @@ -1330,8 +1361,7 @@ public class AlarmManagerService extends SystemService { mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater); mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW); - mAllowWhileIdleHistory = new AppWakeupHistory( - Constants.DEFAULT_ALLOW_WHILE_IDLE_WINDOW); + mAllowWhileIdleHistory = new AppWakeupHistory(INTERVAL_HOUR); mNextWakeup = mNextNonWakeup = 0; @@ -1730,9 +1760,15 @@ public class AlarmManagerService extends SystemService { } else if (isAllowedWhileIdleRestricted(alarm)) { // Allowed but limited. final int userId = UserHandle.getUserId(alarm.creatorUid); - final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) - ? mConstants.ALLOW_WHILE_IDLE_QUOTA - : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA; + final int quota; + final long window; + if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) { + quota = mConstants.ALLOW_WHILE_IDLE_QUOTA; + window = mConstants.ALLOW_WHILE_IDLE_WINDOW; + } else { + quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA; + window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW; + } final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow( alarm.sourcePackage, userId); if (dispatchesInWindow < quota) { @@ -1740,7 +1776,7 @@ public class AlarmManagerService extends SystemService { batterySaverPolicyElapsed = nowElapsed; } else { batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage( - alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW; + alarm.sourcePackage, userId, quota) + window; } } else { // Not allowed. @@ -1778,9 +1814,15 @@ public class AlarmManagerService extends SystemService { } else if (isAllowedWhileIdleRestricted(alarm)) { // Allowed but limited. final int userId = UserHandle.getUserId(alarm.creatorUid); - final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) - ? mConstants.ALLOW_WHILE_IDLE_QUOTA - : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA; + final int quota; + final long window; + if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) { + quota = mConstants.ALLOW_WHILE_IDLE_QUOTA; + window = mConstants.ALLOW_WHILE_IDLE_WINDOW; + } else { + quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA; + window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW; + } final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow( alarm.sourcePackage, userId); if (dispatchesInWindow < quota) { @@ -1788,7 +1830,7 @@ public class AlarmManagerService extends SystemService { deviceIdlePolicyTime = nowElapsed; } else { final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage( - alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW; + alarm.sourcePackage, userId, quota) + window; deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed()); } } else { diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 51c9b0ddb0d6c..f2e85a700327e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -49,8 +49,10 @@ import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STAN import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL; @@ -566,17 +568,23 @@ public class AlarmManagerServiceTest { setDeviceConfigLong(KEY_MAX_INTERVAL, 15); setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 20); setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 25); - setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 30); - setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WINDOW, 30); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW, 35); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40); + setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45); assertEquals(5, mService.mConstants.MIN_FUTURITY); assertEquals(10, mService.mConstants.MIN_INTERVAL); assertEquals(15, mService.mConstants.MAX_INTERVAL); assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA); assertEquals(25, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA); - assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION); - assertEquals(35, mService.mConstants.LISTENER_TIMEOUT); + assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WINDOW); + assertEquals(35, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW); + assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION); + assertEquals(45, mService.mConstants.LISTENER_TIMEOUT); + } - // Test safeguards. + @Test + public void positiveWhileIdleQuotas() { setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, -3); assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA); setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 0); @@ -588,6 +596,21 @@ public class AlarmManagerServiceTest { assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA); } + @Test + public void whileIdleWindowsDontExceedAnHour() { + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WINDOW, AlarmManager.INTERVAL_DAY); + assertEquals(AlarmManager.INTERVAL_HOUR, mService.mConstants.ALLOW_WHILE_IDLE_WINDOW); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WINDOW, AlarmManager.INTERVAL_HOUR + 1); + assertEquals(AlarmManager.INTERVAL_HOUR, mService.mConstants.ALLOW_WHILE_IDLE_WINDOW); + + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW, AlarmManager.INTERVAL_DAY); + assertEquals(AlarmManager.INTERVAL_HOUR, + mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW, AlarmManager.INTERVAL_HOUR + 1); + assertEquals(AlarmManager.INTERVAL_HOUR, + mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW); + } + @Test public void testMinFuturity() { setDeviceConfigLong(KEY_MIN_FUTURITY, 10L);