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 Diff line number Diff line
@@ -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;

@@ -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(),
@@ -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);
@@ -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) {
@@ -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)
@@ -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) {
@@ -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());
+5 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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;
    }

    /**
@@ -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))
                        ));
+23 −0
Original line number Diff line number Diff line
@@ -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;
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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))
        );
+8 −0
Original line number 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) {
        buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
        buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
Loading