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

Commit ef1073f7 authored by Christoph Studer's avatar Christoph Studer Committed by Android Git Automerger
Browse files

am ff02faf8: Merge "NoMan: Optimize grouped notifications" into lmp-mr1-dev

* commit 'ff02faf8':
  NoMan: Optimize grouped notifications
parents f57d9dcb ff02faf8
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -51,8 +51,8 @@ option java_package com.android.server
# ---------------------------
# ---------------------------
# NotificationManagerService.java
# NotificationManagerService.java
# ---------------------------
# ---------------------------
# when a NotificationManager.notify is called
# when a NotificationManager.notify is called. status: 0=post, 1=update, 2=ignored
2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3),(update|1)
2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3),(status|1)
# when someone tries to cancel a notification, the notification manager sometimes
# when someone tries to cancel a notification, the notification manager sometimes
# calls this with flags too
# calls this with flags too
2751 notification_cancel (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(required_flags|1),(forbidden_flags|1),(reason|1|5),(listener|3)
2751 notification_cancel (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(required_flags|1),(forbidden_flags|1),(reason|1|5),(listener|3)
+168 −20
Original line number Original line Diff line number Diff line
@@ -116,6 +116,7 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Objects;


@@ -139,6 +140,7 @@ public class NotificationManagerService extends SystemService {
    static final int SHORT_DELAY = 2000; // 2 seconds
    static final int SHORT_DELAY = 2000; // 2 seconds


    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};

    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps


    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
@@ -166,6 +168,15 @@ public class NotificationManagerService extends SystemService {
    static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
    static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
            ValidateNotificationPeople.STARRED_CONTACT;
            ValidateNotificationPeople.STARRED_CONTACT;


    /** notification_enqueue status value for a newly enqueued notification. */
    private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;

    /** notification_enqueue status value for an existing notification. */
    private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;

    /** notification_enqueue status value for an ignored notification. */
    private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;

    private IActivityManager mAm;
    private IActivityManager mAm;
    AudioManager mAudioManager;
    AudioManager mAudioManager;
    StatusBarManagerInternal mStatusBar;
    StatusBarManagerInternal mStatusBar;
@@ -209,6 +220,7 @@ public class NotificationManagerService extends SystemService {
    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
            new ArrayMap<String, NotificationRecord>();
            new ArrayMap<String, NotificationRecord>();
    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
    final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();


    ArrayList<String> mLights = new ArrayList<String>();
    ArrayList<String> mLights = new ArrayList<String>();
    NotificationRecord mLedNotification;
    NotificationRecord mLedNotification;
@@ -251,6 +263,7 @@ public class NotificationManagerService extends SystemService {
    private static final int REASON_LISTENER_CANCEL = 10;
    private static final int REASON_LISTENER_CANCEL = 10;
    private static final int REASON_LISTENER_CANCEL_ALL = 11;
    private static final int REASON_LISTENER_CANCEL_ALL = 11;
    private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
    private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
    private static final int REASON_GROUP_OPTIMIZATION = 13;


    private static class Archive {
    private static class Archive {
        final int mBufferSize;
        final int mBufferSize;
@@ -1658,6 +1671,16 @@ public class NotificationManagerService extends SystemService {


            pw.println("\n  Condition providers:");
            pw.println("\n  Condition providers:");
            mConditionProviders.dump(pw, filter);
            mConditionProviders.dump(pw, filter);

            pw.println("\n  Group summaries:");
            for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
                NotificationRecord r = entry.getValue();
                pw.println("    " + entry.getKey() + " -> " + r.getKey());
                if (mNotificationsByKey.get(r.getKey()) != r) {
                    pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
                    r.dump(pw, "      ", getContext());
                }
            }
        }
        }
    }
    }


@@ -1779,16 +1802,34 @@ public class NotificationManagerService extends SystemService {
                        // Retain ranking information from previous record
                        // Retain ranking information from previous record
                        r.copyRankingInformation(old);
                        r.copyRankingInformation(old);
                    }
                    }
                    mRankingHelper.extractSignals(r);

                    // Handle grouped notifications and bail out early if we
                    // can to avoid extracting signals.
                    handleGroupedNotificationLocked(r, old, callingUid, callingPid);
                    boolean ignoreNotification =
                            removeUnusedGroupedNotificationLocked(r, callingUid, callingPid);


                    // This conditional is a dirty hack to limit the logging done on
                    // This conditional is a dirty hack to limit the logging done on
                    //     behalf of the download manager without affecting other apps.
                    //     behalf of the download manager without affecting other apps.
                    if (!pkg.equals("com.android.providers.downloads")
                    if (!pkg.equals("com.android.providers.downloads")
                            || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
                            || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
                        int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
                        if (ignoreNotification) {
                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
                        } else if (old != null) {
                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
                        }
                        EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
                        EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
                                pkg, id, tag, userId, notification.toString(),
                                pkg, id, tag, userId, notification.toString(),
                                (old != null) ? 1 : 0);
                                enqueueStatus);
                    }

                    if (ignoreNotification) {
                        return;
                    }
                    }

                    mRankingHelper.extractSignals(r);

                    // 3. Apply local rules
                    // 3. Apply local rules


                    // blocked apps
                    // blocked apps
@@ -1805,16 +1846,6 @@ public class NotificationManagerService extends SystemService {
                        return;
                        return;
                    }
                    }


                    // Clear out group children of the old notification if the update causes the
                    // group summary to go away. This happens when the old notification was a
                    // summary and the new one isn't, or when the old notification was a summary
                    // and its group key changed.
                    if (old != null && old.getNotification().isGroupSummary() &&
                            (!notification.isGroupSummary() ||
                                    !old.getGroupKey().equals(r.getGroupKey()))) {
                        cancelGroupChildrenLocked(old, callingUid, callingPid, null);
                    }

                    int index = indexOfNotificationLocked(n.getKey());
                    int index = indexOfNotificationLocked(n.getKey());
                    if (index < 0) {
                    if (index < 0) {
                        mNotificationList.add(r);
                        mNotificationList.add(r);
@@ -1864,6 +1895,90 @@ public class NotificationManagerService extends SystemService {
        idOut[0] = id;
        idOut[0] = id;
    }
    }


    /**
     * Ensures that grouped notification receive their special treatment.
     *
     * <p>Cancels group children if the new notification causes a group to lose
     * its summary.</p>
     *
     * <p>Updates mSummaryByGroupKey.</p>
     */
    private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
            int callingUid, int callingPid) {
        StatusBarNotification sbn = r.sbn;
        Notification n = sbn.getNotification();
        String group = sbn.getGroupKey();
        boolean isSummary = n.isGroupSummary();

        Notification oldN = old != null ? old.sbn.getNotification() : null;
        String oldGroup = old != null ? old.sbn.getGroupKey() : null;
        boolean oldIsSummary = old != null && oldN.isGroupSummary();

        if (oldIsSummary) {
            NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
            if (removedSummary != old) {
                String removedKey =
                        removedSummary != null ? removedSummary.getKey() : "<null>";
                Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
                        ", removed=" + removedKey);
            }
        }
        if (isSummary) {
            mSummaryByGroupKey.put(group, r);
        }

        // Clear out group children of the old notification if the update
        // causes the group summary to go away. This happens when the old
        // notification was a summary and the new one isn't, or when the old
        // notification was a summary and its group key changed.
        if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
            cancelGroupChildrenLocked(old, callingUid, callingPid, null,
                    REASON_GROUP_SUMMARY_CANCELED);
        }
    }

    /**
     * Performs group notification optimizations if SysUI is the only active
     * notification listener and returns whether the given notification should
     * be ignored.
     *
     * <p>Returns true if the given notification is a child of a group with a
     * summary, which means that SysUI will never show it, and hence the new
     * notification can be safely ignored.</p>
     *
     * <p>For summaries, cancels all children of that group, as SysUI will
     * never show them anymore.</p>
     *
     * @return true if the given notification can be ignored as an optimization
     */
    private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
            int callingUid, int callingPid) {
        // No optimizations are possible if listeners want groups.
        if (mListeners.notificationGroupsDesired()) {
            return false;
        }

        StatusBarNotification sbn = r.sbn;
        String group = sbn.getGroupKey();
        boolean isSummary = sbn.getNotification().isGroupSummary();
        boolean isChild = sbn.getNotification().isGroupChild();

        NotificationRecord summary = mSummaryByGroupKey.get(group);
        if (isChild && summary != null) {
            // Child with an active summary -> ignore
            if (DBG) {
                Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
                        + summary.getKey());
            }
            return true;
        } else if (isSummary) {
            // Summary -> cancel children
            cancelGroupChildrenLocked(r, callingUid, callingPid, null,
                    REASON_GROUP_OPTIMIZATION);
        }
        return false;
    }

    private void buzzBeepBlinkLocked(NotificationRecord record) {
    private void buzzBeepBlinkLocked(NotificationRecord record) {
        boolean buzzBeepBlinked = false;
        boolean buzzBeepBlinked = false;
        final Notification notification = record.sbn.getNotification();
        final Notification notification = record.sbn.getNotification();
@@ -2386,6 +2501,11 @@ public class NotificationManagerService extends SystemService {
        }
        }


        mNotificationsByKey.remove(r.sbn.getKey());
        mNotificationsByKey.remove(r.sbn.getKey());
        String groupKey = r.getGroupKey();
        NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
        if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
            mSummaryByGroupKey.remove(groupKey);
        }


        // Save it for users of getHistoricalNotifications()
        // Save it for users of getHistoricalNotifications()
        mArchive.record(r.sbn);
        mArchive.record(r.sbn);
@@ -2433,7 +2553,8 @@ public class NotificationManagerService extends SystemService {
                        mNotificationList.remove(index);
                        mNotificationList.remove(index);


                        cancelNotificationLocked(r, sendDelete, reason);
                        cancelNotificationLocked(r, sendDelete, reason);
                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
                                REASON_GROUP_SUMMARY_CANCELED);
                        updateLightsLocked();
                        updateLightsLocked();
                    }
                    }
                }
                }
@@ -2512,7 +2633,7 @@ public class NotificationManagerService extends SystemService {
                final int M = canceledNotifications.size();
                final int M = canceledNotifications.size();
                for (int i = 0; i < M; i++) {
                for (int i = 0; i < M; i++) {
                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
                            listenerName);
                            listenerName, REASON_GROUP_SUMMARY_CANCELED);
                }
                }
            }
            }
            if (canceledNotifications != null) {
            if (canceledNotifications != null) {
@@ -2556,14 +2677,14 @@ public class NotificationManagerService extends SystemService {
        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
        for (int i = 0; i < M; i++) {
        for (int i = 0; i < M; i++) {
            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
                    listenerName);
                    listenerName, REASON_GROUP_SUMMARY_CANCELED);
        }
        }
        updateLightsLocked();
        updateLightsLocked();
    }
    }


    // Warning: The caller is responsible for invoking updateLightsLocked().
    // Warning: The caller is responsible for invoking updateLightsLocked().
    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
            String listenerName) {
            String listenerName, int reason) {
        Notification n = r.getNotification();
        Notification n = r.getNotification();
        if (!n.isGroupSummary()) {
        if (!n.isGroupSummary()) {
            return;
            return;
@@ -2583,11 +2704,10 @@ public class NotificationManagerService extends SystemService {
            StatusBarNotification childSbn = childR.sbn;
            StatusBarNotification childSbn = childR.sbn;
            if (childR.getNotification().isGroupChild() &&
            if (childR.getNotification().isGroupChild() &&
                    childR.getGroupKey().equals(r.getGroupKey())) {
                    childR.getGroupKey().equals(r.getGroupKey())) {
                EventLogTags.writeNotificationCancel(callingUid, callingPid,
                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
                        childSbn.getTag(), userId, 0, 0, reason, listenerName);
                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
                mNotificationList.remove(i);
                mNotificationList.remove(i);
                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
                cancelNotificationLocked(childR, false, reason);
            }
            }
        }
        }
    }
    }
@@ -2783,6 +2903,7 @@ public class NotificationManagerService extends SystemService {
    public class NotificationListeners extends ManagedServices {
    public class NotificationListeners extends ManagedServices {


        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
        private boolean mNotificationGroupsDesired;


        public NotificationListeners() {
        public NotificationListeners() {
            super(getContext(), mHandler, mNotificationList, mUserProfiles);
            super(getContext(), mHandler, mNotificationList, mUserProfiles);
@@ -2810,6 +2931,7 @@ public class NotificationManagerService extends SystemService {
            final INotificationListener listener = (INotificationListener) info.service;
            final INotificationListener listener = (INotificationListener) info.service;
            final NotificationRankingUpdate update;
            final NotificationRankingUpdate update;
            synchronized (mNotificationList) {
            synchronized (mNotificationList) {
                updateNotificationGroupsDesiredLocked();
                update = makeRankingUpdateLocked(info);
                update = makeRankingUpdateLocked(info);
            }
            }
            try {
            try {
@@ -2825,6 +2947,7 @@ public class NotificationManagerService extends SystemService {
                updateListenerHintsLocked();
                updateListenerHintsLocked();
            }
            }
            mLightTrimListeners.remove(removed);
            mLightTrimListeners.remove(removed);
            updateNotificationGroupsDesiredLocked();
        }
        }


        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
@@ -3028,6 +3151,31 @@ public class NotificationManagerService extends SystemService {
            }
            }
            return false;
            return false;
        }
        }

        /**
         * Returns whether any of the currently registered listeners wants to receive notification
         * groups.
         *
         * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
         */
        public boolean notificationGroupsDesired() {
            return mNotificationGroupsDesired;
        }

        private void updateNotificationGroupsDesiredLocked() {
            mNotificationGroupsDesired = true;
            // No listeners, no groups.
            if (mServices.isEmpty()) {
                mNotificationGroupsDesired = false;
                return;
            }
            // One listener: Check whether it's SysUI.
            if (mServices.size() == 1 &&
                    mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
                mNotificationGroupsDesired = false;
                return;
            }
        }
    }
    }


    public static final class DumpFilter {
    public static final class DumpFilter {