Loading apex/jobscheduler/framework/java/android/app/AlarmManager.java +14 −5 Original line number Diff line number Diff line Loading @@ -1294,22 +1294,31 @@ public class AlarmManager { /** * Called to check if the caller can schedule exact alarms. * Your app schedules exact alarms when it calls any of the {@code setExact...} or * {@link #setAlarmClock(AlarmClockInfo, PendingIntent) setAlarmClock} API methods. * <p> * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms if they * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. These apps can also * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms only if they * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission or they are on the * device's power-save exemption list. * These apps can also * start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to * request this from the user. * request this permission from the user. * <p> * Apps targeting lower sdk versions, can always schedule exact alarms. * * @return {@code true} if the caller can schedule exact alarms. * @return {@code true} if the caller can schedule exact alarms, {@code false} otherwise. * @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM * @see #setExact(int, long, PendingIntent) * @see #setExactAndAllowWhileIdle(int, long, PendingIntent) * @see #setAlarmClock(AlarmClockInfo, PendingIntent) * @see android.os.PowerManager#isIgnoringBatteryOptimizations(String) */ public boolean canScheduleExactAlarms() { return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId()); try { return mService.canScheduleExactAlarms(mContext.getOpPackageName()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** Loading apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ interface IAlarmManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); long currentNetworkTimeMillis(); boolean canScheduleExactAlarms(String packageName); boolean hasScheduleExactAlarm(String packageName, int userId); int getConfigVersion(); } apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +34 −17 Original line number Diff line number Diff line Loading @@ -1740,7 +1740,7 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { return false; } return a.alarmClock != null || !isExemptFromExactAlarmPermission(a.uid); return !isExemptFromExactAlarmPermission(a.uid); }; removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); } Loading Loading @@ -2414,6 +2414,7 @@ public class AlarmManagerService extends SystemService { /** * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, * allow-while-idle alarms. * Note: It is ok to call this method without the lock {@link #mLock} held. */ boolean isExemptFromExactAlarmPermission(int uid) { return (UserHandle.isSameApp(mSystemUiUid, uid) Loading Loading @@ -2515,7 +2516,7 @@ public class AlarmManagerService extends SystemService { idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; } if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid)) { if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) { if (!isExemptFromExactAlarmPermission(callingUid)) { final String errorMessage = "Caller " + callingPackage + " needs to hold " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " + "exact alarms."; Loading @@ -2527,10 +2528,16 @@ public class AlarmManagerService extends SystemService { } else { allowListed = true; } // If the app is on the full system power allow-list (not except-idle), or we're // in a soft failure mode, we still allow the alarms. // We give temporary allowlist to allow-while-idle alarms but without FGS // capability. Note that apps that are in the power allow-list do not need it. // If the app is on the full system power allow-list (not except-idle), or the // user-elected allow-list, or we're in a soft failure mode, we still allow the // alarms. // In both cases, ALLOW_WHILE_IDLE alarms get a lower quota equivalent to what // pre-S apps got. Note that user-allow-listed apps don't use the flag // ALLOW_WHILE_IDLE. // We grant temporary allow-list to allow-while-idle alarms but without FGS // capability. AlarmClock alarms do not get the temporary allow-list. This is // consistent with pre-S behavior. Note that apps that are in either of the // power-save allow-lists do not need it. idleOptions = allowWhileIdle ? mOptsWithoutFgs.toBundle() : null; lowerQuota = allowWhileIdle; } Loading Loading @@ -2560,6 +2567,22 @@ public class AlarmManagerService extends SystemService { idleOptions, exactAllowReason); } @Override public boolean canScheduleExactAlarms(String packageName) { final int callingUid = mInjector.getCallingUid(); final int userId = UserHandle.getUserId(callingUid); final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); if (callingUid != packageUid) { throw new SecurityException("Uid " + callingUid + " cannot query canScheduleExactAlarms for package " + packageName); } if (!isExactAlarmChangeEnabled(packageName, userId)) { return true; } return isExemptFromExactAlarmPermission(packageUid) || hasScheduleExactAlarmInternal(packageName, packageUid); } @Override public boolean hasScheduleExactAlarm(String packageName, int userId) { final int callingUid = mInjector.getCallingUid(); Loading @@ -2572,9 +2595,6 @@ public class AlarmManagerService extends SystemService { throw new SecurityException("Uid " + callingUid + " cannot query hasScheduleExactAlarm for uid " + uid); } if (!isExactAlarmChangeEnabled(packageName, userId)) { return true; } return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false; } Loading Loading @@ -3577,17 +3597,14 @@ public class AlarmManagerService extends SystemService { * This is not expected to get called frequently. */ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { if (isExemptFromExactAlarmPermission(uid) || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { return; } Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); final Predicate<Alarm> whichAlarms = a -> { if (a.uid == uid && a.packageName.equals(packageName) && a.windowLength == 0) { return a.alarmClock != null || !isExemptFromExactAlarmPermission(uid); } return false; }; final Predicate<Alarm> whichAlarms = a -> (a.uid == uid && a.packageName.equals(packageName) && a.windowLength == 0); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { Loading services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +57 −18 Original line number Diff line number Diff line Loading @@ -1910,17 +1910,6 @@ public class AlarmManagerServiceTest { assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @Test public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } private void mockChangeEnabled(long changeId, boolean enabled) { doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(), any(UserHandle.class))); Loading @@ -1940,6 +1929,53 @@ public class AlarmManagerServiceTest { assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @Test public void canScheduleExactAlarmsBinderCallChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @Test public void canScheduleExactAlarmsBinderCall() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // No permission, no exemption. mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Permission, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Permission, no exemption. mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false); doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID)); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Both permission and exemption. mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @Test public void noPermissionCheckWhenChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); Loading Loading @@ -2086,14 +2122,17 @@ public class AlarmManagerServiceTest { final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); try { mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, alarmClock); fail("alarm clock binder call succeeded without permission"); } catch (SecurityException se) { // Expected. } verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); // Correct permission checks are invoked. verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID); verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE), isNull(), eq(alarmClock), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), isNull(), eq(EXACT_ALLOW_REASON_ALLOW_LIST)); } @Test Loading Loading
apex/jobscheduler/framework/java/android/app/AlarmManager.java +14 −5 Original line number Diff line number Diff line Loading @@ -1294,22 +1294,31 @@ public class AlarmManager { /** * Called to check if the caller can schedule exact alarms. * Your app schedules exact alarms when it calls any of the {@code setExact...} or * {@link #setAlarmClock(AlarmClockInfo, PendingIntent) setAlarmClock} API methods. * <p> * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms if they * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. These apps can also * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms only if they * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission or they are on the * device's power-save exemption list. * These apps can also * start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to * request this from the user. * request this permission from the user. * <p> * Apps targeting lower sdk versions, can always schedule exact alarms. * * @return {@code true} if the caller can schedule exact alarms. * @return {@code true} if the caller can schedule exact alarms, {@code false} otherwise. * @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM * @see #setExact(int, long, PendingIntent) * @see #setExactAndAllowWhileIdle(int, long, PendingIntent) * @see #setAlarmClock(AlarmClockInfo, PendingIntent) * @see android.os.PowerManager#isIgnoringBatteryOptimizations(String) */ public boolean canScheduleExactAlarms() { return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId()); try { return mService.canScheduleExactAlarms(mContext.getOpPackageName()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** Loading
apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ interface IAlarmManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); long currentNetworkTimeMillis(); boolean canScheduleExactAlarms(String packageName); boolean hasScheduleExactAlarm(String packageName, int userId); int getConfigVersion(); }
apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +34 −17 Original line number Diff line number Diff line Loading @@ -1740,7 +1740,7 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { return false; } return a.alarmClock != null || !isExemptFromExactAlarmPermission(a.uid); return !isExemptFromExactAlarmPermission(a.uid); }; removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); } Loading Loading @@ -2414,6 +2414,7 @@ public class AlarmManagerService extends SystemService { /** * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, * allow-while-idle alarms. * Note: It is ok to call this method without the lock {@link #mLock} held. */ boolean isExemptFromExactAlarmPermission(int uid) { return (UserHandle.isSameApp(mSystemUiUid, uid) Loading Loading @@ -2515,7 +2516,7 @@ public class AlarmManagerService extends SystemService { idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; } if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid)) { if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) { if (!isExemptFromExactAlarmPermission(callingUid)) { final String errorMessage = "Caller " + callingPackage + " needs to hold " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " + "exact alarms."; Loading @@ -2527,10 +2528,16 @@ public class AlarmManagerService extends SystemService { } else { allowListed = true; } // If the app is on the full system power allow-list (not except-idle), or we're // in a soft failure mode, we still allow the alarms. // We give temporary allowlist to allow-while-idle alarms but without FGS // capability. Note that apps that are in the power allow-list do not need it. // If the app is on the full system power allow-list (not except-idle), or the // user-elected allow-list, or we're in a soft failure mode, we still allow the // alarms. // In both cases, ALLOW_WHILE_IDLE alarms get a lower quota equivalent to what // pre-S apps got. Note that user-allow-listed apps don't use the flag // ALLOW_WHILE_IDLE. // We grant temporary allow-list to allow-while-idle alarms but without FGS // capability. AlarmClock alarms do not get the temporary allow-list. This is // consistent with pre-S behavior. Note that apps that are in either of the // power-save allow-lists do not need it. idleOptions = allowWhileIdle ? mOptsWithoutFgs.toBundle() : null; lowerQuota = allowWhileIdle; } Loading Loading @@ -2560,6 +2567,22 @@ public class AlarmManagerService extends SystemService { idleOptions, exactAllowReason); } @Override public boolean canScheduleExactAlarms(String packageName) { final int callingUid = mInjector.getCallingUid(); final int userId = UserHandle.getUserId(callingUid); final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); if (callingUid != packageUid) { throw new SecurityException("Uid " + callingUid + " cannot query canScheduleExactAlarms for package " + packageName); } if (!isExactAlarmChangeEnabled(packageName, userId)) { return true; } return isExemptFromExactAlarmPermission(packageUid) || hasScheduleExactAlarmInternal(packageName, packageUid); } @Override public boolean hasScheduleExactAlarm(String packageName, int userId) { final int callingUid = mInjector.getCallingUid(); Loading @@ -2572,9 +2595,6 @@ public class AlarmManagerService extends SystemService { throw new SecurityException("Uid " + callingUid + " cannot query hasScheduleExactAlarm for uid " + uid); } if (!isExactAlarmChangeEnabled(packageName, userId)) { return true; } return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false; } Loading Loading @@ -3577,17 +3597,14 @@ public class AlarmManagerService extends SystemService { * This is not expected to get called frequently. */ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { if (isExemptFromExactAlarmPermission(uid) || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { return; } Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); final Predicate<Alarm> whichAlarms = a -> { if (a.uid == uid && a.packageName.equals(packageName) && a.windowLength == 0) { return a.alarmClock != null || !isExemptFromExactAlarmPermission(uid); } return false; }; final Predicate<Alarm> whichAlarms = a -> (a.uid == uid && a.packageName.equals(packageName) && a.windowLength == 0); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { Loading
services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +57 −18 Original line number Diff line number Diff line Loading @@ -1910,17 +1910,6 @@ public class AlarmManagerServiceTest { assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @Test public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } private void mockChangeEnabled(long changeId, boolean enabled) { doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(), any(UserHandle.class))); Loading @@ -1940,6 +1929,53 @@ public class AlarmManagerServiceTest { assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @Test public void canScheduleExactAlarmsBinderCallChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @Test public void canScheduleExactAlarmsBinderCall() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // No permission, no exemption. mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Permission, no exemption. mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Permission, no exemption. mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, exemption. mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false); doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID)); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Both permission and exemption. mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @Test public void noPermissionCheckWhenChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); Loading Loading @@ -2086,14 +2122,17 @@ public class AlarmManagerServiceTest { final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); try { mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, alarmClock); fail("alarm clock binder call succeeded without permission"); } catch (SecurityException se) { // Expected. } verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); // Correct permission checks are invoked. verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID); verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE), isNull(), eq(alarmClock), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), isNull(), eq(EXACT_ALLOW_REASON_ALLOW_LIST)); } @Test Loading