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

Commit aead434f authored by Beverly Tai's avatar Beverly Tai Committed by Android (Google) Code Review
Browse files

Merge "Add NotifCollection dismiss methods"

parents cc104cc3 7e0d649a
Loading
Loading
Loading
Loading
+138 −43
Original line number Original line Diff line number Diff line
@@ -44,12 +44,16 @@ import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Notification;
import android.app.Notification;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.Pair;


import androidx.annotation.NonNull;
import androidx.annotation.NonNull;


@@ -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();
        Assert.isMainThread();
        requireNonNull(stats);
        checkForReentrantCall();
        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())) {
            if (entry != mNotificationSet.get(entry.getKey())) {
                throw new IllegalStateException("Invalid entry: " + entry.getKey());
                throw new IllegalStateException("Invalid entry: " + entry.getKey());
            }
            }


            if (entry.getDismissState() == DISMISSED) {
            if (entry.getDismissState() == DISMISSED) {
            return;
                continue;
            }
            }


            updateDismissInterceptors(entry);
            updateDismissInterceptors(entry);
            if (isDismissIntercepted(entry)) {
            if (isDismissIntercepted(entry)) {
                mLogger.logNotifDismissedIntercepted(entry.getKey());
                mLogger.logNotifDismissedIntercepted(entry.getKey());
            return;
                continue;
            }
            }


        // Optimistically mark the notification as dismissed -- we'll wait for the signal from
            entriesToLocallyDismiss.add(entry);
        // system server before removing it from our notification set.
            if (!isCanceled(entry)) {
        entry.setDismissState(DISMISSED);
                // send message to system server if this notification hasn't already been cancelled
        mLogger.logNotifDismissed(entry.getKey());

        List<NotificationEntry> canceledEntries = new ArrayList<>();

        if (isCanceled(entry)) {
            canceledEntries.add(entry);
        } else {
            // Ask system server to remove it for us
                try {
                try {
                    mStatusBarService.onNotificationClear(
                    mStatusBarService.onNotificationClear(
                            entry.getSbn().getPackageName(),
                            entry.getSbn().getPackageName(),
@@ -236,11 +238,74 @@ public class NotifCollection implements Dumpable {
                } catch (RemoteException e) {
                } catch (RemoteException e) {
                    // system process is dead if we're here.
                    // 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()) {
                if (entry.getSbn().getNotification().isGroupSummary()) {
                    for (NotificationEntry otherEntry : mNotificationSet.values()) {
                    for (NotificationEntry otherEntry : mNotificationSet.values()) {
                    if (shouldAutoDismiss(otherEntry, entry.getSbn().getGroupKey())) {
                        if (shouldAutoDismissChildren(otherEntry, entry.getSbn().getGroupKey())) {
                            otherEntry.setDismissState(PARENT_DISMISSED);
                            otherEntry.setDismissState(PARENT_DISMISSED);
                            if (isCanceled(otherEntry)) {
                            if (isCanceled(otherEntry)) {
                                canceledEntries.add(otherEntry);
                                canceledEntries.add(otherEntry);
@@ -249,13 +314,13 @@ public class NotifCollection implements Dumpable {
                    }
                    }
                }
                }
            }
            }
        }


        // Immediately remove any dismissed notifs that have already been canceled by system server
        // Immediately remove any dismissed notifs that have already been canceled by system server
        // (probably due to being lifetime-extended up until this point).
        // (probably due to being lifetime-extended up until this point).
        for (NotificationEntry canceledEntry : canceledEntries) {
        for (NotificationEntry canceledEntry : canceledEntries) {
            tryRemoveNotification(canceledEntry);
            tryRemoveNotification(canceledEntry);
        }
        }
        rebuildList();
    }
    }


    private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
    private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
@@ -552,7 +617,7 @@ public class NotifCollection implements Dumpable {
     *
     *
     * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code.
     * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code.
     */
     */
    private static boolean shouldAutoDismiss(
    private static boolean shouldAutoDismissChildren(
            NotificationEntry entry,
            NotificationEntry entry,
            String dismissedGroupKey) {
            String dismissedGroupKey) {
        return entry.getSbn().getGroupKey().equals(dismissedGroupKey)
        return entry.getSbn().getGroupKey().equals(dismissedGroupKey)
@@ -562,10 +627,39 @@ public class NotifCollection implements Dumpable {
                && entry.getDismissState() != DISMISSED;
                && 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) {
    private static boolean hasFlag(NotificationEntry entry, int flag) {
        return (entry.getSbn().getNotification().flags & flag) != 0;
        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) {
    private void dispatchOnEntryInit(NotificationEntry entry) {
        mAmDispatchingToOtherCode = true;
        mAmDispatchingToOtherCode = true;
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
@@ -613,6 +707,7 @@ public class NotifCollection implements Dumpable {
        }
        }
        mAmDispatchingToOtherCode = false;
        mAmDispatchingToOtherCode = false;
    }
    }

    @Override
    @Override
    public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
    public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
        final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
        final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
+5 −2
Original line number Original line Diff line number Diff line
@@ -44,6 +44,7 @@ public class NotifInflaterImpl implements NotifInflater {
    private final IStatusBarService mStatusBarService;
    private final IStatusBarService mStatusBarService;
    private final NotifCollection mNotifCollection;
    private final NotifCollection mNotifCollection;
    private final NotifInflationErrorManager mNotifErrorManager;
    private final NotifInflationErrorManager mNotifErrorManager;
    private final NotifPipeline mNotifPipeline;


    private NotificationRowBinderImpl mNotificationRowBinder;
    private NotificationRowBinderImpl mNotificationRowBinder;
    private InflationCallback mExternalInflationCallback;
    private InflationCallback mExternalInflationCallback;
@@ -52,10 +53,12 @@ public class NotifInflaterImpl implements NotifInflater {
    public NotifInflaterImpl(
    public NotifInflaterImpl(
            IStatusBarService statusBarService,
            IStatusBarService statusBarService,
            NotifCollection notifCollection,
            NotifCollection notifCollection,
            NotifInflationErrorManager errorManager) {
            NotifInflationErrorManager errorManager,
            NotifPipeline notifPipeline) {
        mStatusBarService = statusBarService;
        mStatusBarService = statusBarService;
        mNotifCollection = notifCollection;
        mNotifCollection = notifCollection;
        mNotifErrorManager = errorManager;
        mNotifErrorManager = errorManager;
        mNotifPipeline = notifPipeline;
    }
    }


    /**
    /**
@@ -110,7 +113,7 @@ public class NotifInflaterImpl implements NotifInflater {
                                DISMISS_SENTIMENT_NEUTRAL,
                                DISMISS_SENTIMENT_NEUTRAL,
                                NotificationVisibility.obtain(entry.getKey(),
                                NotificationVisibility.obtain(entry.getKey(),
                                        entry.getRanking().getRank(),
                                        entry.getRanking().getRank(),
                                        mNotifCollection.getActiveNotifs().size(),
                                        mNotifPipeline.getShadeListCount(),
                                        true,
                                        true,
                                        NotificationLogger.getNotificationLocation(entry))
                                        NotificationLogger.getNotificationLocation(entry))
                        ));
                        ));
+23 −0
Original line number Original line Diff line number Diff line
@@ -195,4 +195,27 @@ public class NotifPipeline implements CommonNotifCollection {
    public List<ListEntry> getShadeList() {
    public List<ListEntry> getShadeList() {
        return mShadeListBuilder.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;
    }
}
}
+3 −3
Original line number Original line Diff line number Diff line
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
package com.android.systemui.statusbar.notification.collection.coordinator;


import static android.service.notification.NotificationStats.DISMISSAL_OTHER;
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.internal.statusbar.NotificationVisibility;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubbleController;
@@ -153,10 +153,10 @@ public class BubbleCoordinator implements Coordinator {
    private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) {
    private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) {
        return new DismissedByUserStats(
        return new DismissedByUserStats(
                DISMISSAL_OTHER,
                DISMISSAL_OTHER,
                DISMISS_SENTIMENT_UNKNOWN,
                DISMISS_SENTIMENT_NEUTRAL,
                NotificationVisibility.obtain(entry.getKey(),
                NotificationVisibility.obtain(entry.getKey(),
                        entry.getRanking().getRank(),
                        entry.getRanking().getRank(),
                        mNotifPipeline.getActiveNotifs().size(),
                        mNotifPipeline.getShadeListCount(),
                        true, // was visible as a bubble
                        true, // was visible as a bubble
                        NotificationLogger.getNotificationLocation(entry))
                        NotificationLogger.getNotificationLocation(entry))
        );
        );
+8 −0
Original line number Original line Diff line number Diff line
@@ -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) {
    fun logRankingMissing(key: String, rankingMap: RankingMap) {
        buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
        buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
        buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
        buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
Loading