Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d56d13e authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

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
parent 92e59dde
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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:
+35 −36
Original line number Diff line number Diff line
@@ -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 "
                }
                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);
                            }
                    } 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
                        // 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.
                        // 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,
+3 −0
Original line number Diff line number Diff line
@@ -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;
        }
+85 −3
Original line number Diff line number Diff line
@@ -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);