diff options
6 files changed, 218 insertions, 134 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index da429af7e351..61424ae0e158 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -270,6 +270,16 @@ public class AlarmManager { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) public static final long ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS = 185199076L; + /** + * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} or above, certain kinds of apps can + * use {@link Manifest.permission#USE_EXACT_ALARM} to schedule exact alarms. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long ENABLE_USE_EXACT_ALARM = 218533173L; + @UnsupportedAppUsage private final IAlarmManager mService; private final Context mContext; 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 a09f39f26eab..f67e8d2baa11 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -1827,30 +1827,9 @@ public class AlarmManagerService extends SystemService { newAppIds.add(UserHandle.getAppId(uid)); } } - final ArraySet<Integer> removed = new ArraySet<>(mExactAlarmCandidates); - removed.removeAll(newAppIds); - // This code is only called on package_added and boot. The set {removed} is only expected to - // be non-empty when a package was updated and it removed the permission from its manifest. - for (int i = 0; i < removed.size(); i++) { - final int removedAppId = removed.valueAt(i); - synchronized (mLock) { - Slog.i(TAG, "App id " + removedAppId + " lost SCHEDULE_EXACT_ALARM on update"); + // Some packages may have lost permission to schedule exact alarms on update, their alarms + // will be removed while handling CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE after this. - final Predicate<Alarm> whichAlarms = a -> { - if (UserHandle.getAppId(a.uid) != removedAppId || a.windowLength != 0) { - return false; - } - if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { - return false; - } - if (hasUseExactAlarmPermission(a.packageName, a.uid)) { - return false; - } - return !isExemptFromExactAlarmPermission(a.uid); - }; - removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); - } - } // No need to lock. Assignment is always atomic. mExactAlarmCandidates = Collections.unmodifiableSet(newAppIds); } @@ -1910,7 +1889,7 @@ public class AlarmManagerService extends SystemService { || !isExactAlarmChangeEnabled(packageName, userId)) { return; } - if (hasUseExactAlarmPermission(packageName, uid)) { + if (hasUseExactAlarmInternal(packageName, uid)) { return; } @@ -2533,8 +2512,9 @@ public class AlarmManagerService extends SystemService { } @Override - public boolean hasScheduleExactAlarm(String packageName, int uid) { - return hasScheduleExactAlarmInternal(packageName, uid); + public boolean hasExactAlarmPermission(String packageName, int uid) { + return hasScheduleExactAlarmInternal(packageName, uid) + || hasUseExactAlarmInternal(packageName, uid); } @Override @@ -2558,21 +2538,19 @@ public class AlarmManagerService extends SystemService { return appOpMode == AppOpsManager.MODE_ALLOWED; } - boolean hasUseExactAlarmPermission(String packageName, int uid) { - return PermissionChecker.checkPermissionForPreflight(getContext(), + boolean hasUseExactAlarmInternal(String packageName, int uid) { + return isUseExactAlarmEnabled(packageName, UserHandle.getUserId(uid)) + && (PermissionChecker.checkPermissionForPreflight(getContext(), Manifest.permission.USE_EXACT_ALARM, PermissionChecker.PID_UNKNOWN, uid, - packageName) == PermissionChecker.PERMISSION_GRANTED; + packageName) == PermissionChecker.PERMISSION_GRANTED); } boolean hasScheduleExactAlarmInternal(String packageName, int uid) { - if (hasUseExactAlarmPermission(packageName, uid)) { - return true; - } + final long start = mStatLogger.getTime(); + // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService. // Not using #mLastOpScheduleExactAlarm as it may contain stale values. // No locking needed as all internal containers being queried are immutable. - - final long start = mStatLogger.getTime(); final boolean hasPermission; if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) { hasPermission = false; @@ -2703,7 +2681,8 @@ public class AlarmManagerService extends SystemService { lowerQuota = allowWhileIdle; idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; } - if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid)) { + if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid) + && !hasUseExactAlarmInternal(callingPackage, callingUid)) { if (!isExemptFromExactAlarmPermission(callingUid)) { final String errorMessage = "Caller " + callingPackage + " needs to hold " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " @@ -2768,7 +2747,8 @@ public class AlarmManagerService extends SystemService { return true; } return isExemptFromExactAlarmPermission(packageUid) - || hasScheduleExactAlarmInternal(packageName, packageUid); + || hasScheduleExactAlarmInternal(packageName, packageUid) + || hasUseExactAlarmInternal(packageName, packageUid); } @Override @@ -2875,6 +2855,11 @@ public class AlarmManagerService extends SystemService { packageName, UserHandle.of(userId)); } + private static boolean isUseExactAlarmEnabled(String packageName, int userId) { + return CompatChanges.isChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, + packageName, UserHandle.of(userId)); + } + @NeverCompile // Avoid size overhead of debugging code. void dumpImpl(IndentingPrintWriter pw) { synchronized (mLock) { @@ -3784,7 +3769,7 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(changedPackage, userId)) { continue; } - if (hasUseExactAlarmPermission(changedPackage, uid)) { + if (hasUseExactAlarmInternal(changedPackage, uid)) { continue; } final int appOpMode; @@ -3817,8 +3802,9 @@ public class AlarmManagerService extends SystemService { } /** - * Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms - * that the app is no longer eligible to use. + * Called when an app loses the permission to use exact alarms. This will happen when the app + * no longer has either {@link Manifest.permission#SCHEDULE_EXACT_ALARM} or + * {@link Manifest.permission#USE_EXACT_ALARM}. * * This is not expected to get called frequently. */ @@ -3828,7 +3814,8 @@ public class AlarmManagerService extends SystemService { || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { return; } - Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); + Slog.w(TAG, "Package " + packageName + ", uid " + uid + + " lost permission to set exact alarms!"); final Predicate<Alarm> whichAlarms = a -> (a.uid == uid && a.packageName.equals(packageName) && a.windowLength == 0); @@ -4764,7 +4751,8 @@ public class AlarmManagerService extends SystemService { case CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE: packageName = (String) msg.obj; uid = msg.arg1; - if (!hasScheduleExactAlarmInternal(packageName, uid)) { + if (!hasScheduleExactAlarmInternal(packageName, uid) + && !hasUseExactAlarmInternal(packageName, uid)) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, packageName, /*killUid = */false); @@ -4936,13 +4924,15 @@ public class AlarmManagerService extends SystemService { mLastOpScheduleExactAlarm.delete(uid); return; case Intent.ACTION_PACKAGE_ADDED: + mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + // Some apps may lose permission to set exact alarms on update. + // We need to remove their exact alarms. final String packageUpdated = intent.getData().getSchemeSpecificPart(); mHandler.obtainMessage( AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE, uid, -1, packageUpdated).sendToTarget(); } - mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); return; case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index b520102b2830..fde59ea1e5f3 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1309,7 +1309,7 @@ public class AppStandbyController return STANDBY_BUCKET_WORKING_SET; } - if (mInjector.hasScheduleExactAlarm(packageName, UserHandle.getUid(userId, appId))) { + if (mInjector.hasExactAlarmPermission(packageName, UserHandle.getUid(userId, appId))) { return STANDBY_BUCKET_WORKING_SET; } } @@ -2326,8 +2326,8 @@ public class AppStandbyController return mWellbeingApp != null && mWellbeingApp.equals(packageName); } - boolean hasScheduleExactAlarm(String packageName, int uid) { - return mAlarmManagerInternal.hasScheduleExactAlarm(packageName, uid); + boolean hasExactAlarmPermission(String packageName, int uid) { + return mAlarmManagerInternal.hasExactAlarmPermission(packageName, uid); } void updatePowerWhitelistCache() { diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index e5ae77a26246..e3c8afabcf6f 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -44,7 +44,8 @@ public interface AlarmManagerInternal { /** * Returns if the given package in the given user holds - * {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} + * {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} or + * {@link android.Manifest.permission#USE_EXACT_ALARM}. */ - boolean hasScheduleExactAlarm(String packageName, int uid); + boolean hasExactAlarmPermission(String packageName, int uid); } 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 4a51e41b1dc5..c9523ec16b8f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -58,6 +58,7 @@ import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION; import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED; +import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_ADDED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES; @@ -1044,9 +1045,19 @@ public class AlarmManagerServiceTest { final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); verify(mService.mHandler, atLeastOnce()).sendMessageAtTime(messageCaptor.capture(), anyLong()); - final Message lastMessage = messageCaptor.getValue(); - assertEquals("Unexpected message send to handler", what, lastMessage.what); - mService.mHandler.handleMessage(lastMessage); + + Message expectedMessage = null; + final ArrayList<Integer> sentMessages = new ArrayList<>(); + for (Message sentMessage : messageCaptor.getAllValues()) { + if (sentMessage.what == what) { + expectedMessage = sentMessage; + } + sentMessages.add(sentMessage.what); + } + + assertNotNull("Unexpected messages sent to handler. Expected: " + what + ", sent: " + + sentMessages, expectedMessage); + mService.mHandler.handleMessage(expectedMessage); } @Test @@ -2109,16 +2120,16 @@ public class AlarmManagerServiceTest { public void hasScheduleExactAlarmBinderCallNotDenyListed() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); + mockScheduleExactAlarmState(true, false, MODE_DEFAULT); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); + mockScheduleExactAlarmState(true, false, MODE_ALLOWED); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, false, MODE_IGNORED); + mockScheduleExactAlarmState(true, false, MODE_IGNORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @@ -2126,16 +2137,16 @@ public class AlarmManagerServiceTest { public void hasScheduleExactAlarmBinderCallDenyListed() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, true, MODE_ERRORED); + mockScheduleExactAlarmState(true, true, MODE_ERRORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); + mockScheduleExactAlarmState(true, true, MODE_DEFAULT); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, true, MODE_IGNORED); + mockScheduleExactAlarmState(true, true, MODE_IGNORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); + mockScheduleExactAlarmState(true, true, MODE_ALLOWED); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @@ -2148,13 +2159,13 @@ public class AlarmManagerServiceTest { public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(false, false, MODE_DEFAULT); + mockScheduleExactAlarmState(false, false, MODE_DEFAULT); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(false, false, MODE_ALLOWED); + mockScheduleExactAlarmState(false, false, MODE_ALLOWED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(false, true, MODE_ALLOWED); + mockScheduleExactAlarmState(false, true, MODE_ALLOWED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @@ -2162,10 +2173,17 @@ public class AlarmManagerServiceTest { public void canScheduleExactAlarmsBinderCallChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); - mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); + // canScheduleExactAlarms should be true regardless of any permission state. + mockUseExactAlarmState(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockUseExactAlarmState(false); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + mockScheduleExactAlarmState(false, true, MODE_DEFAULT); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + mockScheduleExactAlarmState(true, false, MODE_ERRORED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @@ -2173,44 +2191,45 @@ public class AlarmManagerServiceTest { public void canScheduleExactAlarmsBinderCall() throws RemoteException { // Policy permission is denied in setUp(). mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); // No permission, no exemption. - mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); + mockScheduleExactAlarmState(true, true, MODE_DEFAULT); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, no exemption. - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // Policy permission only, no exemption. - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); - doReturn(PermissionChecker.PERMISSION_GRANTED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), eq(TEST_CALLING_UID), - eq(TEST_CALLING_PACKAGE))); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mockUseExactAlarmState(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); - // Permission, no exemption. - mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); + mockUseExactAlarmState(false); + + // User permission only, no exemption. + mockScheduleExactAlarmState(true, false, MODE_DEFAULT); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); - // Permission, no exemption. - mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); + // User permission only, no exemption. + mockScheduleExactAlarmState(true, true, MODE_ALLOWED); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); // No permission, exemption. - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(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); + mockScheduleExactAlarmState(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); + // Both permissions and exemption. + mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mockUseExactAlarmState(true); assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); } @@ -2237,6 +2256,8 @@ public class AlarmManagerServiceTest { verify(mService, never()).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID); + verify(mService, never()).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, + TEST_CALLING_UID); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); } @@ -2313,7 +2334,7 @@ public class AlarmManagerServiceTest { public void alarmClockBinderCall() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); + mockScheduleExactAlarmState(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); @@ -2335,7 +2356,7 @@ public class AlarmManagerServiceTest { assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); } - private void mockExactAlarmPermissionGrant(boolean declared, boolean denyList, int mode) { + private void mockScheduleExactAlarmState(boolean declared, boolean denyList, int mode) { String[] requesters = declared ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING; when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM)) .thenReturn(requesters); @@ -2350,12 +2371,21 @@ public class AlarmManagerServiceTest { TEST_CALLING_PACKAGE)).thenReturn(mode); } + private void mockUseExactAlarmState(boolean granted) { + final int result = granted ? PermissionChecker.PERMISSION_GRANTED + : PermissionChecker.PERMISSION_HARD_DENIED; + doReturn(result).when( + () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), + eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), eq(TEST_CALLING_UID), + eq(TEST_CALLING_PACKAGE))); + } + @Test public void alarmClockBinderCallWithoutPermission() throws RemoteException { setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true); mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2377,7 +2407,7 @@ public class AlarmManagerServiceTest { public void exactBinderCallWithPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); + mockScheduleExactAlarmState(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, null); @@ -2401,7 +2431,7 @@ public class AlarmManagerServiceTest { public void exactBinderCallWithAllowlist() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // If permission is denied, only then allowlist will be checked. - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2421,7 +2451,7 @@ public class AlarmManagerServiceTest { public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); + mockScheduleExactAlarmState(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); @@ -2444,7 +2474,7 @@ public class AlarmManagerServiceTest { public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); // If permission is denied, only then allowlist will be checked. - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2471,7 +2501,7 @@ public class AlarmManagerServiceTest { setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true); mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2503,6 +2533,7 @@ public class AlarmManagerServiceTest { FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); verify(mService, never()).hasScheduleExactAlarmInternal(anyString(), anyInt()); + verify(mService, never()).hasUseExactAlarmInternal(anyString(), anyInt()); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); @@ -2520,7 +2551,7 @@ public class AlarmManagerServiceTest { public void binderCallWithUserAllowlist() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); when(mAppStateTracker.isUidPowerSaveUserExempt(TEST_CALLING_UID)).thenReturn(true); @@ -2792,7 +2823,7 @@ public class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED); - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); @@ -2805,7 +2836,7 @@ public class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED); - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); @@ -2818,7 +2849,7 @@ public class AlarmManagerServiceTest { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED); - mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); + mockScheduleExactAlarmState(true, true, MODE_DEFAULT); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); @@ -2834,7 +2865,7 @@ public class AlarmManagerServiceTest { when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs); mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED); - mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); + mockScheduleExactAlarmState(true, false, MODE_ALLOWED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); @@ -3036,48 +3067,6 @@ public class AlarmManagerServiceTest { } @Test - public void refreshExactAlarmCandidatesRemovesExactAlarmsIfNeeded() { - mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); - - mService.mExactAlarmCandidates = new ArraySet<>(new Integer[]{1, 2, 5}); - final String[] updatedRequesters = new String[]{"p11", "p2", "p9"}; - final Integer[] appIds = new Integer[]{11, 2, 9}; - registerAppIds(updatedRequesters, appIds); - - when(mPermissionManagerInternal.getAppOpPermissionPackages( - SCHEDULE_EXACT_ALARM)).thenReturn(updatedRequesters); - - final PendingIntent exactAppId1 = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId1, 0, 0, - UserHandle.getUid(TEST_CALLING_USER, 1), null); - - final PendingIntent exactAppId2 = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId2, 0, 0, - UserHandle.getUid(TEST_CALLING_USER, 2), null); - - final PendingIntent exactAppId5 = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, 0, 0, exactAppId5, 0, 0, - UserHandle.getUid(TEST_CALLING_USER, 5), null); - - final PendingIntent inexactAppId5 = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME, 0, 23, inexactAppId5, 0, 0, - UserHandle.getUid(TEST_CALLING_USER, 5), null); - - assertEquals(4, mService.mAlarmStore.size()); - - mService.refreshExactAlarmCandidates(); - // App ids 1 and 5 lost the permission, so there alarms should be removed. - - final ArrayList<Alarm> remaining = mService.mAlarmStore.asList(); - assertEquals(2, remaining.size()); - - assertTrue("Inexact alarm removed", - remaining.removeIf(a -> a.matches(inexactAppId5, null))); - assertTrue("Alarm from app id 2 removed", - remaining.removeIf(a -> a.matches(exactAppId2, null))); - } - - @Test public void refreshExactAlarmCandidatesOnPackageAdded() { final String[] exactAlarmRequesters = new String[]{"p11", "p2", "p9"}; final Integer[] appIds = new Integer[]{11, 2, 9}; @@ -3130,6 +3119,78 @@ public class AlarmManagerServiceTest { } @Test + public void exactAlarmsRemovedIfNeededOnPackageReplaced() { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); + + final int otherUid = 2313; + final String otherPackage = "p1"; + + final PendingIntent exactAlarm1 = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 1, 0, exactAlarm1, 0, 0, TEST_CALLING_UID, + TEST_CALLING_PACKAGE, null); + + final PendingIntent exactAlarm2 = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 2, 0, exactAlarm2, 0, 0, TEST_CALLING_UID, + TEST_CALLING_PACKAGE, null); + + final PendingIntent otherPackageExactAlarm = getNewMockPendingIntent(otherUid, + otherPackage); + setTestAlarm(ELAPSED_REALTIME, 0, 0, otherPackageExactAlarm, 0, 0, otherUid, otherPackage, + null); + + final PendingIntent inexactAlarm = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, 23, inexactAlarm, 0, 0, TEST_CALLING_UID, + TEST_CALLING_PACKAGE, null); + + final PendingIntent otherPackageInexactAlarm = getNewMockPendingIntent(otherUid, + otherPackage); + setTestAlarm(ELAPSED_REALTIME, 0, 23, otherPackageInexactAlarm, 0, 0, otherUid, + otherPackage, null); + + assertEquals(5, mService.mAlarmStore.size()); + + final Intent packageReplacedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED) + .setData(Uri.fromParts("package", TEST_CALLING_PACKAGE, null)) + .putExtra(Intent.EXTRA_UID, TEST_CALLING_UID) + .putExtra(Intent.EXTRA_REPLACING, true); + + mockUseExactAlarmState(false); + mockScheduleExactAlarmState(true, false, MODE_ALLOWED); + mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent); + assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE); + + // user permission is granted, no alarms should be removed + assertEquals(5, mService.mAlarmStore.size()); + + mockUseExactAlarmState(true); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent); + assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE); + + // policy permission is granted, no alarms should be removed + assertEquals(5, mService.mAlarmStore.size()); + + mockUseExactAlarmState(false); + mockScheduleExactAlarmState(true, false, MODE_ERRORED); + mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent); + assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE); + + // no permission is granted, exact alarms should be removed + assertEquals(3, mService.mAlarmStore.size()); + + List<Alarm> remaining = mService.mAlarmStore.asList(); + assertTrue("Inexact alarm removed", remaining.removeIf(a -> a.matches(inexactAlarm, null))); + + assertEquals(2, remaining.size()); + assertTrue("Alarms from other package removed", + remaining.removeIf(a -> a.matches(otherPackageExactAlarm, null) + || a.matches(otherPackageInexactAlarm, null))); + + assertEquals(0, remaining.size()); + } + + @Test public void alarmScheduledAtomPushed() { for (int i = 0; i < 10; i++) { final PendingIntent pi = getNewMockPendingIntent(); @@ -3187,6 +3248,28 @@ public class AlarmManagerServiceTest { bOptions.getTemporaryAppAllowlistReasonCode()); } + @Test + public void hasUseExactAlarmPermission() { + mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true); + + mockUseExactAlarmState(true); + assertTrue(mService.hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + + mockUseExactAlarmState(false); + assertFalse(mService.hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + } + + @Test + public void hasUseExactAlarmPermissionChangeDisabled() { + mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, false); + + mockUseExactAlarmState(true); + assertFalse(mService.hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + + mockUseExactAlarmState(false); + assertFalse(mService.hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID)); + } + @After public void tearDown() { if (mMockingSession != null) { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 17464a6551d4..fb4d84cc75f2 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -283,7 +283,7 @@ public class AppStandbyControllerTests { } @Override - boolean hasScheduleExactAlarm(String packageName, int uid) { + boolean hasExactAlarmPermission(String packageName, int uid) { return mClockApps.contains(Pair.create(packageName, uid)); } |