blob: d6471243e053906dbf8ae6504f759a201aa9561b [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 }
Beverly8ca664b2020-05-07 14:46:50 -0400443 if (!lifetimeExtended) {
444 // At this point, we are guaranteed the notification will be removed
445 mAllNotifications.remove(pendingEntry);
446 }
Evan Laird3ceaa9b2019-08-05 17:11:54 -0400447 }
448 }
449
450 if (!lifetimeExtended) {
Beverly85d4c192019-09-30 11:40:39 -0400451 abortExistingInflation(key, "removeNotification");
Evan Laird3ceaa9b2019-08-05 17:11:54 -0400452 }
453
Gus Prevasd65c2db2018-12-18 17:13:38 -0500454 if (entry != null) {
Gus Prevasf37435a2018-12-20 15:40:01 -0500455 // If a manager needs to keep the notification around for whatever reason, we
456 // keep the notification
Selim Cinek7bc9be72019-03-06 18:42:05 -0800457 boolean entryDismissed = entry.isRowDismissed();
458 if (!forceRemove && !entryDismissed) {
Gus Prevasf37435a2018-12-20 15:40:01 -0500459 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
460 if (extender.shouldExtendLifetime(entry)) {
461 mLatestRankingMap = ranking;
Ned Burnsa5dad552019-01-29 17:59:10 -0500462 extendLifetime(entry, extender);
Gus Prevasf37435a2018-12-20 15:40:01 -0500463 lifetimeExtended = true;
Ned Burnsafe77bc2020-01-30 20:45:07 -0500464 mLogger.logLifetimeExtended(key, extender.getClass().getName(), "active");
Gus Prevasf37435a2018-12-20 15:40:01 -0500465 break;
466 }
Kevina5ff1fa2018-08-21 16:35:48 -0700467 }
468 }
Gus Prevasf37435a2018-12-20 15:40:01 -0500469
470 if (!lifetimeExtended) {
471 // At this point, we are guaranteed the notification will be removed
472
473 // Ensure any managers keeping the lifetime extended stop managing the entry
Ned Burnsa5dad552019-01-29 17:59:10 -0500474 cancelLifetimeExtension(entry);
Gus Prevasf37435a2018-12-20 15:40:01 -0500475
Gus Prevasf37435a2018-12-20 15:40:01 -0500476 if (entry.rowExists()) {
477 entry.removeRow();
Gus Prevasf37435a2018-12-20 15:40:01 -0500478 }
479
Ned Burns88aeaf72020-03-13 20:45:19 -0400480 mAllNotifications.remove(entry);
481
Gus Prevasf37435a2018-12-20 15:40:01 -0500482 // Let's remove the children if this was a summary
483 handleGroupSummaryRemoved(key);
Evan Laird181de622019-10-24 09:53:02 -0400484 removeVisibleNotification(key);
Beverly85d4c192019-09-30 11:40:39 -0400485 updateNotifications("removeNotificationInternal");
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500486 mLeakDetector.trackGarbage(entry);
Selim Cinek7bc9be72019-03-06 18:42:05 -0800487 removedByUser |= entryDismissed;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900488
Ned Burnsafe77bc2020-01-30 20:45:07 -0500489 mLogger.logNotifRemoved(entry.getKey(), removedByUser);
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500490 for (NotificationEntryListener listener : mNotificationEntryListeners) {
Julia Reynolds138111f2020-02-26 11:17:39 -0500491 listener.onEntryRemoved(entry, visibility, removedByUser, reason);
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500492 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800493 for (NotifCollectionListener listener : mNotifCollectionListeners) {
494 // NEM doesn't have a good knowledge of reasons so defaulting to unknown.
495 listener.onEntryRemoved(entry, REASON_UNKNOWN);
496 }
497 for (NotifCollectionListener listener : mNotifCollectionListeners) {
498 listener.onEntryCleanUp(entry);
499 }
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500500 }
Gus Prevas8621bd22018-12-20 15:04:25 -0500501 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900502 }
503
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900504 /**
505 * Ensures that the group children are cancelled immediately when the group summary is cancelled
506 * instead of waiting for the notification manager to send all cancels. Otherwise this could
507 * lead to flickers.
508 *
509 * This also ensures that the animation looks nice and only consists of a single disappear
510 * animation instead of multiple.
511 * @param key the key of the notification was removed
512 *
513 */
514 private void handleGroupSummaryRemoved(String key) {
Evan Laird181de622019-10-24 09:53:02 -0400515 NotificationEntry entry = getActiveNotificationUnfiltered(key);
Evan Laird94492852018-10-25 13:43:01 -0400516 if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400517 if (entry.getSbn().getOverrideGroupKey() != null && !entry.isRowDismissed()) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900518 // We don't want to remove children for autobundled notifications as they are not
519 // always cancelled. We only remove them if they were dismissed by the user.
520 return;
521 }
Kevin Han43077f92020-02-28 12:51:53 -0800522 List<NotificationEntry> childEntries = entry.getAttachedNotifChildren();
Evan Laird94492852018-10-25 13:43:01 -0400523 if (childEntries == null) {
524 return;
525 }
526 for (int i = 0; i < childEntries.size(); i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500527 NotificationEntry childEntry = childEntries.get(i);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400528 boolean isForeground = (entry.getSbn().getNotification().flags
Selim Cinek038e2592018-08-16 16:30:45 -0700529 & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Kevina5ff1fa2018-08-21 16:35:48 -0700530 boolean keepForReply =
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500531 mRemoteInputManagerLazy.get().shouldKeepForRemoteInputHistory(childEntry)
532 || mRemoteInputManagerLazy.get().shouldKeepForSmartReplyHistory(childEntry);
Selim Cinek038e2592018-08-16 16:30:45 -0700533 if (isForeground || keepForReply) {
534 // the child is a foreground service notification which we can't remove or it's
535 // a child we're keeping around for reply!
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900536 continue;
537 }
Evan Lairdce2d1af2019-05-30 16:00:22 -0400538 childEntry.setKeepInParent(true);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900539 // we need to set this state earlier as otherwise we might generate some weird
540 // animations
Evan Lairdce2d1af2019-05-30 16:00:22 -0400541 childEntry.removeRow();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900542 }
543 }
544 }
545
Ned Burnsc4111072020-04-03 01:46:55 -0400546 private void addNotificationInternal(
547 StatusBarNotification notification,
Evan Laird181de622019-10-24 09:53:02 -0400548 RankingMap rankingMap) throws InflationException {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900549 String key = notification.getKey();
Kevina97ea052018-09-11 13:53:18 -0700550 if (DEBUG) {
551 Log.d(TAG, "addNotification key=" + key);
552 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900553
Evan Laird181de622019-10-24 09:53:02 -0400554 updateRankingAndSort(rankingMap, "addNotificationInternal");
555
Ned Burnse372f322019-09-24 13:14:08 -0400556 Ranking ranking = new Ranking();
Tony Mak628cb932018-06-19 18:30:41 +0100557 rankingMap.getRanking(key, ranking);
Ned Burns69760872019-01-03 15:36:38 -0500558
Evan Laird04373662020-01-24 17:37:39 -0500559 NotificationEntry entry = new NotificationEntry(
560 notification,
561 ranking,
Evan Lairdccf5c5e2020-02-06 14:01:28 -0500562 mFgsFeatureController.isForegroundServiceDismissalEnabled(),
563 SystemClock.uptimeMillis());
Kevin Han37423222020-03-10 16:05:07 -0700564 for (NotifCollectionListener listener : mNotifCollectionListeners) {
565 listener.onEntryBind(entry, notification);
566 }
567 mAllNotifications.add(entry);
Ned Burns69760872019-01-03 15:36:38 -0500568
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500569 mLeakDetector.trackInstance(entry);
Beverly8b493df2019-12-18 14:16:50 -0500570
Kevin Hanf400aee2020-01-30 13:10:29 -0800571 for (NotifCollectionListener listener : mNotifCollectionListeners) {
572 listener.onEntryInit(entry);
573 }
574
Ned Burns69760872019-01-03 15:36:38 -0500575 // Construct the expanded view.
Beverly8b493df2019-12-18 14:16:50 -0500576 if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500577 mNotificationRowBinderLazy.get()
Kevin Hanbd142932020-03-10 18:27:50 -0700578 .inflateViews(
579 entry,
580 () -> performRemoveNotification(notification, REASON_CANCEL),
581 mInflationCallback);
Beverly8b493df2019-12-18 14:16:50 -0500582 }
Ned Burns69760872019-01-03 15:36:38 -0500583
Beverly85d4c192019-09-30 11:40:39 -0400584 abortExistingInflation(key, "addNotification");
Gus Prevasd65c2db2018-12-18 17:13:38 -0500585 mPendingNotifications.put(key, entry);
Ned Burnsafe77bc2020-01-30 20:45:07 -0500586 mLogger.logNotifAdded(entry.getKey());
Gus Prevasd65c2db2018-12-18 17:13:38 -0500587 for (NotificationEntryListener listener : mNotificationEntryListeners) {
588 listener.onPendingEntryAdded(entry);
Ned Burns3d6b3962018-12-07 21:26:00 -0500589 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800590 for (NotifCollectionListener listener : mNotifCollectionListeners) {
591 listener.onEntryAdded(entry);
592 }
Ned Burnsc4111072020-04-03 01:46:55 -0400593 for (NotifCollectionListener listener : mNotifCollectionListeners) {
594 listener.onRankingApplied();
595 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900596 }
597
Evan Laird181de622019-10-24 09:53:02 -0400598 public void addNotification(StatusBarNotification notification, RankingMap ranking) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900599 try {
600 addNotificationInternal(notification, ranking);
601 } catch (InflationException e) {
602 handleInflationException(notification, e);
603 }
604 }
605
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900606 private void updateNotificationInternal(StatusBarNotification notification,
Evan Laird181de622019-10-24 09:53:02 -0400607 RankingMap ranking) throws InflationException {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900608 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
609
610 final String key = notification.getKey();
Beverly85d4c192019-09-30 11:40:39 -0400611 abortExistingInflation(key, "updateNotification");
Evan Laird181de622019-10-24 09:53:02 -0400612 NotificationEntry entry = getActiveNotificationUnfiltered(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900613 if (entry == null) {
614 return;
615 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900616
Kevina5ff1fa2018-08-21 16:35:48 -0700617 // Notification is updated so it is essentially re-added and thus alive again. Don't need
Kevine9e938c2018-09-06 13:38:11 -0700618 // to keep its lifetime extended.
Ned Burnsa5dad552019-01-29 17:59:10 -0500619 cancelLifetimeExtension(entry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900620
Evan Laird181de622019-10-24 09:53:02 -0400621 updateRankingAndSort(ranking, "updateNotificationInternal");
622 StatusBarNotification oldSbn = entry.getSbn();
623 entry.setSbn(notification);
Kevin Han37423222020-03-10 16:05:07 -0700624 for (NotifCollectionListener listener : mNotifCollectionListeners) {
625 listener.onEntryBind(entry, notification);
Kevin Hanf69b71e2020-04-13 15:46:14 -0700626 }
627 mGroupManager.onEntryUpdated(entry, oldSbn);
Evan Laird181de622019-10-24 09:53:02 -0400628
Ned Burnsafe77bc2020-01-30 20:45:07 -0500629 mLogger.logNotifUpdated(entry.getKey());
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800630 for (NotificationEntryListener listener : mNotificationEntryListeners) {
631 listener.onPreEntryUpdated(entry);
632 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800633 for (NotifCollectionListener listener : mNotifCollectionListeners) {
634 listener.onEntryUpdated(entry);
635 }
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800636
Beverly8b493df2019-12-18 14:16:50 -0500637 if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500638 mNotificationRowBinderLazy.get()
Kevin Hanbd142932020-03-10 18:27:50 -0700639 .inflateViews(
640 entry,
641 () -> performRemoveNotification(notification, REASON_CANCEL),
642 mInflationCallback);
Beverly8b493df2019-12-18 14:16:50 -0500643 }
644
Beverly85d4c192019-09-30 11:40:39 -0400645 updateNotifications("updateNotificationInternal");
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900646
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900647 if (DEBUG) {
648 // Is this for you?
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500649 boolean isForCurrentUser = mKeyguardEnvironment
Jason Monk297c04e2018-08-23 17:16:59 -0400650 .isNotificationForCurrentProfiles(notification);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900651 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
652 }
653
Gus Prevasb43dc652018-12-20 13:11:45 -0500654 for (NotificationEntryListener listener : mNotificationEntryListeners) {
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800655 listener.onPostEntryUpdated(entry);
Gus Prevasb43dc652018-12-20 13:11:45 -0500656 }
Ned Burnsc4111072020-04-03 01:46:55 -0400657 for (NotifCollectionListener listener : mNotifCollectionListeners) {
658 listener.onRankingApplied();
659 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900660 }
661
Evan Laird181de622019-10-24 09:53:02 -0400662 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900663 try {
664 updateNotificationInternal(notification, ranking);
665 } catch (InflationException e) {
666 handleInflationException(notification, e);
667 }
668 }
669
Beverly85d4c192019-09-30 11:40:39 -0400670 /**
671 * Update the notifications
672 * @param reason why the notifications are updating
673 */
674 public void updateNotifications(String reason) {
Evan Laird181de622019-10-24 09:53:02 -0400675 reapplyFilterAndSort(reason);
Kevin Han4943b862020-03-23 17:14:39 -0700676 if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
Steve Elliott1ba1b7c2020-05-05 14:16:32 -0400677 mPresenter.updateNotificationViews(reason);
Ned Burnsf36c6252019-01-09 19:12:33 -0500678 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900679 }
680
Evan Laird181de622019-10-24 09:53:02 -0400681 public void updateNotificationRanking(RankingMap rankingMap) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500682 List<NotificationEntry> entries = new ArrayList<>();
Evan Laird181de622019-10-24 09:53:02 -0400683 entries.addAll(getVisibleNotifications());
Tony Mak628cb932018-06-19 18:30:41 +0100684 entries.addAll(mPendingNotifications.values());
685
686 // Has a copy of the current UI adjustments.
687 ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
Gus Prevas33fbc152018-12-04 13:17:32 -0500688 ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500689 for (NotificationEntry entry : entries) {
Tony Mak628cb932018-06-19 18:30:41 +0100690 NotificationUiAdjustment adjustment =
691 NotificationUiAdjustment.extractFromNotificationEntry(entry);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400692 oldAdjustments.put(entry.getKey(), adjustment);
693 oldImportances.put(entry.getKey(), entry.getImportance());
Tony Mak628cb932018-06-19 18:30:41 +0100694 }
695
696 // Populate notification entries from the new rankings.
Evan Laird181de622019-10-24 09:53:02 -0400697 updateRankingAndSort(rankingMap, "updateNotificationRanking");
Tony Mak628cb932018-06-19 18:30:41 +0100698 updateRankingOfPendingNotifications(rankingMap);
699
700 // By comparing the old and new UI adjustments, reinflate the view accordingly.
Ned Burnsf81c4c42019-01-07 14:10:43 -0500701 for (NotificationEntry entry : entries) {
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500702 mNotificationRowBinderLazy.get()
703 .onNotificationRankingUpdated(
704 entry,
705 oldImportances.get(entry.getKey()),
706 oldAdjustments.get(entry.getKey()),
Kevin Han13909302020-04-15 14:53:17 -0700707 NotificationUiAdjustment.extractFromNotificationEntry(entry),
708 mInflationCallback);
Tony Mak628cb932018-06-19 18:30:41 +0100709 }
710
Beverly85d4c192019-09-30 11:40:39 -0400711 updateNotifications("updateNotificationRanking");
Mark Renoufbbcf07f2019-05-09 10:42:43 -0400712
713 for (NotificationEntryListener listener : mNotificationEntryListeners) {
714 listener.onNotificationRankingUpdated(rankingMap);
715 }
Kevin Hanf400aee2020-01-30 13:10:29 -0800716 for (NotifCollectionListener listener : mNotifCollectionListeners) {
717 listener.onRankingUpdate(rankingMap);
718 }
Ned Burnsc4111072020-04-03 01:46:55 -0400719 for (NotifCollectionListener listener : mNotifCollectionListeners) {
720 listener.onRankingApplied();
721 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900722 }
723
Evan Laird181de622019-10-24 09:53:02 -0400724 private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
Tony Mak628cb932018-06-19 18:30:41 +0100725 if (rankingMap == null) {
726 return;
727 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500728 for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
Ned Burnse372f322019-09-24 13:14:08 -0400729 Ranking ranking = new Ranking();
Evan Laird9afe7662019-10-16 17:16:39 -0400730 if (rankingMap.getRanking(pendingNotification.getKey(), ranking)) {
Ned Burnse372f322019-09-24 13:14:08 -0400731 pendingNotification.setRanking(ranking);
732 }
Tony Mak628cb932018-06-19 18:30:41 +0100733 }
734 }
735
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900736 /**
Ned Burns3d6b3962018-12-07 21:26:00 -0500737 * @return An iterator for all "pending" notifications. Pending notifications are newly-posted
738 * notifications whose views have not yet been inflated. In general, the system pretends like
739 * these don't exist, although there are a couple exceptions.
740 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500741 public Iterable<NotificationEntry> getPendingNotificationsIterator() {
Ned Burns3d6b3962018-12-07 21:26:00 -0500742 return mPendingNotifications.values();
743 }
Ned Burnsa5dad552019-01-29 17:59:10 -0500744
Beverly201cdd52019-10-18 14:30:46 -0400745 /**
Evan Laird181de622019-10-24 09:53:02 -0400746 * Use this method to retrieve a notification entry that has been prepared for presentation.
747 * Note that the notification may be filtered out and never shown to the user.
748 *
749 * @see #getVisibleNotifications() for the currently sorted and filtered list
750 *
751 * @return a {@link NotificationEntry} if it has been prepared, else null
752 */
753 public NotificationEntry getActiveNotificationUnfiltered(String key) {
754 return mActiveNotifications.get(key);
755 }
756
757 /**
Beverly201cdd52019-10-18 14:30:46 -0400758 * Gets the pending or visible notification entry with the given key. Returns null if
759 * notification doesn't exist.
760 */
Evan Laird181de622019-10-24 09:53:02 -0400761 public NotificationEntry getPendingOrActiveNotif(String key) {
Beverly201cdd52019-10-18 14:30:46 -0400762 if (mPendingNotifications.containsKey(key)) {
763 return mPendingNotifications.get(key);
764 } else {
Evan Laird181de622019-10-24 09:53:02 -0400765 return mActiveNotifications.get(key);
Beverly201cdd52019-10-18 14:30:46 -0400766 }
767 }
768
Ned Burnsa5dad552019-01-29 17:59:10 -0500769 private void extendLifetime(NotificationEntry entry, NotificationLifetimeExtender extender) {
770 NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
771 if (activeExtender != null && activeExtender != extender) {
772 activeExtender.setShouldManageLifetime(entry, false);
773 }
774 mRetainedNotifications.put(entry, extender);
775 extender.setShouldManageLifetime(entry, true);
776 }
777
778 private void cancelLifetimeExtension(NotificationEntry entry) {
779 NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
780 if (activeExtender != null) {
781 activeExtender.setShouldManageLifetime(entry, false);
782 }
783 }
Ned Burnsc5864672019-02-20 12:57:29 -0500784
Evan Laird181de622019-10-24 09:53:02 -0400785 /*
786 * -----
787 * Annexed from NotificationData below:
788 * Some of these methods may be redundant but require some reworking to remove. For now
789 * we'll try to keep the behavior the same and can simplify these interfaces in another pass
790 */
791
792 /** Internalization of NotificationData#remove */
793 private void removeVisibleNotification(String key) {
794 // no need to synchronize if we're on the main thread dawg
795 Assert.isMainThread();
796
797 NotificationEntry removed = mActiveNotifications.remove(key);
798
799 if (removed == null) return;
800 mGroupManager.onEntryRemoved(removed);
801 }
802
803 /** @return list of active notifications filtered for the current user */
804 public List<NotificationEntry> getActiveNotificationsForCurrentUser() {
805 Assert.isMainThread();
806 ArrayList<NotificationEntry> filtered = new ArrayList<>();
807
808 final int len = mActiveNotifications.size();
809 for (int i = 0; i < len; i++) {
810 NotificationEntry entry = mActiveNotifications.valueAt(i);
811 final StatusBarNotification sbn = entry.getSbn();
812 if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
813 continue;
814 }
815 filtered.add(entry);
816 }
817
818 return filtered;
819 }
820
821 //TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking
822 /**
823 * @param rankingMap the {@link RankingMap} to apply to the current notification list
Ned Burnsafe77bc2020-01-30 20:45:07 -0500824 * @param reason the reason for calling this method, which will be logged
Evan Laird181de622019-10-24 09:53:02 -0400825 */
826 public void updateRanking(RankingMap rankingMap, String reason) {
827 updateRankingAndSort(rankingMap, reason);
Ned Burnsc4111072020-04-03 01:46:55 -0400828 for (NotifCollectionListener listener : mNotifCollectionListeners) {
829 listener.onRankingApplied();
830 }
Evan Laird181de622019-10-24 09:53:02 -0400831 }
832
833 /** Resorts / filters the current notification set with the current RankingMap */
834 public void reapplyFilterAndSort(String reason) {
835 updateRankingAndSort(mRankingManager.getRankingMap(), reason);
836 }
837
838 /** Calls to NotificationRankingManager and updates mSortedAndFiltered */
839 private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
840 mSortedAndFiltered.clear();
841 mSortedAndFiltered.addAll(mRankingManager.updateRanking(
842 rankingMap, mActiveNotifications.values(), reason));
843 }
844
845 /** dump the current active notification list. Called from StatusBar */
846 public void dump(PrintWriter pw, String indent) {
847 pw.println("NotificationEntryManager");
848 int filteredLen = mSortedAndFiltered.size();
849 pw.print(indent);
850 pw.println("active notifications: " + filteredLen);
851 int active;
852 for (active = 0; active < filteredLen; active++) {
853 NotificationEntry e = mSortedAndFiltered.get(active);
854 dumpEntry(pw, indent, active, e);
855 }
856 synchronized (mActiveNotifications) {
857 int totalLen = mActiveNotifications.size();
858 pw.print(indent);
859 pw.println("inactive notifications: " + (totalLen - active));
860 int inactiveCount = 0;
861 for (int i = 0; i < totalLen; i++) {
862 NotificationEntry entry = mActiveNotifications.valueAt(i);
863 if (!mSortedAndFiltered.contains(entry)) {
864 dumpEntry(pw, indent, inactiveCount, entry);
865 inactiveCount++;
866 }
867 }
868 }
869 }
870
871 private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
872 pw.print(indent);
Ned Burnsd8b51542020-03-13 20:52:43 -0400873 pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.getIcons().getStatusBarIcon());
Evan Laird181de622019-10-24 09:53:02 -0400874 StatusBarNotification n = e.getSbn();
875 pw.print(indent);
876 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
877 + e.getRanking().getImportance());
878 pw.print(indent);
879 pw.println(" notification=" + n.getNotification());
880 }
881
882 /**
883 * This is the answer to the question "what notifications should the user be seeing right now?"
884 * These are sorted and filtered, and directly inform the notification shade what to show
885 *
886 * @return A read-only list of the currently active notifications
887 */
888 public List<NotificationEntry> getVisibleNotifications() {
889 return mReadOnlyNotifications;
890 }
891
Ned Burns88aeaf72020-03-13 20:45:19 -0400892 /**
893 * Returns a collections containing ALL notifications we know about, including ones that are
894 * hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
895 */
896 @Override
897 public Collection<NotificationEntry> getAllNotifs() {
898 return mReadOnlyAllNotifications;
899 }
900
Evan Laird181de622019-10-24 09:53:02 -0400901 /** @return A count of the active notifications */
902 public int getActiveNotificationsCount() {
903 return mReadOnlyNotifications.size();
904 }
905
906 /**
907 * @return {@code true} if there is at least one notification that should be visible right now
908 */
909 public boolean hasActiveNotifications() {
910 return mReadOnlyNotifications.size() != 0;
911 }
912
Kevin Hanf400aee2020-01-30 13:10:29 -0800913 @Override
914 public void addCollectionListener(NotifCollectionListener listener) {
915 mNotifCollectionListeners.add(listener);
916 }
917
Evan Laird181de622019-10-24 09:53:02 -0400918 /*
919 * End annexation
920 * -----
921 */
922
923
924 /**
925 * Provides access to keyguard state and user settings dependent data.
926 */
927 public interface KeyguardEnvironment {
928 /** true if the device is provisioned (should always be true in practice) */
929 boolean isDeviceProvisioned();
930 /** true if the notification is for the current profiles */
931 boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
932 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900933}