Merge "Separating alarm history for lower quota" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
977e2adc14
@@ -256,6 +256,7 @@ public class AlarmManagerService extends SystemService {
|
||||
AlarmHandler mHandler;
|
||||
AppWakeupHistory mAppWakeupHistory;
|
||||
AppWakeupHistory mAllowWhileIdleHistory;
|
||||
AppWakeupHistory mAllowWhileIdleCompatHistory;
|
||||
private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
|
||||
private final SparseArray<RingBuffer<RemovedAlarm>> mRemovalHistory = new SparseArray<>();
|
||||
ClockReceiver mClockReceiver;
|
||||
@@ -1633,6 +1634,7 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
|
||||
mAllowWhileIdleHistory = new AppWakeupHistory(INTERVAL_HOUR);
|
||||
mAllowWhileIdleCompatHistory = new AppWakeupHistory(INTERVAL_HOUR);
|
||||
|
||||
mNextWakeup = mNextNonWakeup = 0;
|
||||
|
||||
@@ -2142,20 +2144,23 @@ public class AlarmManagerService extends SystemService {
|
||||
final int userId = UserHandle.getUserId(alarm.creatorUid);
|
||||
final int quota;
|
||||
final long window;
|
||||
final AppWakeupHistory history;
|
||||
if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) {
|
||||
quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||
window = mConstants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
history = mAllowWhileIdleHistory;
|
||||
} else {
|
||||
quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
|
||||
history = mAllowWhileIdleCompatHistory;
|
||||
}
|
||||
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
|
||||
final int dispatchesInHistory = history.getTotalWakeupsInWindow(
|
||||
alarm.sourcePackage, userId);
|
||||
if (dispatchesInWindow < quota) {
|
||||
if (dispatchesInHistory < quota) {
|
||||
// fine to go out immediately.
|
||||
batterySaverPolicyElapsed = nowElapsed;
|
||||
} else {
|
||||
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
|
||||
batterySaverPolicyElapsed = history.getNthLastWakeupForPackage(
|
||||
alarm.sourcePackage, userId, quota) + window;
|
||||
}
|
||||
} else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
|
||||
@@ -2201,20 +2206,23 @@ public class AlarmManagerService extends SystemService {
|
||||
final int userId = UserHandle.getUserId(alarm.creatorUid);
|
||||
final int quota;
|
||||
final long window;
|
||||
final AppWakeupHistory history;
|
||||
if ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) {
|
||||
quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||
window = mConstants.ALLOW_WHILE_IDLE_WINDOW;
|
||||
history = mAllowWhileIdleHistory;
|
||||
} else {
|
||||
quota = mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
window = mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
|
||||
history = mAllowWhileIdleCompatHistory;
|
||||
}
|
||||
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
|
||||
final int dispatchesInHistory = history.getTotalWakeupsInWindow(
|
||||
alarm.sourcePackage, userId);
|
||||
if (dispatchesInWindow < quota) {
|
||||
if (dispatchesInHistory < quota) {
|
||||
// fine to go out immediately.
|
||||
deviceIdlePolicyTime = nowElapsed;
|
||||
} else {
|
||||
final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
|
||||
final long whenInQuota = history.getNthLastWakeupForPackage(
|
||||
alarm.sourcePackage, userId, quota) + window;
|
||||
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
|
||||
}
|
||||
@@ -2502,6 +2510,7 @@ public class AlarmManagerService extends SystemService {
|
||||
Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
|
||||
// The API doesn't allow using both together.
|
||||
flags &= ~FLAG_ALLOW_WHILE_IDLE;
|
||||
// Prioritized alarms don't need any extra permission to be exact.
|
||||
} else if (exact || allowWhileIdle) {
|
||||
final boolean needsPermission;
|
||||
boolean lowerQuota;
|
||||
@@ -2992,6 +3001,10 @@ public class AlarmManagerService extends SystemService {
|
||||
mAllowWhileIdleHistory.dump(pw, nowELAPSED);
|
||||
pw.println();
|
||||
|
||||
pw.println("Allow while idle compat history:");
|
||||
mAllowWhileIdleCompatHistory.dump(pw, nowELAPSED);
|
||||
pw.println();
|
||||
|
||||
if (mLastPriorityAlarmDispatch.size() > 0) {
|
||||
pw.println("Last priority alarm dispatches:");
|
||||
pw.increaseIndent();
|
||||
@@ -4553,6 +4566,7 @@ public class AlarmManagerService extends SystemService {
|
||||
removeUserLocked(userHandle);
|
||||
mAppWakeupHistory.removeForUser(userHandle);
|
||||
mAllowWhileIdleHistory.removeForUser(userHandle);
|
||||
mAllowWhileIdleCompatHistory.removeForUser(userHandle);
|
||||
}
|
||||
return;
|
||||
case Intent.ACTION_UID_REMOVED:
|
||||
@@ -4588,6 +4602,8 @@ public class AlarmManagerService extends SystemService {
|
||||
// package-removed and package-restarted case
|
||||
mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
|
||||
mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
|
||||
mAllowWhileIdleCompatHistory.removeForPackage(pkg,
|
||||
UserHandle.getUserId(uid));
|
||||
removeLocked(uid, REMOVE_REASON_UNDEFINED);
|
||||
} else {
|
||||
// external-applications-unavailable case
|
||||
@@ -4965,7 +4981,10 @@ public class AlarmManagerService extends SystemService {
|
||||
if (isAllowedWhileIdleRestricted(alarm)) {
|
||||
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
|
||||
// device was in doze or battery saver.
|
||||
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
|
||||
final AppWakeupHistory history = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
|
||||
? mAllowWhileIdleHistory
|
||||
: mAllowWhileIdleCompatHistory;
|
||||
history.recordAlarmForPackage(alarm.sourcePackage,
|
||||
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
|
||||
mAlarmStore.updateAlarmDeliveries(a -> {
|
||||
if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) {
|
||||
|
||||
@@ -514,8 +514,16 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi,
|
||||
boolean unrestricted) {
|
||||
final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE;
|
||||
boolean unrestricted, boolean compat) {
|
||||
assertFalse("Alarm cannot be compat and unrestricted", unrestricted && compat);
|
||||
final int flags;
|
||||
if (unrestricted) {
|
||||
flags = FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
||||
} else if (compat) {
|
||||
flags = FLAG_ALLOW_WHILE_IDLE_COMPAT;
|
||||
} else {
|
||||
flags = FLAG_ALLOW_WHILE_IDLE;
|
||||
}
|
||||
setTestAlarm(type, triggerTime, pi, 0, flags, TEST_CALLING_UID);
|
||||
}
|
||||
|
||||
@@ -1600,13 +1608,13 @@ public class AlarmManagerServiceTest {
|
||||
final long firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent(), false);
|
||||
getNewMockPendingIntent(), false, false);
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
// This one should get deferred on set.
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||
getNewMockPendingIntent(), false);
|
||||
getNewMockPendingIntent(), false, false);
|
||||
final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow;
|
||||
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
@@ -1618,6 +1626,108 @@ public class AlarmManagerServiceTest {
|
||||
assertEquals(expectedNextTrigger - 50, mTestTimer.getElapsed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowWhileIdleCompatAlarmsWhileDeviceIdle() throws Exception {
|
||||
setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0);
|
||||
|
||||
final long window = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
|
||||
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + window + 1000,
|
||||
getNewMockPendingIntent());
|
||||
assertNotNull(mService.mPendingIdleUntil);
|
||||
|
||||
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
final long firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < quota; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent(), false, true);
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
// This one should get deferred on set.
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
|
||||
getNewMockPendingIntent(), false, true);
|
||||
final long expectedNextTrigger = firstTrigger + window;
|
||||
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
|
||||
// Bring the idle until alarm back.
|
||||
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger - 50,
|
||||
getNewMockPendingIntent());
|
||||
assertEquals(expectedNextTrigger - 50, mService.mPendingIdleUntil.getWhenElapsed());
|
||||
assertEquals(expectedNextTrigger - 50, mTestTimer.getElapsed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowWhileIdleCompatHistorySeparate() throws Exception {
|
||||
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE)).thenReturn(true);
|
||||
when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
|
||||
|
||||
final int fullQuota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||
final int compatQuota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
|
||||
final long fullWindow = mAllowWhileIdleWindow;
|
||||
final long compatWindow = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
|
||||
|
||||
final long firstFullTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < fullQuota; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstFullTrigger + i,
|
||||
getNewMockPendingIntent(), false, false);
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
// This one should get deferred on set, as full quota is not available.
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstFullTrigger + fullQuota,
|
||||
getNewMockPendingIntent(), false, false);
|
||||
final long expectedNextFullTrigger = firstFullTrigger + fullWindow;
|
||||
assertEquals("Incorrect trigger when no quota left", expectedNextFullTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
|
||||
|
||||
// The following should be allowed, as compat quota should be free.
|
||||
for (int i = 0; i < compatQuota; i++) {
|
||||
final long trigger = mNowElapsedTest + 1;
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, getNewMockPendingIntent(),
|
||||
false, true);
|
||||
assertEquals(trigger, mTestTimer.getElapsed());
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
|
||||
// Refresh the state
|
||||
mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
|
||||
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
||||
mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
|
||||
TEST_CALLING_USER);
|
||||
|
||||
// Now test with flipped order
|
||||
|
||||
final long firstCompatTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < compatQuota; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstCompatTrigger + i,
|
||||
getNewMockPendingIntent(), false, true);
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
// This one should get deferred on set, as full quota is not available.
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstCompatTrigger + compatQuota,
|
||||
getNewMockPendingIntent(), false, true);
|
||||
final long expectedNextCompatTrigger = firstCompatTrigger + compatWindow;
|
||||
assertEquals("Incorrect trigger when no quota left", expectedNextCompatTrigger,
|
||||
mTestTimer.getElapsed());
|
||||
mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
|
||||
|
||||
// The following should be allowed, as full quota should be free.
|
||||
for (int i = 0; i < fullQuota; i++) {
|
||||
final long trigger = mNowElapsedTest + 1;
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, getNewMockPendingIntent(),
|
||||
false, false);
|
||||
assertEquals(trigger, mTestTimer.getElapsed());
|
||||
mNowElapsedTest = mTestTimer.getElapsed();
|
||||
mTestTimer.expire();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowWhileIdleUnrestricted() throws Exception {
|
||||
setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0);
|
||||
@@ -1634,7 +1744,7 @@ public class AlarmManagerServiceTest {
|
||||
final long firstTrigger = mNowElapsedTest + 10;
|
||||
for (int i = 0; i < numAlarms; i++) {
|
||||
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
|
||||
getNewMockPendingIntent(), true);
|
||||
getNewMockPendingIntent(), true, false);
|
||||
}
|
||||
// All of them should fire as expected.
|
||||
for (int i = 0; i < numAlarms; i++) {
|
||||
@@ -1736,7 +1846,7 @@ public class AlarmManagerServiceTest {
|
||||
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
|
||||
|
||||
testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||
getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);
|
||||
|
||||
// Refresh the state
|
||||
mService.removeLocked(TEST_CALLING_UID,
|
||||
@@ -1744,7 +1854,7 @@ public class AlarmManagerServiceTest {
|
||||
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
||||
|
||||
testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
|
||||
trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||
trigger, getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);
|
||||
|
||||
// Refresh the state
|
||||
mService.removeLocked(TEST_CALLING_UID,
|
||||
@@ -1752,7 +1862,36 @@ public class AlarmManagerServiceTest {
|
||||
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
|
||||
|
||||
testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
|
||||
getNewMockPendingIntent(), false, false), quota, mAllowWhileIdleWindow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowWhileIdleCompatAlarmsInBatterySaver() throws Exception {
|
||||
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
|
||||
TEST_CALLING_PACKAGE)).thenReturn(true);
|
||||
when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
|
||||
|
||||
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
|
||||
final long window = mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW;
|
||||
|
||||
testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent(), false, true), quota, window);
|
||||
|
||||
// Refresh the state
|
||||
mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
|
||||
mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
|
||||
TEST_CALLING_USER);
|
||||
|
||||
testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
|
||||
trigger, getNewMockPendingIntent(), false, true), quota, window);
|
||||
|
||||
// Refresh the state
|
||||
mService.removeLocked(TEST_CALLING_UID, REMOVE_REASON_UNDEFINED);
|
||||
mService.mAllowWhileIdleCompatHistory.removeForPackage(TEST_CALLING_PACKAGE,
|
||||
TEST_CALLING_USER);
|
||||
|
||||
testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
|
||||
getNewMockPendingIntent(), false, true), quota, window);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2123,7 +2262,7 @@ public class AlarmManagerServiceTest {
|
||||
final PendingIntent alarmPi = getNewMockPendingIntent();
|
||||
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
|
||||
mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
|
||||
alarmPi, null, null, null, alarmClock);
|
||||
alarmPi, null, null, null, alarmClock);
|
||||
|
||||
// Correct permission checks are invoked.
|
||||
verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
|
||||
|
||||
Reference in New Issue
Block a user