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

Commit ccb699c0 authored by Gaurav Gupta's avatar Gaurav Gupta
Browse files

Extend rapid cancelling notification AppOp to include snoozed

notifications and rework time arithmetic to prevent overflow

Bug: 289080543
Test: unit testing included
Change-Id: I242ccd0dad84d97dfc65aa104be057425fca9eeb
parent 1c9ab252
Loading
Loading
Loading
Loading
+41 −32
Original line number Diff line number Diff line
@@ -4877,14 +4877,14 @@ public class NotificationManagerService extends SystemService {
                                continue;
                            }
                            notificationsRapidlyCleared = notificationsRapidlyCleared
                                    || isNotificationRecent(r);
                                    || isNotificationRecent(r.getUpdateTimeMs());
                            cancelNotificationFromListenerLocked(info, callingUid, callingPid,
                                    r.getSbn().getPackageName(), r.getSbn().getTag(),
                                    r.getSbn().getId(), userId, reason);
                        }
                    } else {
                        for (NotificationRecord notificationRecord : mNotificationList) {
                            if (isNotificationRecent(notificationRecord)) {
                            if (isNotificationRecent(notificationRecord.getUpdateTimeMs())) {
                                notificationsRapidlyCleared = true;
                                break;
                            }
@@ -4910,14 +4910,6 @@ public class NotificationManagerService extends SystemService {
            }
        }
        private boolean isNotificationRecent(@NonNull NotificationRecord notificationRecord) {
            if (!rapidClearNotificationsByListenerAppOpEnabled()) {
                return false;
            }
            return notificationRecord.getFreshnessMs(System.currentTimeMillis())
                    < NOTIFICATION_RAPID_CLEAR_THRESHOLD_MS;
        }
        /**
         * Handle request from an approved listener to re-enable itself.
         *
@@ -5041,12 +5033,11 @@ public class NotificationManagerService extends SystemService {
        @Override
        public void snoozeNotificationUntilContextFromListener(INotificationListener token,
                String key, String snoozeCriterionId) {
            final int callingUid = Binder.getCallingUid();
            final long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mNotificationLock) {
                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                    snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
                }
                snoozeNotificationInt(callingUid, token, key, SNOOZE_UNTIL_UNSPECIFIED,
                        snoozeCriterionId);
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -5060,12 +5051,10 @@ public class NotificationManagerService extends SystemService {
        @Override
        public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
                long duration) {
            final int callingUid = Binder.getCallingUid();
            final long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mNotificationLock) {
                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                    snoozeNotificationInt(key, duration, null, info);
                }
                snoozeNotificationInt(callingUid, token, key, duration, null);
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -10306,16 +10295,22 @@ public class NotificationManagerService extends SystemService {
        }
    }
    void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
            ManagedServiceInfo listener) {
    void snoozeNotificationInt(int callingUid, INotificationListener token, String key,
            long duration, String snoozeCriterionId) {
        final String packageName;
        final long notificationUpdateTimeMs;
        synchronized (mNotificationLock) {
            final ManagedServiceInfo listener = mListeners.checkServiceTokenLocked(token);
            if (listener == null) {
                return;
            }
            packageName = listener.component.getPackageName();
            String listenerName = listener.component.toShortString();
            if ((duration <= 0 && snoozeCriterionId == null) || key == null) {
                return;
            }
        synchronized (mNotificationLock) {
            final NotificationRecord r = findInCurrentAndSnoozedNotificationByKeyLocked(key);
            if (r == null) {
                return;
@@ -10323,7 +10318,7 @@ public class NotificationManagerService extends SystemService {
            if (!listener.enabledAndUserMatches(r.getSbn().getNormalizedUserId())){
                return;
            }
        }
            notificationUpdateTimeMs = r.getUpdateTimeMs();
            if (DBG) {
                Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
@@ -10333,6 +10328,12 @@ public class NotificationManagerService extends SystemService {
            mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
        }
        if (isNotificationRecent(notificationUpdateTimeMs)) {
            mAppOps.noteOpNoThrow(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
                    callingUid, packageName, /* attributionTag= */ null, /* message= */ null);
        }
    }
    void unsnoozeNotificationInt(String key, ManagedServiceInfo listener, boolean muteOnReturn) {
        String listenerName = listener == null ? null : listener.component.toShortString();
        if (DBG) {
@@ -10342,6 +10343,14 @@ public class NotificationManagerService extends SystemService {
        handleSavePolicyFile();
    }
    private boolean isNotificationRecent(long notificationUpdateTimeMs) {
        if (!rapidClearNotificationsByListenerAppOpEnabled()) {
            return false;
        }
        return System.currentTimeMillis() - notificationUpdateTimeMs
                < NOTIFICATION_RAPID_CLEAR_THRESHOLD_MS;
    }
    @GuardedBy("mNotificationLock")
    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
            ManagedServiceInfo listener, boolean includeCurrentProfiles, int mustNotHaveFlags) {
+108 −2
Original line number Diff line number Diff line
@@ -4116,7 +4116,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
        mService.snoozeNotificationInt(r.getKey(), 1000, null, mListener);
        mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
                r.getKey(), 1000, null);
        verify(mWorkerHandler, never()).post(
                any(NotificationManagerService.SnoozeNotificationRunnable.class));
@@ -4134,12 +4135,117 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
        mService.snoozeNotificationInt(r2.getKey(), 1000, null, mListener);
        mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
                r2.getKey(), 1000, null);
        verify(mWorkerHandler).post(
                any(NotificationManagerService.SnoozeNotificationRunnable.class));
    }
    @Test
    public void snoozeNotificationInt_rapidSnooze_new() {
        mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
                .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
        // Create recent notification.
        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
                System.currentTimeMillis());
        mService.addNotification(nr1);
        mListener = mock(ManagedServices.ManagedServiceInfo.class);
        mListener.component = new ComponentName(PKG, PKG);
        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
        mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
                nr1.getKey(), 1000, null);
        verify(mWorkerHandler).post(
                any(NotificationManagerService.SnoozeNotificationRunnable.class));
        // Ensure cancel event is logged.
        verify(mAppOpsManager).noteOpNoThrow(
                AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER, mUid, PKG, null,
                null);
    }
    @Test
    public void snoozeNotificationInt_rapidSnooze_old() {
        mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
                .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
        // Create old notification.
        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
                System.currentTimeMillis() - 60000);
        mService.addNotification(nr1);
        mListener = mock(ManagedServices.ManagedServiceInfo.class);
        mListener.component = new ComponentName(PKG, PKG);
        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
        mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
                nr1.getKey(), 1000, null);
        verify(mWorkerHandler).post(
                any(NotificationManagerService.SnoozeNotificationRunnable.class));
        // Ensure cancel event is not logged.
        verify(mAppOpsManager, never()).noteOpNoThrow(
                eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
                any(), any());
    }
    @Test
    public void snoozeNotificationInt_rapidSnooze_new_flagDisabled() {
        mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
                .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
        // Create recent notification.
        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
                System.currentTimeMillis());
        mService.addNotification(nr1);
        mListener = mock(ManagedServices.ManagedServiceInfo.class);
        mListener.component = new ComponentName(PKG, PKG);
        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
        mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
                nr1.getKey(), 1000, null);
        verify(mWorkerHandler).post(
                any(NotificationManagerService.SnoozeNotificationRunnable.class));
        // Ensure cancel event is not logged.
        verify(mAppOpsManager, never()).noteOpNoThrow(
                eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
                any(), any());
    }
    @Test
    public void snoozeNotificationInt_rapidSnooze_old_flagDisabled() {
        mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
                .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
        // Create old notification.
        final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
                System.currentTimeMillis() - 60000);
        mService.addNotification(nr1);
        mListener = mock(ManagedServices.ManagedServiceInfo.class);
        mListener.component = new ComponentName(PKG, PKG);
        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
        mService.snoozeNotificationInt(Binder.getCallingUid(), mock(INotificationListener.class),
                nr1.getKey(), 1000, null);
        verify(mWorkerHandler).post(
                any(NotificationManagerService.SnoozeNotificationRunnable.class));
        // Ensure cancel event is not logged.
        verify(mAppOpsManager, never()).noteOpNoThrow(
                eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
                any(), any());
    }
    @Test
    public void testSnoozeRunnable_tooManySnoozed_singleNotification() {
        final NotificationRecord notification = generateNotificationRecord(