Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +138 −43 Original line number Diff line number Diff line Loading @@ -44,12 +44,16 @@ import static java.util.Objects.requireNonNull; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Notification; import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.Pair; import androidx.annotation.NonNull; Loading Loading @@ -191,38 +195,36 @@ public class NotifCollection implements Dumpable { } /** * Dismiss a notification on behalf of the user. * Dismisses multiple notifications on behalf of the user. */ public void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) { public void dismissNotifications( List<Pair<NotificationEntry, DismissedByUserStats>> entriesToDismiss) { Assert.isMainThread(); requireNonNull(stats); checkForReentrantCall(); final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>(); for (int i = 0; i < entriesToDismiss.size(); i++) { NotificationEntry entry = entriesToDismiss.get(i).first; DismissedByUserStats stats = entriesToDismiss.get(i).second; requireNonNull(stats); if (entry != mNotificationSet.get(entry.getKey())) { throw new IllegalStateException("Invalid entry: " + entry.getKey()); } if (entry.getDismissState() == DISMISSED) { return; continue; } updateDismissInterceptors(entry); if (isDismissIntercepted(entry)) { mLogger.logNotifDismissedIntercepted(entry.getKey()); return; continue; } // Optimistically mark the notification as dismissed -- we'll wait for the signal from // system server before removing it from our notification set. entry.setDismissState(DISMISSED); mLogger.logNotifDismissed(entry.getKey()); List<NotificationEntry> canceledEntries = new ArrayList<>(); if (isCanceled(entry)) { canceledEntries.add(entry); } else { // Ask system server to remove it for us entriesToLocallyDismiss.add(entry); if (!isCanceled(entry)) { // send message to system server if this notification hasn't already been cancelled try { mStatusBarService.onNotificationClear( entry.getSbn().getPackageName(), Loading @@ -236,11 +238,74 @@ public class NotifCollection implements Dumpable { } catch (RemoteException e) { // system process is dead if we're here. } } } locallyDismissNotifications(entriesToLocallyDismiss); rebuildList(); } /** * Dismisses a single notification on behalf of the user. */ public void dismissNotification( NotificationEntry entry, @NonNull DismissedByUserStats stats) { dismissNotifications(List.of( new Pair<NotificationEntry, DismissedByUserStats>(entry, stats))); } /** * Dismisses all clearable notifications for a given userid on behalf of the user. */ public void dismissAllNotifications(@UserIdInt int userId) { Assert.isMainThread(); checkForReentrantCall(); try { mStatusBarService.onClearAllNotifications(userId); } catch (RemoteException e) { // system process is dead if we're here. } // Also mark any children as dismissed as system server will auto-dismiss them as well final List<NotificationEntry> entries = new ArrayList(getActiveNotifs()); for (int i = entries.size() - 1; i >= 0; i--) { NotificationEntry entry = entries.get(i); if (!shouldDismissOnClearAll(entry, userId)) { // system server won't be removing these notifications, but we still give dismiss // interceptors the chance to filter the notification updateDismissInterceptors(entry); if (isDismissIntercepted(entry)) { mLogger.logNotifClearAllDismissalIntercepted(entry.getKey()); } entries.remove(i); } } locallyDismissNotifications(entries); rebuildList(); } /** * Optimistically marks the given notifications as dismissed -- we'll wait for the signal * from system server before removing it from our notification set. */ private void locallyDismissNotifications(List<NotificationEntry> entries) { final List<NotificationEntry> canceledEntries = new ArrayList<>(); for (int i = 0; i < entries.size(); i++) { NotificationEntry entry = entries.get(i); entry.setDismissState(DISMISSED); mLogger.logNotifDismissed(entry.getKey()); if (isCanceled(entry)) { canceledEntries.add(entry); } else { // Mark any children as dismissed as system server will auto-dismiss them as well if (entry.getSbn().getNotification().isGroupSummary()) { for (NotificationEntry otherEntry : mNotificationSet.values()) { if (shouldAutoDismiss(otherEntry, entry.getSbn().getGroupKey())) { if (shouldAutoDismissChildren(otherEntry, entry.getSbn().getGroupKey())) { otherEntry.setDismissState(PARENT_DISMISSED); if (isCanceled(otherEntry)) { canceledEntries.add(otherEntry); Loading @@ -249,13 +314,13 @@ public class NotifCollection implements Dumpable { } } } } // Immediately remove any dismissed notifs that have already been canceled by system server // (probably due to being lifetime-extended up until this point). for (NotificationEntry canceledEntry : canceledEntries) { tryRemoveNotification(canceledEntry); } rebuildList(); } private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { Loading Loading @@ -552,7 +617,7 @@ public class NotifCollection implements Dumpable { * * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code. */ private static boolean shouldAutoDismiss( private static boolean shouldAutoDismissChildren( NotificationEntry entry, String dismissedGroupKey) { return entry.getSbn().getGroupKey().equals(dismissedGroupKey) Loading @@ -562,10 +627,39 @@ public class NotifCollection implements Dumpable { && entry.getDismissState() != DISMISSED; } /** * When the user 'clears all notifications' through SystemUI, NotificationManager will not * dismiss unclearable notifications. * @return true if we think NotificationManager will dismiss the entry when asked to * cancel this notification with {@link NotificationListenerService#REASON_CANCEL_ALL} * * See NotificationManager.cancelAllLocked for corresponding code. */ private static boolean shouldDismissOnClearAll( NotificationEntry entry, @UserIdInt int userId) { // TODO: (b/149396544) add FLAG_BUBBLE check here + in NoManService return userIdMatches(entry, userId) && entry.isClearable() && entry.getDismissState() != DISMISSED; } private static boolean hasFlag(NotificationEntry entry, int flag) { return (entry.getSbn().getNotification().flags & flag) != 0; } /** * Determine whether the userId applies to the notification in question, either because * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). * * See NotificationManager#notificationMatchesUserId */ private static boolean userIdMatches(NotificationEntry entry, int userId) { return userId == UserHandle.USER_ALL || entry.getSbn().getUser().getIdentifier() == UserHandle.USER_ALL || entry.getSbn().getUser().getIdentifier() == userId; } private void dispatchOnEntryInit(NotificationEntry entry) { mAmDispatchingToOtherCode = true; for (NotifCollectionListener listener : mNotifCollectionListeners) { Loading Loading @@ -613,6 +707,7 @@ public class NotifCollection implements Dumpable { } mAmDispatchingToOtherCode = false; } @Override public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) { final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs()); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java +5 −2 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ public class NotifInflaterImpl implements NotifInflater { private final IStatusBarService mStatusBarService; private final NotifCollection mNotifCollection; private final NotifInflationErrorManager mNotifErrorManager; private final NotifPipeline mNotifPipeline; private NotificationRowBinderImpl mNotificationRowBinder; private InflationCallback mExternalInflationCallback; Loading @@ -52,10 +53,12 @@ public class NotifInflaterImpl implements NotifInflater { public NotifInflaterImpl( IStatusBarService statusBarService, NotifCollection notifCollection, NotifInflationErrorManager errorManager) { NotifInflationErrorManager errorManager, NotifPipeline notifPipeline) { mStatusBarService = statusBarService; mNotifCollection = notifCollection; mNotifErrorManager = errorManager; mNotifPipeline = notifPipeline; } /** Loading Loading @@ -110,7 +113,7 @@ public class NotifInflaterImpl implements NotifInflater { DISMISS_SENTIMENT_NEUTRAL, NotificationVisibility.obtain(entry.getKey(), entry.getRanking().getRank(), mNotifCollection.getActiveNotifs().size(), mNotifPipeline.getShadeListCount(), true, NotificationLogger.getNotificationLocation(entry)) )); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +23 −0 Original line number Diff line number Diff line Loading @@ -195,4 +195,27 @@ public class NotifPipeline implements CommonNotifCollection { public List<ListEntry> getShadeList() { return mShadeListBuilder.getShadeList(); } /** * Returns the number of notifications currently shown in the shade. This includes all * children and summary notifications. If this method is called during pipeline execution it * will return the number of notifications in its current state, which will likely be only * partially-generated. */ public int getShadeListCount() { final List<ListEntry> entries = getShadeList(); int numNotifs = 0; for (int i = 0; i < entries.size(); i++) { final ListEntry entry = entries.get(i); if (entry instanceof GroupEntry) { final GroupEntry parentEntry = (GroupEntry) entry; numNotifs++; // include the summary in the count numNotifs += parentEntry.getChildren().size(); } else { numNotifs++; } } return numNotifs; } } packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java +3 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static android.service.notification.NotificationStats.DISMISSAL_OTHER; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_UNKNOWN; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.bubbles.BubbleController; Loading Loading @@ -153,10 +153,10 @@ public class BubbleCoordinator implements Coordinator { private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) { return new DismissedByUserStats( DISMISSAL_OTHER, DISMISS_SENTIMENT_UNKNOWN, DISMISS_SENTIMENT_NEUTRAL, NotificationVisibility.obtain(entry.getKey(), entry.getRanking().getRank(), mNotifPipeline.getActiveNotifs().size(), mNotifPipeline.getShadeListCount(), true, // was visible as a bubble NotificationLogger.getNotificationLocation(entry)) ); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt +8 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,14 @@ class NotifCollectionLogger @Inject constructor( }) } fun logNotifClearAllDismissalIntercepted(key: String) { buffer.log(TAG, INFO, { str1 = key }, { "CLEAR ALL DISMISSAL INTERCEPTED $str1" }) } fun logRankingMissing(key: String, rankingMap: RankingMap) { buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" }) buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" }) Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +138 −43 Original line number Diff line number Diff line Loading @@ -44,12 +44,16 @@ import static java.util.Objects.requireNonNull; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Notification; import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.Pair; import androidx.annotation.NonNull; Loading Loading @@ -191,38 +195,36 @@ public class NotifCollection implements Dumpable { } /** * Dismiss a notification on behalf of the user. * Dismisses multiple notifications on behalf of the user. */ public void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) { public void dismissNotifications( List<Pair<NotificationEntry, DismissedByUserStats>> entriesToDismiss) { Assert.isMainThread(); requireNonNull(stats); checkForReentrantCall(); final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>(); for (int i = 0; i < entriesToDismiss.size(); i++) { NotificationEntry entry = entriesToDismiss.get(i).first; DismissedByUserStats stats = entriesToDismiss.get(i).second; requireNonNull(stats); if (entry != mNotificationSet.get(entry.getKey())) { throw new IllegalStateException("Invalid entry: " + entry.getKey()); } if (entry.getDismissState() == DISMISSED) { return; continue; } updateDismissInterceptors(entry); if (isDismissIntercepted(entry)) { mLogger.logNotifDismissedIntercepted(entry.getKey()); return; continue; } // Optimistically mark the notification as dismissed -- we'll wait for the signal from // system server before removing it from our notification set. entry.setDismissState(DISMISSED); mLogger.logNotifDismissed(entry.getKey()); List<NotificationEntry> canceledEntries = new ArrayList<>(); if (isCanceled(entry)) { canceledEntries.add(entry); } else { // Ask system server to remove it for us entriesToLocallyDismiss.add(entry); if (!isCanceled(entry)) { // send message to system server if this notification hasn't already been cancelled try { mStatusBarService.onNotificationClear( entry.getSbn().getPackageName(), Loading @@ -236,11 +238,74 @@ public class NotifCollection implements Dumpable { } catch (RemoteException e) { // system process is dead if we're here. } } } locallyDismissNotifications(entriesToLocallyDismiss); rebuildList(); } /** * Dismisses a single notification on behalf of the user. */ public void dismissNotification( NotificationEntry entry, @NonNull DismissedByUserStats stats) { dismissNotifications(List.of( new Pair<NotificationEntry, DismissedByUserStats>(entry, stats))); } /** * Dismisses all clearable notifications for a given userid on behalf of the user. */ public void dismissAllNotifications(@UserIdInt int userId) { Assert.isMainThread(); checkForReentrantCall(); try { mStatusBarService.onClearAllNotifications(userId); } catch (RemoteException e) { // system process is dead if we're here. } // Also mark any children as dismissed as system server will auto-dismiss them as well final List<NotificationEntry> entries = new ArrayList(getActiveNotifs()); for (int i = entries.size() - 1; i >= 0; i--) { NotificationEntry entry = entries.get(i); if (!shouldDismissOnClearAll(entry, userId)) { // system server won't be removing these notifications, but we still give dismiss // interceptors the chance to filter the notification updateDismissInterceptors(entry); if (isDismissIntercepted(entry)) { mLogger.logNotifClearAllDismissalIntercepted(entry.getKey()); } entries.remove(i); } } locallyDismissNotifications(entries); rebuildList(); } /** * Optimistically marks the given notifications as dismissed -- we'll wait for the signal * from system server before removing it from our notification set. */ private void locallyDismissNotifications(List<NotificationEntry> entries) { final List<NotificationEntry> canceledEntries = new ArrayList<>(); for (int i = 0; i < entries.size(); i++) { NotificationEntry entry = entries.get(i); entry.setDismissState(DISMISSED); mLogger.logNotifDismissed(entry.getKey()); if (isCanceled(entry)) { canceledEntries.add(entry); } else { // Mark any children as dismissed as system server will auto-dismiss them as well if (entry.getSbn().getNotification().isGroupSummary()) { for (NotificationEntry otherEntry : mNotificationSet.values()) { if (shouldAutoDismiss(otherEntry, entry.getSbn().getGroupKey())) { if (shouldAutoDismissChildren(otherEntry, entry.getSbn().getGroupKey())) { otherEntry.setDismissState(PARENT_DISMISSED); if (isCanceled(otherEntry)) { canceledEntries.add(otherEntry); Loading @@ -249,13 +314,13 @@ public class NotifCollection implements Dumpable { } } } } // Immediately remove any dismissed notifs that have already been canceled by system server // (probably due to being lifetime-extended up until this point). for (NotificationEntry canceledEntry : canceledEntries) { tryRemoveNotification(canceledEntry); } rebuildList(); } private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { Loading Loading @@ -552,7 +617,7 @@ public class NotifCollection implements Dumpable { * * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code. */ private static boolean shouldAutoDismiss( private static boolean shouldAutoDismissChildren( NotificationEntry entry, String dismissedGroupKey) { return entry.getSbn().getGroupKey().equals(dismissedGroupKey) Loading @@ -562,10 +627,39 @@ public class NotifCollection implements Dumpable { && entry.getDismissState() != DISMISSED; } /** * When the user 'clears all notifications' through SystemUI, NotificationManager will not * dismiss unclearable notifications. * @return true if we think NotificationManager will dismiss the entry when asked to * cancel this notification with {@link NotificationListenerService#REASON_CANCEL_ALL} * * See NotificationManager.cancelAllLocked for corresponding code. */ private static boolean shouldDismissOnClearAll( NotificationEntry entry, @UserIdInt int userId) { // TODO: (b/149396544) add FLAG_BUBBLE check here + in NoManService return userIdMatches(entry, userId) && entry.isClearable() && entry.getDismissState() != DISMISSED; } private static boolean hasFlag(NotificationEntry entry, int flag) { return (entry.getSbn().getNotification().flags & flag) != 0; } /** * Determine whether the userId applies to the notification in question, either because * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). * * See NotificationManager#notificationMatchesUserId */ private static boolean userIdMatches(NotificationEntry entry, int userId) { return userId == UserHandle.USER_ALL || entry.getSbn().getUser().getIdentifier() == UserHandle.USER_ALL || entry.getSbn().getUser().getIdentifier() == userId; } private void dispatchOnEntryInit(NotificationEntry entry) { mAmDispatchingToOtherCode = true; for (NotifCollectionListener listener : mNotifCollectionListeners) { Loading Loading @@ -613,6 +707,7 @@ public class NotifCollection implements Dumpable { } mAmDispatchingToOtherCode = false; } @Override public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) { final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs()); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java +5 −2 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ public class NotifInflaterImpl implements NotifInflater { private final IStatusBarService mStatusBarService; private final NotifCollection mNotifCollection; private final NotifInflationErrorManager mNotifErrorManager; private final NotifPipeline mNotifPipeline; private NotificationRowBinderImpl mNotificationRowBinder; private InflationCallback mExternalInflationCallback; Loading @@ -52,10 +53,12 @@ public class NotifInflaterImpl implements NotifInflater { public NotifInflaterImpl( IStatusBarService statusBarService, NotifCollection notifCollection, NotifInflationErrorManager errorManager) { NotifInflationErrorManager errorManager, NotifPipeline notifPipeline) { mStatusBarService = statusBarService; mNotifCollection = notifCollection; mNotifErrorManager = errorManager; mNotifPipeline = notifPipeline; } /** Loading Loading @@ -110,7 +113,7 @@ public class NotifInflaterImpl implements NotifInflater { DISMISS_SENTIMENT_NEUTRAL, NotificationVisibility.obtain(entry.getKey(), entry.getRanking().getRank(), mNotifCollection.getActiveNotifs().size(), mNotifPipeline.getShadeListCount(), true, NotificationLogger.getNotificationLocation(entry)) )); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +23 −0 Original line number Diff line number Diff line Loading @@ -195,4 +195,27 @@ public class NotifPipeline implements CommonNotifCollection { public List<ListEntry> getShadeList() { return mShadeListBuilder.getShadeList(); } /** * Returns the number of notifications currently shown in the shade. This includes all * children and summary notifications. If this method is called during pipeline execution it * will return the number of notifications in its current state, which will likely be only * partially-generated. */ public int getShadeListCount() { final List<ListEntry> entries = getShadeList(); int numNotifs = 0; for (int i = 0; i < entries.size(); i++) { final ListEntry entry = entries.get(i); if (entry instanceof GroupEntry) { final GroupEntry parentEntry = (GroupEntry) entry; numNotifs++; // include the summary in the count numNotifs += parentEntry.getChildren().size(); } else { numNotifs++; } } return numNotifs; } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java +3 −3 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static android.service.notification.NotificationStats.DISMISSAL_OTHER; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_UNKNOWN; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.bubbles.BubbleController; Loading Loading @@ -153,10 +153,10 @@ public class BubbleCoordinator implements Coordinator { private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) { return new DismissedByUserStats( DISMISSAL_OTHER, DISMISS_SENTIMENT_UNKNOWN, DISMISS_SENTIMENT_NEUTRAL, NotificationVisibility.obtain(entry.getKey(), entry.getRanking().getRank(), mNotifPipeline.getActiveNotifs().size(), mNotifPipeline.getShadeListCount(), true, // was visible as a bubble NotificationLogger.getNotificationLocation(entry)) ); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt +8 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,14 @@ class NotifCollectionLogger @Inject constructor( }) } fun logNotifClearAllDismissalIntercepted(key: String) { buffer.log(TAG, INFO, { str1 = key }, { "CLEAR ALL DISMISSAL INTERCEPTED $str1" }) } fun logRankingMissing(key: String, rankingMap: RankingMap) { buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" }) buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" }) Loading