Add a metrics reason code for USE_EXACT_ALARM
Updating the reason code logging to better differentiate between usage of both the permissions. The only functional changes expected from this change are the values of exactAllowReason in an alarm: - Prioritized alarms change from "Permission" to "Not applicable" - When crashing is turned off, reason code changes from "Permission" to "Not applicable" - When the caller has USE_EXACT_ALARM, reason code changes from "Permission" to "Policy Permission". Also updating the error message to include USE_EXACT_ALARM. Test: atest FrameworksMockingServicesTests:AlarmManagerServiceTest atest CtsStatsdAtomHostTestCases:AlarmStatsTest Existing tests should pass: atest CtsAlarmManagerTestCases Manually test output of `./out/host/linux-x86/bin/statsd_testdrive 368` and `adb shell dumpsys alarm` Bug: 231661615 Change-Id: I13239c300cfb468e7fc2df21fccb494fdf8c80ac
This commit is contained in:
@@ -88,6 +88,10 @@ class Alarm {
|
||||
* 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 @@ class Alarm {
|
||||
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:
|
||||
|
||||
@@ -46,6 +46,7 @@ import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_ALLOW_LIST;
|
||||
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 @@ public class AlarmManagerService extends SystemService {
|
||||
|
||||
// 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 @@ public class AlarmManagerService extends SystemService {
|
||||
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 (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 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,
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.internal.util.FrameworkStatsLog.ALARM_SCHEDULED__EXACT
|
||||
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 @@ class MetricsHelper {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_ALLOW_LIST;
|
||||
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 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@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 @@ public class AlarmManagerServiceTest {
|
||||
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 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exactBinderCallWithPermission() throws RemoteException {
|
||||
public void exactBinderCallWithSEAPermission() throws RemoteException {
|
||||
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
|
||||
|
||||
mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
|
||||
@@ -2433,6 +2462,32 @@ public class AlarmManagerServiceTest {
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -2454,7 +2509,7 @@ public class AlarmManagerServiceTest {
|
||||
}
|
||||
|
||||
@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 @@ public class AlarmManagerServiceTest {
|
||||
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);
|
||||
@@ -2476,6 +2532,32 @@ public class AlarmManagerServiceTest {
|
||||
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
Reference in New Issue
Block a user