Merge "Add a metrics reason code for USE_EXACT_ALARM" into tm-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index f5167b7..2e6b8bd 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -88,6 +88,10 @@
* Change wasn't enable for the caller due to compat reasons.
*/
static final int EXACT_ALLOW_REASON_COMPAT = 2;
+ /**
+ * Caller had USE_EXACT_ALARM permission.
+ */
+ static final int EXACT_ALLOW_REASON_POLICY_PERMISSION = 3;
public final int type;
/**
@@ -275,6 +279,8 @@
return "compat";
case EXACT_ALLOW_REASON_PERMISSION:
return "permission";
+ case EXACT_ALLOW_REASON_POLICY_PERMISSION:
+ return "policy_permission";
case EXACT_ALLOW_REASON_NOT_APPLICABLE:
return "N/A";
default:
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 4aa9e84..7baf805 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -46,6 +46,7 @@
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
+import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_POLICY_PERMISSION;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
import static com.android.server.alarm.Alarm.TARE_POLICY_INDEX;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED;
@@ -2698,8 +2699,7 @@
// Make sure the caller is allowed to use the requested kind of alarm, and also
// decide what quota and broadcast options to use.
- boolean allowListed = false; // For logging the reason.
- boolean changeDisabled = false; // For logging the reason.
+ int exactAllowReason = EXACT_ALLOW_REASON_NOT_APPLICABLE;
Bundle idleOptions = null;
if ((flags & FLAG_PRIORITIZE) != 0) {
getContext().enforcePermission(
@@ -2721,57 +2721,56 @@
idleOptions = mOptsWithoutFgs.toBundle();
}
} else {
- changeDisabled = true;
needsPermission = false;
lowerQuota = allowWhileIdle;
idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
- }
- if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid)
- && !hasUseExactAlarmInternal(callingPackage, callingUid)) {
- if (!isExemptFromExactAlarmPermissionNoLock(callingUid)) {
- final String errorMessage = "Caller " + callingPackage + " needs to hold "
- + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
- + "exact alarms.";
- if (mConstants.CRASH_NON_CLOCK_APPS) {
- throw new SecurityException(errorMessage);
- } else {
- Slog.wtf(TAG, errorMessage);
- }
- } else {
- allowListed = true;
+ if (exact) {
+ exactAllowReason = EXACT_ALLOW_REASON_COMPAT;
}
- // 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;
+ }
+ if (needsPermission) {
+ if (hasUseExactAlarmInternal(callingPackage, callingUid)) {
+ exactAllowReason = EXACT_ALLOW_REASON_POLICY_PERMISSION;
+ } else if (hasScheduleExactAlarmInternal(callingPackage, callingUid)) {
+ exactAllowReason = EXACT_ALLOW_REASON_PERMISSION;
+ } else {
+ if (isExemptFromExactAlarmPermissionNoLock(callingUid)) {
+ exactAllowReason = EXACT_ALLOW_REASON_ALLOW_LIST;
+ } else {
+ final String errorMessage =
+ "Caller " + callingPackage + " needs to hold "
+ + Manifest.permission.SCHEDULE_EXACT_ALARM + " or "
+ + Manifest.permission.USE_EXACT_ALARM + " to set "
+ + "exact alarms.";
+ if (mConstants.CRASH_NON_CLOCK_APPS) {
+ throw new SecurityException(errorMessage);
+ } else {
+ Slog.wtf(TAG, errorMessage);
+ }
+ }
+ // 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;
+ }
}
if (lowerQuota) {
flags &= ~FLAG_ALLOW_WHILE_IDLE;
flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
}
}
- final int exactAllowReason;
if (exact) {
// If this is an exact time alarm, then it can't be batched with other alarms.
flags |= AlarmManager.FLAG_STANDALONE;
- if (changeDisabled) {
- exactAllowReason = EXACT_ALLOW_REASON_COMPAT;
- } else if (allowListed) {
- exactAllowReason = EXACT_ALLOW_REASON_ALLOW_LIST;
- } else {
- exactAllowReason = EXACT_ALLOW_REASON_PERMISSION;
- }
- } else {
- exactAllowReason = EXACT_ALLOW_REASON_NOT_APPLICABLE;
}
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
index 4c2f8d1..75ed616 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
@@ -20,6 +20,7 @@
import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__CHANGE_DISABLED;
import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__NOT_APPLICABLE;
import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__PERMISSION;
+import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__POLICY_PERMISSION;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import android.app.ActivityManager;
@@ -89,6 +90,8 @@
return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__PERMISSION;
case Alarm.EXACT_ALLOW_REASON_COMPAT:
return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__CHANGE_DISABLED;
+ case Alarm.EXACT_ALLOW_REASON_POLICY_PERMISSION:
+ return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__POLICY_PERMISSION;
default:
return ALARM_SCHEDULED__EXACT_ALARM_ALLOWED_REASON__NOT_APPLICABLE;
}
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 17b4226..cdc9d71 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -56,6 +56,7 @@
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
+import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_POLICY_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;
@@ -2337,7 +2338,7 @@
}
@Test
- public void alarmClockBinderCall() throws RemoteException {
+ public void alarmClockBinderCallWithSEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
@@ -2362,6 +2363,34 @@
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
+ @Test
+ public void alarmClockBinderCallWithUEAPermission() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+
+ mockUseExactAlarmState(true);
+ mockScheduleExactAlarmState(false, false, MODE_ERRORED);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
+ mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ alarmPi, null, null, null, alarmClock);
+
+ // Correct permission checks are invoked.
+ verify(mService).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ 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),
+ bundleCaptor.capture(), eq(EXACT_ALLOW_REASON_POLICY_PERMISSION));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
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))
@@ -2410,7 +2439,7 @@
}
@Test
- public void exactBinderCallWithPermission() throws RemoteException {
+ public void exactBinderCallWithSEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
@@ -2434,6 +2463,32 @@
}
@Test
+ public void exactBinderCallWithUEAPermission() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+
+ mockUseExactAlarmState(true);
+ mockScheduleExactAlarmState(false, false, MODE_ERRORED);
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, alarmPi, null, null, null, null);
+
+ verify(mService).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture(),
+ eq(EXACT_ALLOW_REASON_POLICY_PERMISSION));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
public void exactBinderCallWithAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
// If permission is denied, only then allowlist will be checked.
@@ -2454,7 +2509,7 @@
}
@Test
- public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException {
+ public void exactAllowWhileIdleBinderCallWithSEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
@@ -2462,6 +2517,7 @@
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+ verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -2477,6 +2533,32 @@
}
@Test
+ public void exactAllowWhileIdleBinderCallWithUEAPermission() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+
+ mockUseExactAlarmState(true);
+ mockScheduleExactAlarmState(false, false, MODE_ERRORED);
+ 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);
+
+ verify(mService).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(),
+ eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture(),
+ eq(EXACT_ALLOW_REASON_POLICY_PERMISSION));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
// If permission is denied, only then allowlist will be checked.