diff options
author | 2022-05-18 16:11:37 -0700 | |
---|---|---|
committer | 2022-05-18 23:42:37 -0700 | |
commit | 4d56d13e01d2f752e7c6f30ad7c09c7973e32d69 (patch) | |
tree | d3edbf7a1d845bdc02d1ec397fb162423ba984fe | |
parent | 92e59dde2f7745fd830d716cc41ebec83f9d65f5 (diff) |
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
4 files changed, 129 insertions, 39 deletions
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 f5167b714d8a..2e6b8bdb0ad4 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 @@ 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: 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 4aa9e84c10a9..7baf80502a3c 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_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 (exact) { + exactAllowReason = EXACT_ALLOW_REASON_COMPAT; + } } - 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); + 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 { - Slog.wtf(TAG, errorMessage); + 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); + } } - } else { - allowListed = true; + // 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, 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 4c2f8d124566..75ed616e2d96 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 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; } 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 17b42260948d..cdc9d7198cb5 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_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); @@ -2434,6 +2463,32 @@ public class AlarmManagerServiceTest { } @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 @@ 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); @@ -2477,6 +2533,32 @@ public class AlarmManagerServiceTest { } @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. |