diff options
2 files changed, 149 insertions, 34 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 308d441fb871..5d699b557aa4 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -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) { - if (listener == null) { - return; - } - String listenerName = listener.component.toShortString(); - if ((duration <= 0 && snoozeCriterionId == null) || key == null) { - return; - } + 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; + } + final NotificationRecord r = findInCurrentAndSnoozedNotificationByKeyLocked(key); if (r == null) { return; @@ -10323,14 +10318,20 @@ 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, + snoozeCriterionId, listenerName)); + } + // Needs to post so that it can cancel notifications not yet enqueued. + mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId)); } - if (DBG) { - Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration, - snoozeCriterionId, listenerName)); + if (isNotificationRecent(notificationUpdateTimeMs)) { + mAppOps.noteOpNoThrow(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER, + callingUid, packageName, /* attributionTag= */ null, /* message= */ null); } - // Needs to post so that it can cancel notifications not yet enqueued. - mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId)); } void unsnoozeNotificationInt(String key, ManagedServiceInfo listener, boolean muteOnReturn) { @@ -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) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9c2cba8ecf96..f82b6a41f35d 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -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,13 +4135,118 @@ 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( mTestNotificationChannel, 1, null, true); |