blob: d37e16b176200321650a8f1ecd1d2397149eac8f [file] [log] [blame]
Eliot Courtneya6d8cf22017-10-20 13:26:58 +09001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
Rohan Shah20790b82018-07-02 17:21:04 -070016package com.android.systemui.statusbar.notification;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090017
Mady Mellorc2ff0112019-03-28 14:18:06 -070018import static android.service.notification.NotificationListenerService.REASON_CANCEL;
19import static android.service.notification.NotificationListenerService.REASON_ERROR;
20
Kevin Hanf400aee2020-01-30 13:10:29 -080021import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
Kevin Hanec093092019-12-11 16:47:59 -080022import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
23
Evan Laird181de622019-10-24 09:53:02 -040024import android.annotation.NonNull;
Tony Mak628cb932018-06-19 18:30:41 +010025import android.annotation.Nullable;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090026import android.app.Notification;
Evan Lairdccf5c5e2020-02-06 14:01:28 -050027import android.os.SystemClock;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090028import android.service.notification.NotificationListenerService;
Ned Burnse372f322019-09-24 13:14:08 -040029import android.service.notification.NotificationListenerService.Ranking;
Evan Laird181de622019-10-24 09:53:02 -040030import android.service.notification.NotificationListenerService.RankingMap;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090031import android.service.notification.StatusBarNotification;
Tony Mak628cb932018-06-19 18:30:41 +010032import android.util.ArrayMap;
Ned Burns88aeaf72020-03-13 20:45:19 -040033import android.util.ArraySet;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090034import android.util.Log;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090035
Julia Reynolds91590062018-04-02 16:24:11 -040036import com.android.internal.annotations.VisibleForTesting;
Dieter Hsud39f0d52018-04-14 02:08:30 +080037import com.android.internal.statusbar.NotificationVisibility;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090038import com.android.systemui.Dumpable;
Beverly8b493df2019-12-18 14:16:50 -050039import com.android.systemui.statusbar.FeatureFlags;
Kevin38ce6fa2018-10-17 16:00:14 -070040import com.android.systemui.statusbar.NotificationLifetimeExtender;
Ned Burns2246b332019-12-12 14:39:13 -050041import com.android.systemui.statusbar.NotificationListener;
Ned Burnse6855d62019-12-19 16:38:08 -050042import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
Rohan Shah20790b82018-07-02 17:21:04 -070043import com.android.systemui.statusbar.NotificationPresenter;
44import com.android.systemui.statusbar.NotificationRemoteInputManager;
Mady Mellorc2ff0112019-03-28 14:18:06 -070045import com.android.systemui.statusbar.NotificationRemoveInterceptor;
Rohan Shah20790b82018-07-02 17:21:04 -070046import com.android.systemui.statusbar.NotificationUiAdjustment;
Ned Burnsf81c4c42019-01-07 14:10:43 -050047import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Evan Laird181de622019-10-24 09:53:02 -040048import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
Ned Burns012048d2020-01-08 19:57:30 -050049import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
Kevin Hanf400aee2020-01-30 13:10:29 -080050import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
51import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +010052import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
Gustav Senntonf892fe92019-01-22 15:31:42 +000053import com.android.systemui.statusbar.notification.logging.NotificationLogger;
Evan Laird181de622019-10-24 09:53:02 -040054import com.android.systemui.statusbar.phone.NotificationGroupManager;
Evan Laird181de622019-10-24 09:53:02 -040055import com.android.systemui.util.Assert;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090056import com.android.systemui.util.leak.LeakDetector;
57
58import java.io.FileDescriptor;
59import java.io.PrintWriter;
60import java.util.ArrayList;
Ned Burns88aeaf72020-03-13 20:45:19 -040061import java.util.Collection;
Evan Laird181de622019-10-24 09:53:02 -040062import java.util.Collections;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090063import java.util.HashMap;
64import java.util.List;
Ned Burnsa5dad552019-01-29 17:59:10 -050065import java.util.Map;
Beverly6fc9f222019-10-23 17:56:48 -040066import java.util.Set;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090067
Dave Mankoff02dcaf52020-01-08 15:42:06 -050068import dagger.Lazy;
69
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090070/**
Evan Laird181de622019-10-24 09:53:02 -040071 * NotificationEntryManager is responsible for the adding, removing, and updating of
72 * {@link NotificationEntry}s. It also handles tasks such as their inflation and their interaction
73 * with other Notification.*Manager objects.
74 *
75 * We track notification entries through this lifecycle:
76 * 1. Pending
77 * 2. Active
78 * 3. Sorted / filtered (visible)
79 *
80 * Every entry spends some amount of time in the pending state, while it is being inflated. Once
81 * inflated, an entry moves into the active state, where it _could_ potentially be shown to the
82 * user. After an entry makes its way into the active state, we sort and filter the entire set to
83 * repopulate the visible set.
84 *
85 * There are a few different things that other classes may be interested in, and most of them
86 * involve the current set of notifications. Here's a brief overview of things you may want to know:
87 * @see #getVisibleNotifications() for the visible set
88 * @see #getActiveNotificationUnfiltered(String) to check if a key exists
89 * @see #getPendingNotificationsIterator() for an iterator over the pending notifications
90 * @see #getPendingOrActiveNotif(String) to find a notification exists for that key in any list
Evan Laird181de622019-10-24 09:53:02 -040091 * @see #getActiveNotificationsForCurrentUser() to see every notification that the current user owns
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090092 */
Gus Prevas8ba88a82018-12-18 11:13:44 -050093public class NotificationEntryManager implements
Kevin Hanf400aee2020-01-30 13:10:29 -080094 CommonNotifCollection,
Gus Prevas8ba88a82018-12-18 11:13:44 -050095 Dumpable,
Ned Burns01e38212019-01-03 16:32:52 -050096 VisualStabilityManager.Callback {
Julia Reynoldsfc640012018-02-21 12:25:27 -050097 private static final String TAG = "NotificationEntryMgr";
Ned Burnsd35708c2019-01-09 15:38:03 -050098 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Eliot Courtney6c313d32017-12-14 19:57:51 +090099
Mady Mellor1a4e86f2019-05-03 16:07:23 -0700100 /**
101 * Used when a notification is removed and it doesn't have a reason that maps to one of the
102 * reasons defined in NotificationListenerService
Evan Laird181de622019-10-24 09:53:02 -0400103 * (e.g. {@link NotificationListenerService#REASON_CANCEL})
Mady Mellor1a4e86f2019-05-03 16:07:23 -0700104 */
105 public static final int UNDEFINED_DISMISS_REASON = 0;
106
Ned Burns88aeaf72020-03-13 20:45:19 -0400107 private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
108 private final Set<NotificationEntry> mReadOnlyAllNotifications =
109 Collections.unmodifiableSet(mAllNotifications);
110
Evan Laird181de622019-10-24 09:53:02 -0400111 /** Pending notifications are ones awaiting inflation */
Ned Burnsd35708c2019-01-09 15:38:03 -0500112 @VisibleForTesting
Ned Burnsf81c4c42019-01-07 14:10:43 -0500113 protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
Evan Laird181de622019-10-24 09:53:02 -0400114 /**
115 * Active notifications have been inflated / prepared and could become visible, but may get
116 * filtered out if for instance they are not for the current user
117 */
118 private final ArrayMap<String, NotificationEntry> mActiveNotifications = new ArrayMap<>();
119 @VisibleForTesting
120 /** This is the list of "active notifications for this user in this context" */
121 protected final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
122 private final List<NotificationEntry> mReadOnlyNotifications =
123 Collections.unmodifiableList(mSortedAndFiltered);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900124
Ned Burnsa5dad552019-01-29 17:59:10 -0500125 private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
126 new ArrayMap<>();
127
Ned Burnsafe77bc2020-01-30 20:45:07 -0500128 private final NotificationEntryManagerLogger mLogger;
129
Jason Monk297c04e2018-08-23 17:16:59 -0400130 // Lazily retrieved dependencies
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500131 private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
132 private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
133 private final LeakDetector mLeakDetector;
Kevin Hanf400aee2020-01-30 13:10:29 -0800134 private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
Eliot Courtney6c313d32017-12-14 19:57:51 +0900135
Evan Laird181de622019-10-24 09:53:02 -0400136 private final KeyguardEnvironment mKeyguardEnvironment;
137 private final NotificationGroupManager mGroupManager;
138 private final NotificationRankingManager mRankingManager;
Beverly8b493df2019-12-18 14:16:50 -0500139 private final FeatureFlags mFeatureFlags;
Evan Laird04373662020-01-24 17:37:39 -0500140 private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
Evan Laird181de622019-10-24 09:53:02 -0400141
Jason Monk297c04e2018-08-23 17:16:59 -0400142 private NotificationPresenter mPresenter;
Evan Laird181de622019-10-24 09:53:02 -0400143 private RankingMap mLatestRankingMap;
Ned Burnsf36c6252019-01-09 19:12:33 -0500144
Ned Burnsdc10c952018-11-21 14:36:21 -0500145 @VisibleForTesting
146 final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
Kevina5ff1fa2018-08-21 16:35:48 -0700147 = new ArrayList<>();
Gus Prevasd65c2db2018-12-18 17:13:38 -0500148 private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
Evan Laird04373662020-01-24 17:37:39 -0500149 private final List<NotificationRemoveInterceptor> mRemoveInterceptors = new ArrayList<>();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900150
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900151 @Override
152 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
153 pw.println("NotificationEntryManager state:");
154 pw.print(" mPendingNotifications=");
155 if (mPendingNotifications.size() == 0) {
156 pw.println("null");
157 } else {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500158 for (NotificationEntry entry : mPendingNotifications.values()) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400159 pw.println(entry.getSbn());
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900160 }
161 }
Evan Laird04373662020-01-24 17:37:39 -0500162 pw.println(" Remove interceptors registered:");
163 for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
164 pw.println(" " + interceptor.getClass().getSimpleName());
165 }
166 pw.println(" Lifetime extenders registered:");
167 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
168 pw.println(" " + extender.getClass().getSimpleName());
169 }
Ned Burnsa5dad552019-01-29 17:59:10 -0500170 pw.println(" Lifetime-extended notifications:");
171 if (mRetainedNotifications.isEmpty()) {
172 pw.println(" None");
173 } else {
174 for (Map.Entry<NotificationEntry, NotificationLifetimeExtender> entry
175 : mRetainedNotifications.entrySet()) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400176 pw.println(" " + entry.getKey().getSbn() + " retained by "
Ned Burnsa5dad552019-01-29 17:59:10 -0500177 + entry.getValue().getClass().getName());
178 }
179 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900180 }
181
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +0100182 /**
183 * Injected constructor. See {@link NotificationsModule}.
184 */
Steve Elliott58adc212019-10-15 11:07:54 -0400185 public NotificationEntryManager(
Ned Burnsafe77bc2020-01-30 20:45:07 -0500186 NotificationEntryManagerLogger logger,
Evan Laird181de622019-10-24 09:53:02 -0400187 NotificationGroupManager groupManager,
188 NotificationRankingManager rankingManager,
Beverly8b493df2019-12-18 14:16:50 -0500189 KeyguardEnvironment keyguardEnvironment,
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500190 FeatureFlags featureFlags,
191 Lazy<NotificationRowBinder> notificationRowBinderLazy,
192 Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
Evan Laird04373662020-01-24 17:37:39 -0500193 LeakDetector leakDetector,
194 ForegroundServiceDismissalFeatureController fgsFeatureController) {
Ned Burnsafe77bc2020-01-30 20:45:07 -0500195 mLogger = logger;
Evan Laird181de622019-10-24 09:53:02 -0400196 mGroupManager = groupManager;
197 mRankingManager = rankingManager;
198 mKeyguardEnvironment = keyguardEnvironment;
Beverly8b493df2019-12-18 14:16:50 -0500199 mFeatureFlags = featureFlags;
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500200 mNotificationRowBinderLazy = notificationRowBinderLazy;
201 mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
202 mLeakDetector = leakDetector;
Evan Laird04373662020-01-24 17:37:39 -0500203 mFgsFeatureController = fgsFeatureController;
Jason Monk297c04e2018-08-23 17:16:59 -0400204 }
205
Ned Burns2246b332019-12-12 14:39:13 -0500206 /** Once called, the NEM will start processing notification events from system server. */
207 public void attach(NotificationListener notificationListener) {
Ned Burnse6855d62019-12-19 16:38:08 -0500208 notificationListener.addNotificationHandler(mNotifListener);
Ned Burns2246b332019-12-12 14:39:13 -0500209 }
210
Gus Prevasd65c2db2018-12-18 17:13:38 -0500211 /** Adds a {@link NotificationEntryListener}. */
212 public void addNotificationEntryListener(NotificationEntryListener listener) {
213 mNotificationEntryListeners.add(listener);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900214 }
215
Steve Elliott984cfec2019-10-21 15:28:05 -0400216 /**
217 * Removes a {@link NotificationEntryListener} previously registered via
218 * {@link #addNotificationEntryListener(NotificationEntryListener)}.
219 */
220 public void removeNotificationEntryListener(NotificationEntryListener listener) {
221 mNotificationEntryListeners.remove(listener);
222 }
223
Evan Laird04373662020-01-24 17:37:39 -0500224 /** Add a {@link NotificationRemoveInterceptor}. */
225 public void addNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
226 mRemoveInterceptors.add(interceptor);
227 }
228
229 /** Remove a {@link NotificationRemoveInterceptor} */
230 public void removeNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
231 mRemoveInterceptors.remove(interceptor);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700232 }
233
Beverly95a0802ac2020-02-10 15:27:40 -0500234 public void setUpWithPresenter(NotificationPresenter presenter) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900235 mPresenter = presenter;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900236 }
237
Gus Prevas5c84abb2018-12-28 16:41:49 -0500238 /** Adds multiple {@link NotificationLifetimeExtender}s. */
239 public void addNotificationLifetimeExtenders(List<NotificationLifetimeExtender> extenders) {
240 for (NotificationLifetimeExtender extender : extenders) {
241 addNotificationLifetimeExtender(extender);
242 }
243 }
244
245 /** Adds a {@link NotificationLifetimeExtender}. */
246 public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
247 mNotificationLifetimeExtenders.add(extender);
Mady Mellor1a4e86f2019-05-03 16:07:23 -0700248 extender.setCallback(key -> removeNotification(key, mLatestRankingMap,
249 UNDEFINED_DISMISS_REASON));
Gus Prevas5c84abb2018-12-28 16:41:49 -0500250 }
251
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900252 @Override
Selim Cinekba069ae2020-04-01 19:45:16 -0700253 public void onChangeAllowed() {
Beverly85d4c192019-09-30 11:40:39 -0400254 updateNotifications("reordering is now allowed");
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900255 }
256
Mady Mellorc2ff0112019-03-28 14:18:06 -0700257 /**
258 * Requests a notification to be removed.
259 *
260 * @param n the notification to remove.
261 * @param reason why it is being removed e.g. {@link NotificationListenerService#REASON_CANCEL},
262 * or 0 if unknown.
263 */
264 public void performRemoveNotification(StatusBarNotification n, int reason) {
Selim Cinek7bc9be72019-03-06 18:42:05 -0800265 final NotificationVisibility nv = obtainVisibility(n.getKey());
Gus Prevasca1b6f72018-12-28 10:53:11 -0500266 removeNotificationInternal(
Mady Mellorc2ff0112019-03-28 14:18:06 -0700267 n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
268 reason);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900269 }
270
Selim Cinek7bc9be72019-03-06 18:42:05 -0800271 private NotificationVisibility obtainVisibility(String key) {
Evan Laird181de622019-10-24 09:53:02 -0400272 NotificationEntry e = mActiveNotifications.get(key);
273 final int rank;
274 if (e != null) {
275 rank = e.getRanking().getRank();
276 } else {
277 rank = 0;
278 }
279
280 final int count = mActiveNotifications.size();
Selim Cinek7bc9be72019-03-06 18:42:05 -0800281 NotificationVisibility.NotificationLocation location =
Evan Laird181de622019-10-24 09:53:02 -0400282 NotificationLogger.getNotificationLocation(getActiveNotificationUnfiltered(key));
Selim Cinek7bc9be72019-03-06 18:42:05 -0800283 return NotificationVisibility.obtain(key, rank, count, true, location);
284 }
285
Beverly85d4c192019-09-30 11:40:39 -0400286 private void abortExistingInflation(String key, String reason) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900287 if (mPendingNotifications.containsKey(key)) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500288 NotificationEntry entry = mPendingNotifications.get(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900289 entry.abortTask();
290 mPendingNotifications.remove(key);
Kevin Hand5ca4592020-02-10 15:18:46 -0800291 for (NotifCollectionListener listener : mNotifCollectionListeners) {
292 listener.onEntryCleanUp(entry);
293 }
Ned Burnsafe77bc2020-01-30 20:45:07 -0500294 mLogger.logInflationAborted(key, "pending", reason);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900295 }
Evan Laird181de622019-10-24 09:53:02 -0400296 NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900297 if (addedEntry != null) {
298 addedEntry.abortTask();
Ned Burnsafe77bc2020-01-30 20:45:07 -0500299 mLogger.logInflationAborted(key, "active", reason);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900300 }
301 }
302
Ned Burnsdc10c952018-11-21 14:36:21 -0500303 /**
304 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
305 * about the failure.
306 *
307 * WARNING: this will call back into us. Don't hold any locks.
308 */
Kevin Hanbd142932020-03-10 18:27:50 -0700309 private void handleInflationException(StatusBarNotification n, Exception e) {
Gus Prevasdca2be52018-12-21 11:25:10 -0500310 removeNotificationInternal(
Mady Mellorc2ff0112019-03-28 14:18:06 -0700311 n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
312 REASON_ERROR);
Gus Prevasca1b6f72018-12-28 10:53:11 -0500313 for (NotificationEntryListener listener : mNotificationEntryListeners) {
314 listener.onInflationError(n, e);
Ned Burnsdc10c952018-11-21 14:36:21 -0500315 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900316 }
317
Kevin Hanbd142932020-03-10 18:27:50 -0700318 private final InflationCallback mInflationCallback = new InflationCallback() {
319 @Override
320 public void handleInflationException(NotificationEntry entry, Exception e) {
321 NotificationEntryManager.this.handleInflationException(entry.getSbn(), e);
322 }
323
324 @Override
325 public void onAsyncInflationFinished(NotificationEntry entry) {
326 mPendingNotifications.remove(entry.getKey());
327 // If there was an async task started after the removal, we don't want to add it back to
328 // the list, otherwise we might get leaks.
329 if (!entry.isRowRemoved()) {
330 boolean isNew = getActiveNotificationUnfiltered(entry.getKey()) == null;
331 mLogger.logNotifInflated(entry.getKey(), isNew);
332 if (isNew) {
333 for (NotificationEntryListener listener : mNotificationEntryListeners) {
334 listener.onEntryInflated(entry);
335 }
336 addActiveNotification(entry);
337 updateNotifications("onAsyncInflationFinished");
338 for (NotificationEntryListener listener : mNotificationEntryListeners) {
339 listener.onNotificationAdded(entry);
340 }
341 } else {
342 for (NotificationEntryListener listener : mNotificationEntryListeners) {
343 listener.onEntryReinflated(entry);
344 }
Ned Burns3d6b3962018-12-07 21:26:00 -0500345 }
Kevin01a53cb2018-11-09 18:19:54 -0800346 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900347 }
Kevin Hanbd142932020-03-10 18:27:50 -0700348 };
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900349
Ned Burnse6855d62019-12-19 16:38:08 -0500350 private final NotificationHandler mNotifListener = new NotificationHandler() {
Ned Burns2246b332019-12-12 14:39:13 -0500351 @Override
352 public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
353 final boolean isUpdate = mActiveNotifications.containsKey(sbn.getKey());
354 if (isUpdate) {
355 updateNotification(sbn, rankingMap);
356 } else {
357 addNotification(sbn, rankingMap);
358 }
359 }
360
361 @Override
362 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
363 removeNotification(sbn.getKey(), rankingMap, UNDEFINED_DISMISS_REASON);
364 }
365
366 @Override
367 public void onNotificationRemoved(
368 StatusBarNotification sbn,
369 RankingMap rankingMap,
370 int reason) {
371 removeNotification(sbn.getKey(), rankingMap, reason);
372 }
373
374 @Override
375 public void onNotificationRankingUpdate(RankingMap rankingMap) {
376 updateNotificationRanking(rankingMap);
377 }
378 };
379
Evan Laird181de622019-10-24 09:53:02 -0400380 /**
381 * Equivalent to the old NotificationData#add
382 * @param entry - an entry which is prepared for display
383 */
384 private void addActiveNotification(NotificationEntry entry) {
385 Assert.isMainThread();
386
387 mActiveNotifications.put(entry.getKey(), entry);
388 mGroupManager.onEntryAdded(entry);
389 updateRankingAndSort(mRankingManager.getRankingMap(), "addEntryInternalInternal");
390 }
391
392 /**
393 * Available so that tests can directly manipulate the list of active notifications easily
394 *
395 * @param entry the entry to add directly to the visible notification map
396 */
397 @VisibleForTesting
398 public void addActiveNotificationForTest(NotificationEntry entry) {
399 mActiveNotifications.put(entry.getKey(), entry);
400 mGroupManager.onEntryAdded(entry);
401
402 reapplyFilterAndSort("addVisibleNotification");
403 }
404
405
Evan Laird181de622019-10-24 09:53:02 -0400406 public void removeNotification(String key, RankingMap ranking,
Mady Mellorc2ff0112019-03-28 14:18:06 -0700407 int reason) {
Selim Cinek7bc9be72019-03-06 18:42:05 -0800408 removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
Mady Mellorc2ff0112019-03-28 14:18:06 -0700409 false /* removedByUser */, reason);
Kevina5ff1fa2018-08-21 16:35:48 -0700410 }
411
Gus Prevasdca2be52018-12-21 11:25:10 -0500412 private void removeNotificationInternal(
413 String key,
Evan Laird181de622019-10-24 09:53:02 -0400414 @Nullable RankingMap ranking,
Gus Prevasca1b6f72018-12-28 10:53:11 -0500415 @Nullable NotificationVisibility visibility,
Gus Prevasdca2be52018-12-21 11:25:10 -0500416 boolean forceRemove,
Mady Mellorc2ff0112019-03-28 14:18:06 -0700417 boolean removedByUser,
418 int reason) {
419
Evan Laird04373662020-01-24 17:37:39 -0500420 final NotificationEntry entry = getActiveNotificationUnfiltered(key);
421
422 for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
423 if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
424 // Remove intercepted; log and skip
Ned Burnsafe77bc2020-01-30 20:45:07 -0500425 mLogger.logRemovalIntercepted(key);
Evan Laird04373662020-01-24 17:37:39 -0500426 return;
427 }
Mady Mellorc2ff0112019-03-28 14:18:06 -0700428 }
429
Gus Prevasf37435a2018-12-20 15:40:01 -0500430 boolean lifetimeExtended = false;
431
Evan Laird3ceaa9b2019-08-05 17:11:54 -0400432 // Notification was canceled before it got inflated
433 if (entry == null) {
434 NotificationEntry pendingEntry = mPendingNotifications.get(key);
435 if (pendingEntry != null) {
436 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
437 if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
438 extendLifetime(pendingEntry, extender);
439 lifetimeExtended = true;
Ned Burnsafe77bc2020-01-30 20:45:07 -0500440 mLogger.logLifetimeExtended(key, extender.getClass().getName(), "pending");
Evan Laird3ceaa9b2019-08-05 17:11:54 -0400441 }
442 }
443 }
444 }
445
446 if (!lifetimeExtended) {
Beverly85d4c192019-09-30 11:40:39 -0400447 abortExistingInflation(key, "removeNotification");
Evan Laird3ceaa9b2019-08-05 17:11:54 -0400448 }
449
Gus Prevasd65c2db2018-12-18 17:13:38 -0500450 if (entry != null) {
Gus Prevasf37435a2018-12-20 15:40:01 -0500451 // If a manager needs to keep the notification around for whatever reason, we
452 // keep the notification
Selim Cinek7bc9be72019-03-06 18:42:05 -0800453 boolean entryDismissed = entry.isRowDismissed();
454 if (!forceRemove && !entryDismissed) {
Gus Prevasf37435a2018-12-20 15:40:01 -0500455 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
456 if (extender.shouldExtendLifetime(entry)) {
457 mLatestRankingMap = ranking;
Ned Burnsa5dad552019-01-29 17:59:10 -0500458 extendLifetime(entry, extender);
Gus Prevasf37435a2018-12-20 15:40:01 -0500459 lifetimeExtended = true;
Ned Burnsafe77bc2020-01-30 20:45:07 -0500460 mLogger.logLifetimeExtended(key, extender.getClass().getName(), "active");
Gus Prevasf37435a2018-12-20 15:40:01 -0500461 break;
462 }
Kevina5ff1fa2018-08-21 16:35:48 -0700463 }
464 }
Gus Prevasf37435a2018-12-20 15:40:01 -0500465
466 if (!lifetimeExtended) {
467 // At this point, we are guaranteed the notification will be removed
468
469 // Ensure any managers keeping the lifetime extended stop managing the entry
Ned Burnsa5dad552019-01-29 17:59:10 -0500470 cancelLifetimeExtension(entry);
Gus Prevasf37435a2018-12-20 15:40:01 -0500471
Gus Prevasf37435a2018-12-20 15:40:01 -0500472 if (entry.rowExists()) {
473 entry.removeRow();
Gus Prevasf37435a2018-12-20 15:40:01 -0500474 }
475
Ned Burns88aeaf72020-03-13 20:45:19 -0400476 mAllNotifications.remove(entry);
477
Gus Prevasf37435a2018-12-20 15:40:01 -0500478 // Let's remove the children if this was a summary
479 handleGroupSummaryRemoved(key);
Evan Laird181de622019-10-24 09:53:02 -0400480 removeVisibleNotification(key);
Beverly85d4c192019-09-30 11:40:39 -0400481 updateNotifications("removeNotificationInternal");
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500482 mLeakDetector.trackGarbage(entry);
Selim Cinek7bc9be72019-03-06 18:42:05 -0800483 removedByUser |= entryDismissed;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900484
Ned Burnsafe77bc2020-01-30 20:45:07 -0500485 mLogger.logNotifRemoved(entry.getKey(), removedByUser);
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500486 for (NotificationEntryListener listener : mNotificationEntryListeners) {
Julia Reynolds138111f2020-02-26 11:17:39 -0500487 listener.onEntryRemoved(entry, visibility, removedByUser, reason);
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500488 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800489 for (NotifCollectionListener listener : mNotifCollectionListeners) {
490 // NEM doesn't have a good knowledge of reasons so defaulting to unknown.
491 listener.onEntryRemoved(entry, REASON_UNKNOWN);
492 }
493 for (NotifCollectionListener listener : mNotifCollectionListeners) {
494 listener.onEntryCleanUp(entry);
495 }
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500496 }
Gus Prevas8621bd22018-12-20 15:04:25 -0500497 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900498 }
499
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900500 /**
501 * Ensures that the group children are cancelled immediately when the group summary is cancelled
502 * instead of waiting for the notification manager to send all cancels. Otherwise this could
503 * lead to flickers.
504 *
505 * This also ensures that the animation looks nice and only consists of a single disappear
506 * animation instead of multiple.
507 * @param key the key of the notification was removed
508 *
509 */
510 private void handleGroupSummaryRemoved(String key) {
Evan Laird181de622019-10-24 09:53:02 -0400511 NotificationEntry entry = getActiveNotificationUnfiltered(key);
Evan Laird94492852018-10-25 13:43:01 -0400512 if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400513 if (entry.getSbn().getOverrideGroupKey() != null && !entry.isRowDismissed()) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900514 // We don't want to remove children for autobundled notifications as they are not
515 // always cancelled. We only remove them if they were dismissed by the user.
516 return;
517 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500518 List<NotificationEntry> childEntries = entry.getChildren();
Evan Laird94492852018-10-25 13:43:01 -0400519 if (childEntries == null) {
520 return;
521 }
522 for (int i = 0; i < childEntries.size(); i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500523 NotificationEntry childEntry = childEntries.get(i);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400524 boolean isForeground = (entry.getSbn().getNotification().flags
Selim Cinek038e2592018-08-16 16:30:45 -0700525 & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Kevina5ff1fa2018-08-21 16:35:48 -0700526 boolean keepForReply =
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500527 mRemoteInputManagerLazy.get().shouldKeepForRemoteInputHistory(childEntry)
528 || mRemoteInputManagerLazy.get().shouldKeepForSmartReplyHistory(childEntry);
Selim Cinek038e2592018-08-16 16:30:45 -0700529 if (isForeground || keepForReply) {
530 // the child is a foreground service notification which we can't remove or it's
531 // a child we're keeping around for reply!
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900532 continue;
533 }
Evan Lairdce2d1af2019-05-30 16:00:22 -0400534 childEntry.setKeepInParent(true);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900535 // we need to set this state earlier as otherwise we might generate some weird
536 // animations
Evan Lairdce2d1af2019-05-30 16:00:22 -0400537 childEntry.removeRow();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900538 }
539 }
540 }
541
Ned Burnsc4111072020-04-03 01:46:55 -0400542 private void addNotificationInternal(
543 StatusBarNotification notification,
Evan Laird181de622019-10-24 09:53:02 -0400544 RankingMap rankingMap) throws InflationException {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900545 String key = notification.getKey();
Kevina97ea052018-09-11 13:53:18 -0700546 if (DEBUG) {
547 Log.d(TAG, "addNotification key=" + key);
548 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900549
Evan Laird181de622019-10-24 09:53:02 -0400550 updateRankingAndSort(rankingMap, "addNotificationInternal");
551
Ned Burnse372f322019-09-24 13:14:08 -0400552 Ranking ranking = new Ranking();
Tony Mak628cb932018-06-19 18:30:41 +0100553 rankingMap.getRanking(key, ranking);
Ned Burns69760872019-01-03 15:36:38 -0500554
Evan Laird04373662020-01-24 17:37:39 -0500555 NotificationEntry entry = new NotificationEntry(
556 notification,
557 ranking,
Evan Lairdccf5c5e2020-02-06 14:01:28 -0500558 mFgsFeatureController.isForegroundServiceDismissalEnabled(),
559 SystemClock.uptimeMillis());
Kevin Han37423222020-03-10 16:05:07 -0700560 for (NotifCollectionListener listener : mNotifCollectionListeners) {
561 listener.onEntryBind(entry, notification);
562 }
563 mAllNotifications.add(entry);
Ned Burns69760872019-01-03 15:36:38 -0500564
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500565 mLeakDetector.trackInstance(entry);
Beverly8b493df2019-12-18 14:16:50 -0500566
Kevin Hanf400aee2020-01-30 13:10:29 -0800567 for (NotifCollectionListener listener : mNotifCollectionListeners) {
568 listener.onEntryInit(entry);
569 }
570
Ned Burns69760872019-01-03 15:36:38 -0500571 // Construct the expanded view.
Beverly8b493df2019-12-18 14:16:50 -0500572 if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500573 mNotificationRowBinderLazy.get()
Kevin Hanbd142932020-03-10 18:27:50 -0700574 .inflateViews(
575 entry,
576 () -> performRemoveNotification(notification, REASON_CANCEL),
577 mInflationCallback);
Beverly8b493df2019-12-18 14:16:50 -0500578 }
Ned Burns69760872019-01-03 15:36:38 -0500579
Beverly85d4c192019-09-30 11:40:39 -0400580 abortExistingInflation(key, "addNotification");
Gus Prevasd65c2db2018-12-18 17:13:38 -0500581 mPendingNotifications.put(key, entry);
Ned Burnsafe77bc2020-01-30 20:45:07 -0500582 mLogger.logNotifAdded(entry.getKey());
Gus Prevasd65c2db2018-12-18 17:13:38 -0500583 for (NotificationEntryListener listener : mNotificationEntryListeners) {
584 listener.onPendingEntryAdded(entry);
Ned Burns3d6b3962018-12-07 21:26:00 -0500585 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800586 for (NotifCollectionListener listener : mNotifCollectionListeners) {
587 listener.onEntryAdded(entry);
588 }
Ned Burnsc4111072020-04-03 01:46:55 -0400589 for (NotifCollectionListener listener : mNotifCollectionListeners) {
590 listener.onRankingApplied();
591 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900592 }
593
Evan Laird181de622019-10-24 09:53:02 -0400594 public void addNotification(StatusBarNotification notification, RankingMap ranking) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900595 try {
596 addNotificationInternal(notification, ranking);
597 } catch (InflationException e) {
598 handleInflationException(notification, e);
599 }
600 }
601
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900602 private void updateNotificationInternal(StatusBarNotification notification,
Evan Laird181de622019-10-24 09:53:02 -0400603 RankingMap ranking) throws InflationException {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900604 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
605
606 final String key = notification.getKey();
Beverly85d4c192019-09-30 11:40:39 -0400607 abortExistingInflation(key, "updateNotification");
Evan Laird181de622019-10-24 09:53:02 -0400608 NotificationEntry entry = getActiveNotificationUnfiltered(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900609 if (entry == null) {
610 return;
611 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900612
Kevina5ff1fa2018-08-21 16:35:48 -0700613 // Notification is updated so it is essentially re-added and thus alive again. Don't need
Kevine9e938c2018-09-06 13:38:11 -0700614 // to keep its lifetime extended.
Ned Burnsa5dad552019-01-29 17:59:10 -0500615 cancelLifetimeExtension(entry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900616
Evan Laird181de622019-10-24 09:53:02 -0400617 updateRankingAndSort(ranking, "updateNotificationInternal");
618 StatusBarNotification oldSbn = entry.getSbn();
619 entry.setSbn(notification);
Kevin Han37423222020-03-10 16:05:07 -0700620 for (NotifCollectionListener listener : mNotifCollectionListeners) {
621 listener.onEntryBind(entry, notification);
622 } mGroupManager.onEntryUpdated(entry, oldSbn);
Evan Laird181de622019-10-24 09:53:02 -0400623
Ned Burnsafe77bc2020-01-30 20:45:07 -0500624 mLogger.logNotifUpdated(entry.getKey());
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800625 for (NotificationEntryListener listener : mNotificationEntryListeners) {
626 listener.onPreEntryUpdated(entry);
627 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800628 for (NotifCollectionListener listener : mNotifCollectionListeners) {
629 listener.onEntryUpdated(entry);
630 }
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800631
Beverly8b493df2019-12-18 14:16:50 -0500632 if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500633 mNotificationRowBinderLazy.get()
Kevin Hanbd142932020-03-10 18:27:50 -0700634 .inflateViews(
635 entry,
636 () -> performRemoveNotification(notification, REASON_CANCEL),
637 mInflationCallback);
Beverly8b493df2019-12-18 14:16:50 -0500638 }
639
Beverly85d4c192019-09-30 11:40:39 -0400640 updateNotifications("updateNotificationInternal");
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900641
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900642 if (DEBUG) {
643 // Is this for you?
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500644 boolean isForCurrentUser = mKeyguardEnvironment
Jason Monk297c04e2018-08-23 17:16:59 -0400645 .isNotificationForCurrentProfiles(notification);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900646 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
647 }
648
Gus Prevasb43dc652018-12-20 13:11:45 -0500649 for (NotificationEntryListener listener : mNotificationEntryListeners) {
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800650 listener.onPostEntryUpdated(entry);
Gus Prevasb43dc652018-12-20 13:11:45 -0500651 }
Ned Burnsc4111072020-04-03 01:46:55 -0400652 for (NotifCollectionListener listener : mNotifCollectionListeners) {
653 listener.onRankingApplied();
654 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900655 }
656
Evan Laird181de622019-10-24 09:53:02 -0400657 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900658 try {
659 updateNotificationInternal(notification, ranking);
660 } catch (InflationException e) {
661 handleInflationException(notification, e);
662 }
663 }
664
Beverly85d4c192019-09-30 11:40:39 -0400665 /**
666 * Update the notifications
667 * @param reason why the notifications are updating
668 */
669 public void updateNotifications(String reason) {
Evan Laird181de622019-10-24 09:53:02 -0400670 reapplyFilterAndSort(reason);
Kevin Han4943b862020-03-23 17:14:39 -0700671 if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
Ned Burnsf36c6252019-01-09 19:12:33 -0500672 mPresenter.updateNotificationViews();
673 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900674 }
675
Evan Laird181de622019-10-24 09:53:02 -0400676 public void updateNotificationRanking(RankingMap rankingMap) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500677 List<NotificationEntry> entries = new ArrayList<>();
Evan Laird181de622019-10-24 09:53:02 -0400678 entries.addAll(getVisibleNotifications());
Tony Mak628cb932018-06-19 18:30:41 +0100679 entries.addAll(mPendingNotifications.values());
680
681 // Has a copy of the current UI adjustments.
682 ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
Gus Prevas33fbc152018-12-04 13:17:32 -0500683 ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500684 for (NotificationEntry entry : entries) {
Tony Mak628cb932018-06-19 18:30:41 +0100685 NotificationUiAdjustment adjustment =
686 NotificationUiAdjustment.extractFromNotificationEntry(entry);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400687 oldAdjustments.put(entry.getKey(), adjustment);
688 oldImportances.put(entry.getKey(), entry.getImportance());
Tony Mak628cb932018-06-19 18:30:41 +0100689 }
690
691 // Populate notification entries from the new rankings.
Evan Laird181de622019-10-24 09:53:02 -0400692 updateRankingAndSort(rankingMap, "updateNotificationRanking");
Tony Mak628cb932018-06-19 18:30:41 +0100693 updateRankingOfPendingNotifications(rankingMap);
694
695 // By comparing the old and new UI adjustments, reinflate the view accordingly.
Ned Burnsf81c4c42019-01-07 14:10:43 -0500696 for (NotificationEntry entry : entries) {
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500697 mNotificationRowBinderLazy.get()
698 .onNotificationRankingUpdated(
699 entry,
700 oldImportances.get(entry.getKey()),
701 oldAdjustments.get(entry.getKey()),
702 NotificationUiAdjustment.extractFromNotificationEntry(entry));
Tony Mak628cb932018-06-19 18:30:41 +0100703 }
704
Beverly85d4c192019-09-30 11:40:39 -0400705 updateNotifications("updateNotificationRanking");
Mark Renoufbbcf07f2019-05-09 10:42:43 -0400706
707 for (NotificationEntryListener listener : mNotificationEntryListeners) {
708 listener.onNotificationRankingUpdated(rankingMap);
709 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800710 for (NotifCollectionListener listener : mNotifCollectionListeners) {
711 listener.onRankingUpdate(rankingMap);
712 }
Ned Burnsc4111072020-04-03 01:46:55 -0400713 for (NotifCollectionListener listener : mNotifCollectionListeners) {
714 listener.onRankingApplied();
715 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900716 }
717
Evan Laird181de622019-10-24 09:53:02 -0400718 private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
Tony Mak628cb932018-06-19 18:30:41 +0100719 if (rankingMap == null) {
720 return;
721 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500722 for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
Ned Burnse372f322019-09-24 13:14:08 -0400723 Ranking ranking = new Ranking();
Evan Laird9afe7662019-10-16 17:16:39 -0400724 if (rankingMap.getRanking(pendingNotification.getKey(), ranking)) {
Ned Burnse372f322019-09-24 13:14:08 -0400725 pendingNotification.setRanking(ranking);
726 }
Tony Mak628cb932018-06-19 18:30:41 +0100727 }
728 }
729
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900730 /**
Ned Burns3d6b3962018-12-07 21:26:00 -0500731 * @return An iterator for all "pending" notifications. Pending notifications are newly-posted
732 * notifications whose views have not yet been inflated. In general, the system pretends like
733 * these don't exist, although there are a couple exceptions.
734 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500735 public Iterable<NotificationEntry> getPendingNotificationsIterator() {
Ned Burns3d6b3962018-12-07 21:26:00 -0500736 return mPendingNotifications.values();
737 }
Ned Burnsa5dad552019-01-29 17:59:10 -0500738
Beverly201cdd52019-10-18 14:30:46 -0400739 /**
Evan Laird181de622019-10-24 09:53:02 -0400740 * Use this method to retrieve a notification entry that has been prepared for presentation.
741 * Note that the notification may be filtered out and never shown to the user.
742 *
743 * @see #getVisibleNotifications() for the currently sorted and filtered list
744 *
745 * @return a {@link NotificationEntry} if it has been prepared, else null
746 */
747 public NotificationEntry getActiveNotificationUnfiltered(String key) {
748 return mActiveNotifications.get(key);
749 }
750
751 /**
Beverly201cdd52019-10-18 14:30:46 -0400752 * Gets the pending or visible notification entry with the given key. Returns null if
753 * notification doesn't exist.
754 */
Evan Laird181de622019-10-24 09:53:02 -0400755 public NotificationEntry getPendingOrActiveNotif(String key) {
Beverly201cdd52019-10-18 14:30:46 -0400756 if (mPendingNotifications.containsKey(key)) {
757 return mPendingNotifications.get(key);
758 } else {
Evan Laird181de622019-10-24 09:53:02 -0400759 return mActiveNotifications.get(key);
Beverly201cdd52019-10-18 14:30:46 -0400760 }
761 }
762
Ned Burnsa5dad552019-01-29 17:59:10 -0500763 private void extendLifetime(NotificationEntry entry, NotificationLifetimeExtender extender) {
764 NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
765 if (activeExtender != null && activeExtender != extender) {
766 activeExtender.setShouldManageLifetime(entry, false);
767 }
768 mRetainedNotifications.put(entry, extender);
769 extender.setShouldManageLifetime(entry, true);
770 }
771
772 private void cancelLifetimeExtension(NotificationEntry entry) {
773 NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
774 if (activeExtender != null) {
775 activeExtender.setShouldManageLifetime(entry, false);
776 }
777 }
Ned Burnsc5864672019-02-20 12:57:29 -0500778
Evan Laird181de622019-10-24 09:53:02 -0400779 /*
780 * -----
781 * Annexed from NotificationData below:
782 * Some of these methods may be redundant but require some reworking to remove. For now
783 * we'll try to keep the behavior the same and can simplify these interfaces in another pass
784 */
785
786 /** Internalization of NotificationData#remove */
787 private void removeVisibleNotification(String key) {
788 // no need to synchronize if we're on the main thread dawg
789 Assert.isMainThread();
790
791 NotificationEntry removed = mActiveNotifications.remove(key);
792
793 if (removed == null) return;
794 mGroupManager.onEntryRemoved(removed);
795 }
796
797 /** @return list of active notifications filtered for the current user */
798 public List<NotificationEntry> getActiveNotificationsForCurrentUser() {
799 Assert.isMainThread();
800 ArrayList<NotificationEntry> filtered = new ArrayList<>();
801
802 final int len = mActiveNotifications.size();
803 for (int i = 0; i < len; i++) {
804 NotificationEntry entry = mActiveNotifications.valueAt(i);
805 final StatusBarNotification sbn = entry.getSbn();
806 if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
807 continue;
808 }
809 filtered.add(entry);
810 }
811
812 return filtered;
813 }
814
815 //TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking
816 /**
817 * @param rankingMap the {@link RankingMap} to apply to the current notification list
Ned Burnsafe77bc2020-01-30 20:45:07 -0500818 * @param reason the reason for calling this method, which will be logged
Evan Laird181de622019-10-24 09:53:02 -0400819 */
820 public void updateRanking(RankingMap rankingMap, String reason) {
821 updateRankingAndSort(rankingMap, reason);
Ned Burnsc4111072020-04-03 01:46:55 -0400822 for (NotifCollectionListener listener : mNotifCollectionListeners) {
823 listener.onRankingApplied();
824 }
Evan Laird181de622019-10-24 09:53:02 -0400825 }
826
827 /** Resorts / filters the current notification set with the current RankingMap */
828 public void reapplyFilterAndSort(String reason) {
829 updateRankingAndSort(mRankingManager.getRankingMap(), reason);
830 }
831
832 /** Calls to NotificationRankingManager and updates mSortedAndFiltered */
833 private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
834 mSortedAndFiltered.clear();
835 mSortedAndFiltered.addAll(mRankingManager.updateRanking(
836 rankingMap, mActiveNotifications.values(), reason));
837 }
838
839 /** dump the current active notification list. Called from StatusBar */
840 public void dump(PrintWriter pw, String indent) {
841 pw.println("NotificationEntryManager");
842 int filteredLen = mSortedAndFiltered.size();
843 pw.print(indent);
844 pw.println("active notifications: " + filteredLen);
845 int active;
846 for (active = 0; active < filteredLen; active++) {
847 NotificationEntry e = mSortedAndFiltered.get(active);
848 dumpEntry(pw, indent, active, e);
849 }
850 synchronized (mActiveNotifications) {
851 int totalLen = mActiveNotifications.size();
852 pw.print(indent);
853 pw.println("inactive notifications: " + (totalLen - active));
854 int inactiveCount = 0;
855 for (int i = 0; i < totalLen; i++) {
856 NotificationEntry entry = mActiveNotifications.valueAt(i);
857 if (!mSortedAndFiltered.contains(entry)) {
858 dumpEntry(pw, indent, inactiveCount, entry);
859 inactiveCount++;
860 }
861 }
862 }
863 }
864
865 private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
866 pw.print(indent);
Ned Burnsd8b51542020-03-13 20:52:43 -0400867 pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.getIcons().getStatusBarIcon());
Evan Laird181de622019-10-24 09:53:02 -0400868 StatusBarNotification n = e.getSbn();
869 pw.print(indent);
870 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
871 + e.getRanking().getImportance());
872 pw.print(indent);
873 pw.println(" notification=" + n.getNotification());
874 }
875
876 /**
877 * This is the answer to the question "what notifications should the user be seeing right now?"
878 * These are sorted and filtered, and directly inform the notification shade what to show
879 *
880 * @return A read-only list of the currently active notifications
881 */
882 public List<NotificationEntry> getVisibleNotifications() {
883 return mReadOnlyNotifications;
884 }
885
Ned Burns88aeaf72020-03-13 20:45:19 -0400886 /**
887 * Returns a collections containing ALL notifications we know about, including ones that are
888 * hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
889 */
890 @Override
891 public Collection<NotificationEntry> getAllNotifs() {
892 return mReadOnlyAllNotifications;
893 }
894
Evan Laird181de622019-10-24 09:53:02 -0400895 /** @return A count of the active notifications */
896 public int getActiveNotificationsCount() {
897 return mReadOnlyNotifications.size();
898 }
899
900 /**
901 * @return {@code true} if there is at least one notification that should be visible right now
902 */
903 public boolean hasActiveNotifications() {
904 return mReadOnlyNotifications.size() != 0;
905 }
906
Kevin Hanf400aee2020-01-30 13:10:29 -0800907 @Override
908 public void addCollectionListener(NotifCollectionListener listener) {
909 mNotifCollectionListeners.add(listener);
910 }
911
Evan Laird181de622019-10-24 09:53:02 -0400912 /*
913 * End annexation
914 * -----
915 */
916
917
918 /**
919 * Provides access to keyguard state and user settings dependent data.
920 */
921 public interface KeyguardEnvironment {
922 /** true if the device is provisioned (should always be true in practice) */
923 boolean isDeviceProvisioned();
924 /** true if the notification is for the current profiles */
925 boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
926 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900927}