Remove standby delay from AlarmManager in favor of quotas.

We're moving forward with the quota system, and so no longer need to
keep the delay code or tests.

Bug: 142569967
Test: atest com.android.server.AlarmManagerServiceTest
Test: atest com.android.server.AppStateTrackerTest
Test: atest AppStandbyControllerTests
Test: atest CtsAlarmManagerTestCases
Change-Id: I9ce96a6e050f8dcd14d02fd707c8f4693e397ac5
This commit is contained in:
Kweku Adams
2019-10-15 15:41:28 -07:00
parent 181c51f18b
commit 9e5ef1d8d1
2 changed files with 30 additions and 187 deletions

View File

@@ -154,7 +154,7 @@ class AlarmManagerService extends SystemService {
static final int TICK_HISTORY_DEPTH = 10;
static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
// Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays
// Indices into the KEYS_APP_STANDBY_QUOTAS array.
static final int ACTIVE_INDEX = 0;
static final int WORKING_INDEX = 1;
static final int FREQUENT_INDEX = 2;
@@ -401,8 +401,6 @@ class AlarmManagerService extends SystemService {
static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
@VisibleForTesting
static final String KEY_MAX_ALARMS_PER_UID = "max_alarms_per_uid";
@VisibleForTesting
static final String KEY_APP_STANDBY_QUOTAS_ENABLED = "app_standby_quotas_enabled";
private static final String KEY_APP_STANDBY_WINDOW = "app_standby_window";
@VisibleForTesting
final String[] KEYS_APP_STANDBY_QUOTAS = {
@@ -413,15 +411,6 @@ class AlarmManagerService extends SystemService {
"standby_never_quota",
};
// Keys for specifying throttling delay based on app standby bucketing
private final String[] KEYS_APP_STANDBY_DELAY = {
"standby_active_delay",
"standby_working_delay",
"standby_frequent_delay",
"standby_rare_delay",
"standby_never_delay",
};
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;
@@ -430,7 +419,6 @@ class AlarmManagerService extends SystemService {
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;
private static final boolean DEFAULT_APP_STANDBY_QUOTAS_ENABLED = true;
private static final long DEFAULT_APP_STANDBY_WINDOW = 60 * 60 * 1000; // 1 hr
/**
* Max number of times an app can receive alarms in {@link #APP_STANDBY_WINDOW}
@@ -442,13 +430,6 @@ class AlarmManagerService extends SystemService {
1, // Rare
0 // Never
};
private final long[] DEFAULT_APP_STANDBY_DELAYS = {
0, // Active
6 * 60_000, // Working
30 * 60_000, // Frequent
2 * 60 * 60_000, // Rare
10 * 24 * 60 * 60_000 // Never
};
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -473,10 +454,7 @@ class AlarmManagerService extends SystemService {
public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
public int MAX_ALARMS_PER_UID = DEFAULT_MAX_ALARMS_PER_UID;
public boolean APP_STANDBY_QUOTAS_ENABLED = DEFAULT_APP_STANDBY_QUOTAS_ENABLED;
public long APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
public long[] APP_STANDBY_MIN_DELAYS = new long[DEFAULT_APP_STANDBY_DELAYS.length];
public int[] APP_STANDBY_QUOTAS = new int[DEFAULT_APP_STANDBY_QUOTAS.length];
private ContentResolver mResolver;
@@ -532,16 +510,6 @@ class AlarmManagerService extends SystemService {
DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
DEFAULT_LISTENER_TIMEOUT);
APP_STANDBY_MIN_DELAYS[ACTIVE_INDEX] = mParser.getDurationMillis(
KEYS_APP_STANDBY_DELAY[ACTIVE_INDEX],
DEFAULT_APP_STANDBY_DELAYS[ACTIVE_INDEX]);
for (int i = WORKING_INDEX; i < KEYS_APP_STANDBY_DELAY.length; i++) {
APP_STANDBY_MIN_DELAYS[i] = mParser.getDurationMillis(KEYS_APP_STANDBY_DELAY[i],
Math.max(APP_STANDBY_MIN_DELAYS[i - 1], DEFAULT_APP_STANDBY_DELAYS[i]));
}
APP_STANDBY_QUOTAS_ENABLED = mParser.getBoolean(KEY_APP_STANDBY_QUOTAS_ENABLED,
DEFAULT_APP_STANDBY_QUOTAS_ENABLED);
APP_STANDBY_WINDOW = mParser.getLong(KEY_APP_STANDBY_WINDOW,
DEFAULT_APP_STANDBY_WINDOW);
@@ -614,15 +582,6 @@ class AlarmManagerService extends SystemService {
pw.print(KEY_MAX_ALARMS_PER_UID); pw.print("=");
pw.println(MAX_ALARMS_PER_UID);
for (int i = 0; i < KEYS_APP_STANDBY_DELAY.length; i++) {
pw.print(KEYS_APP_STANDBY_DELAY[i]); pw.print("=");
TimeUtils.formatDuration(APP_STANDBY_MIN_DELAYS[i], pw);
pw.println();
}
pw.print(KEY_APP_STANDBY_QUOTAS_ENABLED); pw.print("=");
pw.println(APP_STANDBY_QUOTAS_ENABLED);
pw.print(KEY_APP_STANDBY_WINDOW); pw.print("=");
TimeUtils.formatDuration(APP_STANDBY_WINDOW, pw);
pw.println();
@@ -1825,27 +1784,6 @@ class AlarmManagerService extends SystemService {
return mConstants.APP_STANDBY_QUOTAS[index];
}
/**
* Return the minimum time that should elapse before an app in the specified bucket
* can receive alarms again
*/
@VisibleForTesting
long getMinDelayForBucketLocked(int bucket) {
// UsageStats bucket values are treated as floors of their behavioral range.
// In other words, a bucket value between WORKING and ACTIVE is treated as
// WORKING, not as ACTIVE. The ACTIVE and NEVER bucket apply only at specific
// values.
final int index;
if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) index = NEVER_INDEX;
else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) index = RARE_INDEX;
else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) index = FREQUENT_INDEX;
else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) index = WORKING_INDEX;
else index = ACTIVE_INDEX;
return mConstants.APP_STANDBY_MIN_DELAYS[index];
}
/**
* Adjusts the alarm delivery time based on the current app standby bucket.
* @param alarm The alarm to adjust
@@ -1872,50 +1810,34 @@ class AlarmManagerService extends SystemService {
final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
sourcePackage, sourceUserId, mInjector.getElapsedRealtime());
if (mConstants.APP_STANDBY_QUOTAS_ENABLED) {
// Quota deferring implementation:
final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
sourceUserId);
final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
boolean deferred = false;
if (wakeupsInWindow >= quotaForBucket) {
final long minElapsed;
if (quotaForBucket <= 0) {
// Just keep deferring for a day till the quota changes
minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
} else {
// Suppose the quota for window was q, and the qth last delivery time for this
// package was t(q) then the next delivery must be after t(q) + <window_size>
final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
sourceUserId, quotaForBucket);
minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
}
if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
deferred = true;
}
// Quota deferring implementation:
final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
sourceUserId);
final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
boolean deferred = false;
if (wakeupsInWindow >= quotaForBucket) {
final long minElapsed;
if (quotaForBucket <= 0) {
// Just keep deferring for a day till the quota changes
minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
} else {
// Suppose the quota for window was q, and the qth last delivery time for this
// package was t(q) then the next delivery must be after t(q) + <window_size>
final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
sourceUserId, quotaForBucket);
minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
}
if (!deferred) {
// Restore original requirements in case they were changed earlier.
alarm.whenElapsed = alarm.expectedWhenElapsed;
alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
} else {
// Minimum delay deferring implementation:
final long lastElapsed = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
sourceUserId, 1);
if (lastElapsed > 0) {
final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
} else {
// app is now eligible to run alarms at the originally requested window.
// Restore original requirements in case they were changed earlier.
alarm.whenElapsed = alarm.expectedWhenElapsed;
alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
deferred = true;
}
}
if (!deferred) {
// Restore original requirements in case they were changed earlier.
alarm.whenElapsed = alarm.expectedWhenElapsed;
alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
}

View File

@@ -38,7 +38,6 @@ import static com.android.server.AlarmManagerService.AlarmHandler.CHARGING_STATU
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
import static com.android.server.AlarmManagerService.Constants.KEY_APP_STANDBY_QUOTAS_ENABLED;
import static com.android.server.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
@@ -305,6 +304,8 @@ public class AlarmManagerServiceTest {
argThat((filter) -> filter.hasAction(BatteryManager.ACTION_CHARGING)
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
mChargingReceiver = chargingReceiverCaptor.getValue();
setTestableQuotas();
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
@@ -342,9 +343,10 @@ public class AlarmManagerServiceTest {
}
/**
* Lowers quotas to make testing feasible.
* Careful while calling as this will replace any existing settings for the calling test.
*/
private void setQuotasEnabled(boolean enabled) {
private void setTestableQuotas() {
final StringBuilder constantsBuilder = new StringBuilder();
constantsBuilder.append(KEY_MIN_FUTURITY);
constantsBuilder.append("=0,");
@@ -353,14 +355,9 @@ public class AlarmManagerServiceTest {
constantsBuilder.append("=8,");
constantsBuilder.append(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]);
constantsBuilder.append("=5,");
if (!enabled) {
constantsBuilder.append(KEY_APP_STANDBY_QUOTAS_ENABLED);
constantsBuilder.append("=false,");
}
doReturn(constantsBuilder.toString()).when(() -> Settings.Global.getString(mMockResolver,
Settings.Global.ALARM_MANAGER_CONSTANTS));
mService.mConstants.onChange(false, null);
assertEquals(mService.mConstants.APP_STANDBY_QUOTAS_ENABLED, enabled);
}
@Test
@@ -481,67 +478,6 @@ public class AlarmManagerServiceTest {
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
}
@Test
public void testStandbyBucketDelay_workingSet() throws Exception {
setQuotasEnabled(false);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
verify(mUsageStatsManagerInternal, atLeastOnce())
.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
final long expectedNextTrigger = mNowElapsedTest
+ mService.getMinDelayForBucketLocked(STANDBY_BUCKET_WORKING_SET);
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
@Test
public void testStandbyBucketDelay_frequent() throws Exception {
setQuotasEnabled(false);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
verify(mUsageStatsManagerInternal, atLeastOnce())
.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
final long expectedNextTrigger = mNowElapsedTest
+ mService.getMinDelayForBucketLocked(STANDBY_BUCKET_FREQUENT);
assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed());
}
@Test
public void testStandbyBucketDelay_rare() throws Exception {
setQuotasEnabled(false);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_RARE);
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
verify(mUsageStatsManagerInternal, atLeastOnce())
.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
final long expectedNextTrigger = mNowElapsedTest
+ mService.getMinDelayForBucketLocked(STANDBY_BUCKET_RARE);
assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed());
}
private void testQuotasDeferralOnSet(int standbyBucket) throws Exception {
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
@@ -601,73 +537,61 @@ public class AlarmManagerServiceTest {
@Test
public void testActiveQuota_deferredOnSet() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_deferredOnExpiration() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_notDeferred() throws Exception {
setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testWorkingQuota_deferredOnSet() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_deferredOnExpiration() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_notDeferred() throws Exception {
setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testFrequentQuota_deferredOnSet() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_deferredOnExpiration() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_notDeferred() throws Exception {
setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testRareQuota_deferredOnSet() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_deferredOnExpiration() throws Exception {
setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_notDeferred() throws Exception {
setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_RARE);
}
@@ -686,7 +610,6 @@ public class AlarmManagerServiceTest {
@Test
public void testQuotaDowngrade() throws Exception {
setQuotasEnabled(true);
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
@@ -714,7 +637,6 @@ public class AlarmManagerServiceTest {
@Test
public void testQuotaUpgrade() throws Exception {
setQuotasEnabled(true);
final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
@@ -752,7 +674,6 @@ public class AlarmManagerServiceTest {
@Test
public void testCharging() throws Exception {
setQuotasEnabled(true);
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);