Remove exact listener alarms when uid gets cached

Listener alarms are only meant to be used within the duration of a valid
lifecycle. Once the app goes out of lifecycle, it can be killed
arbitrarily and the alarm will be removed. Therefore, no apps should be
relying on receiving an alarm after they go out of lifecycle.

Besides, apps can be frozen when they end up in the cached state. This
breaks the alarm delivery guarantees for listener based exact alarms.

So even though dropping the alarm while the app is frozen can cause
inconsistencies in the app's view of the world, this is now done
explicitly as per the contract of the listener exact alarm APIs: Exact
alarms expecting a callback on a listener will now be dropped as soon
as the app goes into cached state. This is done to ensure consistency
and reliability of behavior.

Test: atest FrameworksMockingServicesTests:AlarmManagerServiceTest
Test: atest FrameworksMockingServicesTests:AppStateTrackerTest

Bug: 265195908
Change-Id: If95a0aa7416c4233b244144b353e471322c303a2
This commit is contained in:
Suprabh Shukla
2023-02-09 01:31:14 -08:00
parent 0085edf03b
commit 04bf84e220
5 changed files with 290 additions and 60 deletions

View File

@@ -311,6 +311,15 @@ public class AlarmManager {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static final long SCHEDULE_EXACT_ALARM_DOES_NOT_ELEVATE_BUCKET = 262645982L;
/**
* Exact alarms expecting a {@link OnAlarmListener} callback will be dropped when the calling
* app goes into cached state.
*
* @hide
*/
@ChangeId
public static final long EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED = 265195908L;
@UnsupportedAppUsage
private final IAlarmManager mService;
private final Context mContext;
@@ -808,13 +817,24 @@ public class AlarmManager {
* The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
* invoked via the specified target Handler, or on the application's main looper
* if {@code null} is passed as the {@code targetHandler} parameter.
* <p>
* This API should only be used to set alarms that are relevant in the context of the app's
* current lifecycle, as the {@link OnAlarmListener} instance supplied is only valid as long as
* the process is alive, and the system can clean up the app process as soon as it is out of
* lifecycle. To schedule alarms that fire reliably even after the current lifecycle completes,
* and wakes up the app if required, use any of the other scheduling APIs that accept a
* {@link PendingIntent} instance.
*
* <p class="note"><strong>Note:</strong>
* <p>
* On previous android versions {@link Build.VERSION_CODES#S} and
* {@link Build.VERSION_CODES#TIRAMISU}, apps targeting SDK level 31 or higher needed to hold
* the {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use
* this API, unless the app was exempt from battery restrictions.
*
* <p class="note"><strong>Note:</strong>
* Starting with android version {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the system will
* explicitly drop any alarms set via this API when the calling app goes out of lifecycle.
*
*/
public void setExact(@AlarmType int type, long triggerAtMillis, @Nullable String tag,
@NonNull OnAlarmListener listener, @Nullable Handler targetHandler) {
@@ -984,6 +1004,10 @@ public class AlarmManager {
* allowlist. This can be set, for example, by marking the app as {@code <allow-in-power-save>}
* within the system config.
*
* <p class="note"><strong>Note:</strong>
* Starting with android version {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the system will
* explicitly drop any alarms set via this API when the calling app goes out of lifecycle.
*
* @param type type of alarm
* @param triggerAtMillis The exact time in milliseconds, that the alarm should be delivered,
* expressed in the appropriate clock's units (depending on the alarm
@@ -1295,6 +1319,10 @@ public class AlarmManager {
*
* <p> See {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} for more details.
*
* <p class="note"><strong>Note:</strong>
* Starting with android version {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the system will
* explicitly drop any alarms set via this API when the calling app goes out of lifecycle.
*
* @param type type of alarm
* @param triggerAtMillis The exact time in milliseconds, that the alarm should be delivered,
* expressed in the appropriate clock's units (depending on the alarm

View File

@@ -425,6 +425,12 @@ public class AppStateTrackerImpl implements AppStateTracker {
*/
public void removeAlarmsForUid(int uid) {
}
/**
* Called when a uid goes into cached, so its alarms using a listener should be removed.
*/
public void removeListenerAlarmsForCachedUid(int uid) {
}
}
public AppStateTrackerImpl(Context context, Looper looper) {
@@ -496,7 +502,8 @@ public class AppStateTrackerImpl implements AppStateTracker {
mIActivityManager.registerUidObserver(new UidObserver(),
ActivityManager.UID_OBSERVER_GONE
| ActivityManager.UID_OBSERVER_IDLE
| ActivityManager.UID_OBSERVER_ACTIVE,
| ActivityManager.UID_OBSERVER_ACTIVE
| ActivityManager.UID_OBSERVER_CACHED,
ActivityManager.PROCESS_STATE_UNKNOWN, null);
mAppOpsService.startWatchingMode(TARGET_OP, null,
new AppOpsWatcher());
@@ -731,6 +738,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
@Override
public void onUidCachedChanged(int uid, boolean cached) {
mHandler.onUidCachedChanged(uid, cached);
}
@Override
@@ -800,6 +808,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
private static final int MSG_ON_UID_ACTIVE = 12;
private static final int MSG_ON_UID_GONE = 13;
private static final int MSG_ON_UID_IDLE = 14;
private static final int MSG_ON_UID_CACHED = 15;
MyHandler(Looper looper) {
super(looper);
@@ -860,6 +869,12 @@ public class AppStateTrackerImpl implements AppStateTracker {
obtainMessage(MSG_ON_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
}
public void onUidCachedChanged(int uid, boolean cached) {
if (cached) {
obtainMessage(MSG_ON_UID_CACHED, uid, 0).sendToTarget();
}
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -953,6 +968,15 @@ public class AppStateTrackerImpl implements AppStateTracker {
handleUidDisabled(msg.arg1);
}
return;
case MSG_ON_UID_CACHED:
handleUidCached(msg.arg1);
return;
}
}
private void handleUidCached(int uid) {
for (Listener l : cloneListeners()) {
l.removeListenerAlarmsForCachedUid(uid);
}
}

View File

@@ -19,6 +19,7 @@ package com.android.server.alarm;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
@@ -57,6 +58,8 @@ import static com.android.server.alarm.Alarm.TARE_POLICY_INDEX;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_DATA_CLEARED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_EXACT_PERMISSION_REVOKED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_LISTENER_BINDER_DIED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_LISTENER_CACHED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_PI_CANCELLED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
@@ -610,6 +613,8 @@ public class AlarmManagerService extends SystemService {
static final int REMOVE_REASON_EXACT_PERMISSION_REVOKED = 2;
static final int REMOVE_REASON_DATA_CLEARED = 3;
static final int REMOVE_REASON_PI_CANCELLED = 4;
static final int REMOVE_REASON_LISTENER_BINDER_DIED = 5;
static final int REMOVE_REASON_LISTENER_CACHED = 6;
final String mTag;
final long mWhenRemovedElapsed;
@@ -639,6 +644,10 @@ public class AlarmManagerService extends SystemService {
return "data_cleared";
case REMOVE_REASON_PI_CANCELLED:
return "pi_cancelled";
case REMOVE_REASON_LISTENER_BINDER_DIED:
return "listener_binder_died";
case REMOVE_REASON_LISTENER_CACHED:
return "listener_cached";
default:
return "unknown:" + reason;
}
@@ -1892,7 +1901,9 @@ public class AlarmManagerService extends SystemService {
@Override
public void binderDied(IBinder who) {
final IAlarmListener listener = IAlarmListener.Stub.asInterface(who);
removeImpl(null, listener);
synchronized (mLock) {
removeLocked(null, listener, REMOVE_REASON_LISTENER_BINDER_DIED);
}
}
};
@@ -5444,6 +5455,24 @@ public class AlarmManagerService extends SystemService {
removeForStoppedLocked(uid);
}
}
@Override
public void removeListenerAlarmsForCachedUid(int uid) {
if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uid)) {
return;
}
synchronized (mLock) {
removeAlarmsInternalLocked(a -> {
if (a.uid != uid || a.listener == null || a.windowLength != 0) {
return false;
}
// TODO (b/265195908): Change to a .w once we have some data on breakages.
Slog.wtf(TAG, "Alarm " + a.listenerTag + " being removed for " + a.packageName
+ " because the app went into cached state");
return true;
}, REMOVE_REASON_LISTENER_CACHED);
}
}
};
private final BroadcastStats getStatsLocked(PendingIntent pi) {

View File

@@ -271,7 +271,8 @@ public class AppStateTrackerTest {
verify(mMockIActivityManager).registerUidObserver(
uidObserverArgumentCaptor.capture(),
eq(ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
| ActivityManager.UID_OBSERVER_ACTIVE),
| ActivityManager.UID_OBSERVER_ACTIVE
| ActivityManager.UID_OBSERVER_CACHED),
eq(ActivityManager.PROCESS_STATE_UNKNOWN),
isNull());
verify(mMockIAppOpsService).startWatchingMode(
@@ -1364,6 +1365,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidGone(UID_10_1, true);
@@ -1381,6 +1384,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(1)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidActive(UID_10_1);
@@ -1398,6 +1403,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidIdle(UID_10_1, true);
@@ -1415,8 +1422,49 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(1)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidCachedChanged(UID_10_1, true);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(1)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidCachedChanged(UID_10_1, false);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
// Without battery saver.
mPowerSaveMode = false;
mPowerSaveObserver.accept(getPowerSaveState());
@@ -1433,6 +1481,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidActive(UID_10_1);
@@ -1450,6 +1500,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidGone(UID_10_1, true);
@@ -1467,6 +1519,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(1)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidActive(UID_10_1);
@@ -1484,6 +1538,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidIdle(UID_10_1, true);
@@ -1501,6 +1557,46 @@ public class AppStateTrackerTest {
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(1)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidCachedChanged(UID_10_1, true);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(1)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
mIUidObserver.onUidCachedChanged(UID_10_1, false);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
verify(l, times(0)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
verify(l, times(0)).removeAlarmsForUid(UID_10_1);
verify(l, times(0)).removeListenerAlarmsForCachedUid(UID_10_1);
reset(l);
}

View File

@@ -18,6 +18,7 @@ package com.android.server.alarm;
import static android.Manifest.permission.SCHEDULE_EXACT_ALARM;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
@@ -201,17 +202,21 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.LongConsumer;
@Presubmit
@SuppressWarnings("GuardedBy") // This test enforces synchronous behavior.
@RunWith(AndroidJUnit4.class)
public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
private static final int SYSTEM_UI_UID = 12345;
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
private static final int TEST_CALLING_UID_2 = TEST_CALLING_UID + 1;
private static final int[] ALARM_TYPES =
{RTC_WAKEUP, RTC, ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME};
private long mAppStandbyWindow;
private long mAllowWhileIdleWindow;
private AlarmManagerService mService;
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
private AppStateTrackerImpl.Listener mListener;
private AlarmManagerService.UninstallReceiver mPackageChangesReceiver;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
private IAppOpsCallback mIAppOpsCallback;
@@ -515,10 +520,16 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
setTareEnabled(EconomyManager.ENABLED_MODE_OFF);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> idleListenerCaptor =
ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
verify(mAppStandbyInternal).addListener(captor.capture());
mAppStandbyListener = captor.getValue();
verify(mAppStandbyInternal).addListener(idleListenerCaptor.capture());
mAppStandbyListener = idleListenerCaptor.getValue();
final ArgumentCaptor<AppStateTrackerImpl.Listener> trackerListenerCaptor =
ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(trackerListenerCaptor.capture());
mListener = trackerListenerCaptor.getValue();
final ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor =
ArgumentCaptor.forClass(AlarmManagerService.ChargingReceiver.class);
@@ -613,8 +624,13 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
}
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null, 0);
setTestAlarmWithListener(type, triggerTime, listener, WINDOW_EXACT, TEST_CALLING_UID);
}
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener,
long windowLength, int callingUid) {
mService.setImpl(type, triggerTime, windowLength, 0, null, listener, "test",
FLAG_STANDALONE, null, null, callingUid, TEST_CALLING_PACKAGE, null, 0);
}
private PendingIntent getNewMockPendingIntent() {
@@ -638,6 +654,15 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
return mockPi;
}
private IAlarmListener getNewListener(Runnable onAlarm) {
return new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
onAlarm.run();
}
};
}
private void setDeviceConfigInt(String key, int val) {
mDeviceConfigKeys.add(key);
doReturn(val).when(mDeviceConfigProperties).getInt(eq(key), anyInt());
@@ -1213,10 +1238,6 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
@Test
public void testAlarmRestrictedByFAS() throws Exception {
final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
final PendingIntent alarmPi = getNewMockPendingIntent();
when(mAppStateTracker.areAlarmsRestricted(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
@@ -1231,7 +1252,7 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
mTestTimer.expire();
assertNotNull(restrictedAlarms.get(TEST_CALLING_UID));
listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID);
mListener.unblockAlarmsForUid(TEST_CALLING_UID);
verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), any(),
any(Handler.class), isNull(), any());
assertNull(restrictedAlarms.get(TEST_CALLING_UID));
@@ -1239,11 +1260,6 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
@Test
public void alarmsRemovedOnAppStartModeDisabled() {
final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
final PendingIntent alarmPi1 = getNewMockPendingIntent();
final PendingIntent alarmPi2 = getNewMockPendingIntent();
@@ -1254,7 +1270,7 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
when(mActivityManagerInternal.isAppStartModeDisabled(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
listener.removeAlarmsForUid(TEST_CALLING_UID);
mListener.removeAlarmsForUid(TEST_CALLING_UID);
assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
}
@@ -1305,9 +1321,8 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
@Test
public void alarmCountOnSetPi() {
final int numAlarms = 103;
final int[] types = {RTC_WAKEUP, RTC, ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME};
for (int i = 1; i <= numAlarms; i++) {
setTestAlarm(types[i % 4], mNowElapsedTest + i, getNewMockPendingIntent());
setTestAlarm(ALARM_TYPES[i % 4], mNowElapsedTest + i, getNewMockPendingIntent());
assertEquals(i, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
}
}
@@ -1315,13 +1330,9 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
@Test
public void alarmCountOnSetListener() {
final int numAlarms = 103;
final int[] types = {RTC_WAKEUP, RTC, ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME};
for (int i = 1; i <= numAlarms; i++) {
setTestAlarmWithListener(types[i % 4], mNowElapsedTest + i, new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
}
});
setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
getNewListener(() -> {}));
assertEquals(i, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
}
}
@@ -1346,12 +1357,7 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
final int numAlarms = 8; // This test is slow
for (int i = 0; i < numAlarms; i++) {
setTestAlarmWithListener(ELAPSED_REALTIME, mNowElapsedTest + i + 10,
new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback)
throws RemoteException {
}
});
getNewListener(() -> {}));
}
int expired = 0;
while (expired < numAlarms) {
@@ -1384,12 +1390,9 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
public void alarmCountOnExceptionWhileCallingListener() throws Exception {
final int numAlarms = 5; // This test is slow
for (int i = 0; i < numAlarms; i++) {
final IAlarmListener listener = new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
throw new RemoteException("For testing behavior on exception");
}
};
final IAlarmListener listener = getNewListener(() -> {
throw new RuntimeException("For testing behavior on exception");
});
setTestAlarmWithListener(ELAPSED_REALTIME, mNowElapsedTest + i + 10, listener);
}
int expired = 0;
@@ -1476,7 +1479,7 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
@Test
public void alarmTypes() throws Exception {
final int[] typesToSet = {ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME, RTC_WAKEUP, RTC};
final int[] typesToSet = ALARM_TYPES;
final int[] typesExpected = {ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME,
ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME};
assertAlarmTypeConversion(typesToSet, typesExpected);
@@ -1515,11 +1518,7 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
final int numAlarms = 10;
final IAlarmListener[] listeners = new IAlarmListener[numAlarms];
for (int i = 0; i < numAlarms; i++) {
listeners[i] = new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
}
};
listeners[i] = getNewListener(() -> {});
setTestAlarmWithListener(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + i, listeners[i]);
}
assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
@@ -1562,13 +1561,10 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
final int numAlarms = 5;
final AtomicInteger alarmsFired = new AtomicInteger(0);
for (int i = 0; i < numAlarms; i++) {
final IAlarmListener listener = new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
alarmsFired.incrementAndGet();
mService.mPendingNonWakeupAlarms.clear();
}
};
final IAlarmListener listener = getNewListener(() -> {
alarmsFired.incrementAndGet();
mService.mPendingNonWakeupAlarms.clear();
});
setTestAlarmWithListener(ELAPSED_REALTIME, mNowElapsedTest + i + 5, listener);
}
doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
@@ -1956,11 +1952,6 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
@Test
public void batterySaverThrottling() {
final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
@@ -1970,12 +1961,12 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(false);
listener.updateAllAlarms();
mListener.updateAllAlarms();
assertEquals(mNowElapsedTest + 7, mTestTimer.getElapsed());
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
listener.updateAlarmsForUid(TEST_CALLING_UID);
mListener.updateAlarmsForUid(TEST_CALLING_UID);
assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed());
}
@@ -2229,6 +2220,7 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
private void mockChangeEnabled(long changeId, boolean enabled) {
doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
any(UserHandle.class)));
doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyInt()));
}
@Test
@@ -3753,6 +3745,67 @@ public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
testTemporaryQuota_bumpedBeforeDeferral(STANDBY_BUCKET_RARE);
}
@Test
public void exactListenerAlarmsRemovedOnCached() {
mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
setTestAlarmWithListener(ELAPSED_REALTIME, 31, getNewListener(() -> {}), WINDOW_EXACT,
TEST_CALLING_UID);
setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
setTestAlarm(ELAPSED_REALTIME, 54, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
TEST_CALLING_UID, null);
setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
setTestAlarmWithListener(ELAPSED_REALTIME, 21, getNewListener(() -> {}), WINDOW_EXACT,
TEST_CALLING_UID_2);
setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
setTestAlarm(ELAPSED_REALTIME, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
TEST_CALLING_UID_2, null);
setTestAlarm(RTC, 549, 234, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null);
assertEquals(8, mService.mAlarmStore.size());
mListener.removeListenerAlarmsForCachedUid(TEST_CALLING_UID);
assertEquals(7, mService.mAlarmStore.size());
mListener.removeListenerAlarmsForCachedUid(TEST_CALLING_UID_2);
assertEquals(6, mService.mAlarmStore.size());
}
@Test
public void alarmCountOnListenerCached() {
mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
// Set some alarms for TEST_CALLING_UID.
final int numExactListenerUid1 = 14;
for (int i = 0; i < numExactListenerUid1; i++) {
setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
getNewListener(() -> {}));
}
setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
setTestAlarm(ELAPSED_REALTIME, 54, getNewMockPendingIntent());
setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
// Set some alarms for TEST_CALLING_UID_2.
final int numExactListenerUid2 = 9;
for (int i = 0; i < numExactListenerUid2; i++) {
setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2);
}
setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
setTestAlarm(RTC_WAKEUP, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
TEST_CALLING_UID_2, null);
assertEquals(numExactListenerUid1 + 3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
mListener.removeListenerAlarmsForCachedUid(TEST_CALLING_UID);
assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
mListener.removeListenerAlarmsForCachedUid(TEST_CALLING_UID_2);
assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
}
@Override
public void afterSessionFinished() {
LocalServices.removeServiceForTest(AlarmManagerInternal.class);