summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/AlarmManager.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java74
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java6
-rw-r--r--services/core/java/com/android/server/AlarmManagerInternal.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java255
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java2
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));
}