blob: d583048fbb268fca87c3b11f90250d2c7e245716 [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
Rohan Shah20790b82018-07-02 17:21:04 -070014 * limitations under the License
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090015 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090018
Evan Laird181de622019-10-24 09:53:02 -040019import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
Mady Mellorc2ff0112019-03-28 14:18:06 -070020import static android.service.notification.NotificationListenerService.REASON_CANCEL;
21
Mady Mellor1a4e86f2019-05-03 16:07:23 -070022import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
23
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090024import static junit.framework.Assert.assertNotNull;
25import static junit.framework.Assert.assertNull;
26import static junit.framework.Assert.assertTrue;
27
28import static org.junit.Assert.assertEquals;
Beverly8ca664b2020-05-07 14:46:50 -040029import static org.junit.Assert.assertFalse;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090030import static org.mockito.ArgumentMatchers.any;
Mady Mellorc2ff0112019-03-28 14:18:06 -070031import static org.mockito.ArgumentMatchers.anyBoolean;
32import static org.mockito.ArgumentMatchers.anyInt;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090033import static org.mockito.ArgumentMatchers.eq;
Mady Mellorc2ff0112019-03-28 14:18:06 -070034import static org.mockito.Mockito.atLeastOnce;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090035import static org.mockito.Mockito.doAnswer;
Mady Mellor0ad5b9d2019-01-08 14:59:55 -080036import static org.mockito.Mockito.inOrder;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090037import static org.mockito.Mockito.mock;
38import static org.mockito.Mockito.never;
39import static org.mockito.Mockito.verify;
40import static org.mockito.Mockito.when;
41
42import android.app.ActivityManager;
43import android.app.Notification;
Evan Laird181de622019-10-24 09:53:02 -040044import android.app.NotificationChannel;
Tony Mak628cb932018-06-19 18:30:41 +010045import android.app.PendingIntent;
Tony Mak628cb932018-06-19 18:30:41 +010046import android.content.Intent;
47import android.graphics.drawable.Icon;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090048import android.os.Handler;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090049import android.os.UserHandle;
Evan Laird181de622019-10-24 09:53:02 -040050import android.service.notification.NotificationListenerService.Ranking;
51import android.service.notification.NotificationListenerService.RankingMap;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090052import android.service.notification.StatusBarNotification;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090053import android.testing.AndroidTestingRunner;
54import android.testing.TestableLooper;
Ned Burnsa5dad552019-01-29 17:59:10 -050055import android.util.ArraySet;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090056
Ned Burnsa5dad552019-01-29 17:59:10 -050057import androidx.annotation.NonNull;
Brett Chabot84151d92019-02-27 15:37:59 -080058import androidx.test.filters.SmallTest;
Ned Burnsa5dad552019-01-29 17:59:10 -050059
Mady Mellorc2ff0112019-03-28 14:18:06 -070060import com.android.internal.statusbar.NotificationVisibility;
Jason Monk297c04e2018-08-23 17:16:59 -040061import com.android.systemui.Dependency;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090062import com.android.systemui.R;
63import com.android.systemui.SysuiTestCase;
Beverly8b493df2019-12-18 14:16:50 -050064import com.android.systemui.statusbar.FeatureFlags;
Kevina5ff1fa2018-08-21 16:35:48 -070065import com.android.systemui.statusbar.NotificationLifetimeExtender;
Dave Mankoff48091ed2019-10-04 16:33:14 -040066import com.android.systemui.statusbar.NotificationMediaManager;
Rohan Shah20790b82018-07-02 17:21:04 -070067import com.android.systemui.statusbar.NotificationPresenter;
68import com.android.systemui.statusbar.NotificationRemoteInputManager;
Mady Mellorc2ff0112019-03-28 14:18:06 -070069import com.android.systemui.statusbar.NotificationRemoveInterceptor;
Evan Laird181de622019-10-24 09:53:02 -040070import com.android.systemui.statusbar.RankingBuilder;
Rohan Shah20790b82018-07-02 17:21:04 -070071import com.android.systemui.statusbar.SmartReplyController;
Evan Laird181de622019-10-24 09:53:02 -040072import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
Ned Burnsf81c4c42019-01-07 14:10:43 -050073import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Beverly79c89ec2019-12-13 10:33:01 -050074import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
Evan Laird181de622019-10-24 09:53:02 -040075import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
Kevin Han225b7122020-01-31 15:42:42 -080076import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
Beverlye558f1d2020-01-07 16:28:58 -050077import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
Steve Elliott960f4ce2019-12-04 15:13:52 -050078import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
Rohan Shah20790b82018-07-02 17:21:04 -070079import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Kevin Han225b7122020-01-31 15:42:42 -080080import com.android.systemui.statusbar.notification.row.NotificationEntryManagerInflationTest;
Rohan Shah20790b82018-07-02 17:21:04 -070081import com.android.systemui.statusbar.notification.row.RowInflaterTask;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090082import com.android.systemui.statusbar.phone.NotificationGroupManager;
83import com.android.systemui.statusbar.policy.DeviceProvisionedController;
84import com.android.systemui.statusbar.policy.HeadsUpManager;
Dave Mankoff02dcaf52020-01-08 15:42:06 -050085import com.android.systemui.util.leak.LeakDetector;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090086
87import org.junit.Before;
88import org.junit.Test;
89import org.junit.runner.RunWith;
90import org.mockito.ArgumentCaptor;
Mady Mellor0ad5b9d2019-01-08 14:59:55 -080091import org.mockito.InOrder;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090092import org.mockito.Mock;
93import org.mockito.MockitoAnnotations;
94
Tony Mak628cb932018-06-19 18:30:41 +010095import java.util.ArrayList;
96import java.util.Arrays;
Beverly8ca664b2020-05-07 14:46:50 -040097import java.util.Collection;
Evan Laird181de622019-10-24 09:53:02 -040098import java.util.List;
Ned Burnsa5dad552019-01-29 17:59:10 -050099import java.util.Set;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900100
Kevin Han225b7122020-01-31 15:42:42 -0800101/**
102 * Unit tests for {@link NotificationEntryManager}. This test will not test any interactions with
103 * inflation. Instead, for functional inflation tests, see
104 * {@link NotificationEntryManagerInflationTest}.
105 */
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900106@SmallTest
107@RunWith(AndroidTestingRunner.class)
Evan Laird181de622019-10-24 09:53:02 -0400108@TestableLooper.RunWithLooper()
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900109public class NotificationEntryManagerTest extends SysuiTestCase {
110 private static final String TEST_PACKAGE_NAME = "test";
111 private static final int TEST_UID = 0;
112
113 @Mock private NotificationPresenter mPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -0400114 @Mock private KeyguardEnvironment mEnvironment;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900115 @Mock private ExpandableNotificationRow mRow;
Ned Burnsc5864672019-02-20 12:57:29 -0500116 @Mock private NotificationEntryListener mEntryListener;
Mady Mellorc2ff0112019-03-28 14:18:06 -0700117 @Mock private NotificationRemoveInterceptor mRemoveInterceptor;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900118 @Mock private HeadsUpManager mHeadsUpManager;
Evan Laird181de622019-10-24 09:53:02 -0400119 @Mock private RankingMap mRankingMap;
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900120 @Mock private NotificationGroupManager mGroupManager;
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900121 @Mock private NotificationRemoteInputManager mRemoteInputManager;
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900122 @Mock private DeviceProvisionedController mDeviceProvisionedController;
Tony Mak628cb932018-06-19 18:30:41 +0100123 @Mock private RowInflaterTask mAsyncInflationTask;
Ned Burnsafe77bc2020-01-30 20:45:07 -0500124 @Mock private NotificationEntryManagerLogger mLogger;
Beverly8b493df2019-12-18 14:16:50 -0500125 @Mock private FeatureFlags mFeatureFlags;
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500126 @Mock private LeakDetector mLeakDetector;
Dave Mankoffc0211ff2020-02-07 15:36:12 -0500127 @Mock private NotificationMediaManager mNotificationMediaManager;
Kevin Han225b7122020-01-31 15:42:42 -0800128 @Mock private NotificationRowBinder mNotificationRowBinder;
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900129
Beverly201cdd52019-10-18 14:30:46 -0400130 private int mId;
Ned Burnsf81c4c42019-01-07 14:10:43 -0500131 private NotificationEntry mEntry;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900132 private StatusBarNotification mSbn;
Kevin Han225b7122020-01-31 15:42:42 -0800133 private NotificationEntryManager mEntryManager;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900134
Dan Sandler1d958f82018-01-09 21:10:26 -0500135 private void setUserSentiment(String key, int sentiment) {
136 doAnswer(invocationOnMock -> {
Evan Laird181de622019-10-24 09:53:02 -0400137 Ranking ranking = (Ranking)
Dan Sandler1d958f82018-01-09 21:10:26 -0500138 invocationOnMock.getArguments()[1];
139 ranking.populate(
140 key,
141 0,
142 false,
143 0,
144 0,
Evan Laird181de622019-10-24 09:53:02 -0400145 IMPORTANCE_DEFAULT,
Dan Sandler1d958f82018-01-09 21:10:26 -0500146 null, null,
Danning Chen10326cf2020-01-16 13:29:13 -0800147 null, null, null, true, sentiment, false, -1, false, null, null, false, false,
Mady Mellor56515c42020-02-18 17:58:36 -0800148 false, null, false);
Tony Mak628cb932018-06-19 18:30:41 +0100149 return true;
Evan Laird181de622019-10-24 09:53:02 -0400150 }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
Tony Mak628cb932018-06-19 18:30:41 +0100151 }
152
153 private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
154 doAnswer(invocationOnMock -> {
Evan Laird181de622019-10-24 09:53:02 -0400155 Ranking ranking = (Ranking)
Tony Mak628cb932018-06-19 18:30:41 +0100156 invocationOnMock.getArguments()[1];
157 ranking.populate(
158 key,
159 0,
160 false,
161 0,
162 0,
Evan Laird181de622019-10-24 09:53:02 -0400163 IMPORTANCE_DEFAULT,
Tony Mak628cb932018-06-19 18:30:41 +0100164 null, null,
165 null, null, null, true,
Evan Laird181de622019-10-24 09:53:02 -0400166 Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
Mady Mellor56515c42020-02-18 17:58:36 -0800167 false, smartActions, null, false, false, false, null, false);
Dan Sandler1d958f82018-01-09 21:10:26 -0500168 return true;
Evan Laird181de622019-10-24 09:53:02 -0400169 }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
Dan Sandler1d958f82018-01-09 21:10:26 -0500170 }
171
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900172 @Before
173 public void setUp() {
174 MockitoAnnotations.initMocks(this);
Beverly433923a2020-01-22 11:23:24 -0500175 mDependency.injectMockDependency(SmartReplyController.class);
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900176
Beverly1467c9e2020-02-18 13:31:29 -0500177 allowTestableLooperAsMainThread();
Jason Monk297c04e2018-08-23 17:16:59 -0400178 mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Beverly433923a2020-01-22 11:23:24 -0500179 Handler.createAsync(TestableLooper.get(this).getLooper()));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900180
Beverly201cdd52019-10-18 14:30:46 -0400181 mEntry = createNotification();
Evan Laird9afe7662019-10-16 17:16:39 -0400182 mSbn = mEntry.getSbn();
Ned Burns60e94592019-09-06 14:47:25 -0400183
Beverly8b493df2019-12-18 14:16:50 -0500184 when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
185 when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
Kevin Han225b7122020-01-31 15:42:42 -0800186 mEntryManager = new NotificationEntryManager(
Ned Burnsafe77bc2020-01-30 20:45:07 -0500187 mLogger,
Evan Laird181de622019-10-24 09:53:02 -0400188 mGroupManager,
189 new NotificationRankingManager(
Dave Mankoffc0211ff2020-02-07 15:36:12 -0500190 () -> mNotificationMediaManager,
Evan Laird181de622019-10-24 09:53:02 -0400191 mGroupManager,
192 mHeadsUpManager,
193 mock(NotificationFilter.class),
Ned Burnsafe77bc2020-01-30 20:45:07 -0500194 mLogger,
Steve Elliott960f4ce2019-12-04 15:13:52 -0500195 mock(NotificationSectionsFeatureManager.class),
Beverlye558f1d2020-01-07 16:28:58 -0500196 mock(PeopleNotificationIdentifier.class),
197 mock(HighPriorityProvider.class)),
Beverly8b493df2019-12-18 14:16:50 -0500198 mEnvironment,
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500199 mFeatureFlags,
Kevin Han225b7122020-01-31 15:42:42 -0800200 () -> mNotificationRowBinder,
Dave Mankoff02dcaf52020-01-08 15:42:06 -0500201 () -> mRemoteInputManager,
Evan Laird04373662020-01-24 17:37:39 -0500202 mLeakDetector,
203 mock(ForegroundServiceDismissalFeatureController.class)
Evan Laird181de622019-10-24 09:53:02 -0400204 );
Beverly95a0802ac2020-02-10 15:27:40 -0500205 mEntryManager.setUpWithPresenter(mPresenter);
Gus Prevas8621bd22018-12-20 15:04:25 -0500206 mEntryManager.addNotificationEntryListener(mEntryListener);
Evan Laird04373662020-01-24 17:37:39 -0500207 mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
Gus Prevas8ba88a82018-12-18 11:13:44 -0500208
Kevin Han225b7122020-01-31 15:42:42 -0800209 setUserSentiment(mSbn.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900210 }
211
212 @Test
Kevin Han225b7122020-01-31 15:42:42 -0800213 public void testAddNotification_setsUserSentiment() {
Jason Monk6dceace2018-05-15 20:24:07 -0400214 mEntryManager.addNotification(mSbn, mRankingMap);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900215
Ned Burnsf81c4c42019-01-07 14:10:43 -0500216 ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
217 NotificationEntry.class);
Kevin Han225b7122020-01-31 15:42:42 -0800218 verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
Ned Burnsf81c4c42019-01-07 14:10:43 -0500219 NotificationEntry entry = entryCaptor.getValue();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900220
Kevin Han225b7122020-01-31 15:42:42 -0800221 assertEquals(entry.getUserSentiment(), Ranking.USER_SENTIMENT_NEUTRAL);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900222 }
223
224 @Test
Kevin Han225b7122020-01-31 15:42:42 -0800225 public void testUpdateNotification_updatesUserSentiment() {
Evan Laird181de622019-10-24 09:53:02 -0400226 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400227 setUserSentiment(
Evan Laird181de622019-10-24 09:53:02 -0400228 mEntry.getKey(), Ranking.USER_SENTIMENT_NEGATIVE);
Dan Sandler1d958f82018-01-09 21:10:26 -0500229
Jason Monk6dceace2018-05-15 20:24:07 -0400230 mEntryManager.updateNotification(mSbn, mRankingMap);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900231
Kevin Han225b7122020-01-31 15:42:42 -0800232 assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, mEntry.getUserSentiment());
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900233 }
234
235 @Test
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800236 public void testUpdateNotification_prePostEntryOrder() throws Exception {
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800237 TestableLooper.get(this).processAllMessages();
238
Evan Laird181de622019-10-24 09:53:02 -0400239 mEntryManager.addActiveNotificationForTest(mEntry);
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800240
241 mEntryManager.updateNotification(mSbn, mRankingMap);
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800242
243 // Ensure that update callbacks happen in correct order
Evan Laird181de622019-10-24 09:53:02 -0400244 InOrder order = inOrder(mEntryListener, mPresenter, mEntryListener);
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800245 order.verify(mEntryListener).onPreEntryUpdated(mEntry);
Steve Elliott1ba1b7c2020-05-05 14:16:32 -0400246 order.verify(mPresenter).updateNotificationViews(any());
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800247 order.verify(mEntryListener).onPostEntryUpdated(mEntry);
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800248 }
249
250 @Test
Evan Laird181de622019-10-24 09:53:02 -0400251 public void testRemoveNotification() {
Evan Laird94492852018-10-25 13:43:01 -0400252 mEntry.setRow(mRow);
Evan Laird181de622019-10-24 09:53:02 -0400253 mEntryManager.addActiveNotificationForTest(mEntry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900254
Mady Mellor1a4e86f2019-05-03 16:07:23 -0700255 mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900256
Steve Elliott1ba1b7c2020-05-05 14:16:32 -0400257 verify(mPresenter).updateNotificationViews(any());
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500258 verify(mEntryListener).onEntryRemoved(
Julia Reynolds138111f2020-02-26 11:17:39 -0500259 eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900260 verify(mRow).setRemoved();
261
Evan Laird181de622019-10-24 09:53:02 -0400262 assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900263 }
Julia Reynoldsfc640012018-02-21 12:25:27 -0500264
265 @Test
Beverly8ca664b2020-05-07 14:46:50 -0400266 public void testRemoveUninflatedNotification_removesNotificationFromAllNotifsList() {
267 // GIVEN an uninflated entry is added
268 mEntryManager.addNotification(mSbn, mRankingMap);
269 assertTrue(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
270
271 // WHEN the uninflated entry is removed
272 mEntryManager.performRemoveNotification(mSbn, UNDEFINED_DISMISS_REASON);
273
274 // THEN the entry is still removed from the allNotifications list
275 assertFalse(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
276 }
277
278 @Test
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500279 public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500280
Mady Mellor1a4e86f2019-05-03 16:07:23 -0700281 mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500282
283 verify(mEntryListener, never()).onEntryRemoved(
Julia Reynolds138111f2020-02-26 11:17:39 -0500284 eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500285 }
286
287 @Test
Evan Laird181de622019-10-24 09:53:02 -0400288 public void testRemoveNotification_whilePending() {
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500289 mEntryManager.addNotification(mSbn, mRankingMap);
Mady Mellor1a4e86f2019-05-03 16:07:23 -0700290 mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500291
292 verify(mEntryListener, never()).onEntryRemoved(
Julia Reynolds138111f2020-02-26 11:17:39 -0500293 eq(mEntry), any(), eq(false /* removedByUser */), eq(UNDEFINED_DISMISS_REASON));
Kenny Guy8cc15d22018-05-09 09:50:55 +0100294 }
295
296 @Test
Tony Mak628cb932018-06-19 18:30:41 +0100297 public void testUpdateNotificationRanking() {
Jason Monk297c04e2018-08-23 17:16:59 -0400298 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
299 when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
300 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100301
Evan Laird94492852018-10-25 13:43:01 -0400302 mEntry.setRow(mRow);
Tony Mak628cb932018-06-19 18:30:41 +0100303 mEntry.setInflationTask(mAsyncInflationTask);
Evan Laird181de622019-10-24 09:53:02 -0400304 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400305 setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
Tony Mak628cb932018-06-19 18:30:41 +0100306
307 mEntryManager.updateNotificationRanking(mRankingMap);
Ned Burns5a9e35202019-09-06 22:26:33 -0400308 assertEquals(1, mEntry.getSmartActions().size());
309 assertEquals("action", mEntry.getSmartActions().get(0).title);
Mady Mellor68478692019-05-23 14:40:54 -0700310 verify(mEntryListener).onNotificationRankingUpdated(mRankingMap);
Tony Mak628cb932018-06-19 18:30:41 +0100311 }
312
313 @Test
314 public void testUpdateNotificationRanking_noChange() {
Jason Monk297c04e2018-08-23 17:16:59 -0400315 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
316 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100317
Evan Laird94492852018-10-25 13:43:01 -0400318 mEntry.setRow(mRow);
Evan Laird181de622019-10-24 09:53:02 -0400319 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400320 setSmartActions(mEntry.getKey(), null);
Tony Mak628cb932018-06-19 18:30:41 +0100321
322 mEntryManager.updateNotificationRanking(mRankingMap);
Kevind4660b22018-09-27 10:57:35 -0700323 verify(mRow, never()).setEntry(eq(mEntry));
Ned Burns5a9e35202019-09-06 22:26:33 -0400324 assertNull(mEntry.getSmartActions());
Tony Mak628cb932018-06-19 18:30:41 +0100325 }
326
327 @Test
328 public void testUpdateNotificationRanking_rowNotInflatedYet() {
Jason Monk297c04e2018-08-23 17:16:59 -0400329 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
330 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100331
Evan Laird94492852018-10-25 13:43:01 -0400332 mEntry.setRow(null);
Evan Laird181de622019-10-24 09:53:02 -0400333 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400334 setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
Tony Mak628cb932018-06-19 18:30:41 +0100335
336 mEntryManager.updateNotificationRanking(mRankingMap);
Kevind4660b22018-09-27 10:57:35 -0700337 verify(mRow, never()).setEntry(eq(mEntry));
Ned Burns5a9e35202019-09-06 22:26:33 -0400338 assertEquals(1, mEntry.getSmartActions().size());
339 assertEquals("action", mEntry.getSmartActions().get(0).title);
Tony Mak628cb932018-06-19 18:30:41 +0100340 }
341
342 @Test
343 public void testUpdateNotificationRanking_pendingNotification() {
Jason Monk297c04e2018-08-23 17:16:59 -0400344 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
345 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100346
Evan Laird94492852018-10-25 13:43:01 -0400347 mEntry.setRow(null);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400348 mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
349 setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
Tony Mak628cb932018-06-19 18:30:41 +0100350
351 mEntryManager.updateNotificationRanking(mRankingMap);
Kevind4660b22018-09-27 10:57:35 -0700352 verify(mRow, never()).setEntry(eq(mEntry));
Ned Burns5a9e35202019-09-06 22:26:33 -0400353 assertEquals(1, mEntry.getSmartActions().size());
354 assertEquals("action", mEntry.getSmartActions().get(0).title);
Tony Mak628cb932018-06-19 18:30:41 +0100355 }
356
Ned Burnsa5dad552019-01-29 17:59:10 -0500357 @Test
358 public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
359 // GIVEN an entry manager with a notification
Evan Laird181de622019-10-24 09:53:02 -0400360 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burnsa5dad552019-01-29 17:59:10 -0500361
362 // GIVEN a lifetime extender that always tries to extend lifetime
363 NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
364 when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
365 mEntryManager.addNotificationLifetimeExtender(extender);
366
367 // WHEN the notification is removed
Ned Burns00b4b2d2019-10-17 22:09:27 -0400368 mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Ned Burnsa5dad552019-01-29 17:59:10 -0500369
370 // THEN the extender is asked to manage the lifetime
371 verify(extender).setShouldManageLifetime(mEntry, true);
372 // THEN the notification is retained
Evan Laird181de622019-10-24 09:53:02 -0400373 assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
Julia Reynolds138111f2020-02-26 11:17:39 -0500374 verify(mEntryListener, never()).onEntryRemoved(
375 eq(mEntry), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
Ned Burnsa5dad552019-01-29 17:59:10 -0500376 }
377
378 @Test
379 public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
380 // GIVEN an entry manager with a notification whose life has been extended
Evan Laird181de622019-10-24 09:53:02 -0400381 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burnsa5dad552019-01-29 17:59:10 -0500382 final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
383 mEntryManager.addNotificationLifetimeExtender(extender);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400384 mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
385 assertTrue(extender.isManaging(mEntry.getKey()));
Ned Burnsa5dad552019-01-29 17:59:10 -0500386
387 // WHEN the extender finishes its extension
388 extender.setExtendLifetimes(false);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400389 extender.getCallback().onSafeToRemove(mEntry.getKey());
Ned Burnsa5dad552019-01-29 17:59:10 -0500390
391 // THEN the notification is removed
Evan Laird181de622019-10-24 09:53:02 -0400392 assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
Julia Reynolds138111f2020-02-26 11:17:39 -0500393 verify(mEntryListener).onEntryRemoved(
394 eq(mEntry), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
Ned Burnsa5dad552019-01-29 17:59:10 -0500395 }
396
397 @Test
398 public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() {
399 // GIVEN an entry manager with a notification whose life has been extended
Evan Laird181de622019-10-24 09:53:02 -0400400 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burnsa5dad552019-01-29 17:59:10 -0500401 NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
402 when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
403 mEntryManager.addNotificationLifetimeExtender(extender);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400404 mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Ned Burnsa5dad552019-01-29 17:59:10 -0500405
406 // WHEN the notification is updated
Ned Burns00b4b2d2019-10-17 22:09:27 -0400407 mEntryManager.updateNotification(mEntry.getSbn(), mRankingMap);
Ned Burnsa5dad552019-01-29 17:59:10 -0500408
409 // THEN the lifetime extension is canceled
410 verify(extender).setShouldManageLifetime(mEntry, false);
411 }
412
413 @Test
414 public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() {
415 // GIVEN an entry manager with a notification
Evan Laird181de622019-10-24 09:53:02 -0400416 mEntryManager.addActiveNotificationForTest(mEntry);
Ned Burnsa5dad552019-01-29 17:59:10 -0500417
418 // GIVEN two lifetime extenders, the first which never extends and the second which
419 // always extends
420 NotificationLifetimeExtender extender1 = mock(NotificationLifetimeExtender.class);
421 when(extender1.shouldExtendLifetime(mEntry)).thenReturn(false);
422 NotificationLifetimeExtender extender2 = mock(NotificationLifetimeExtender.class);
423 when(extender2.shouldExtendLifetime(mEntry)).thenReturn(true);
424 mEntryManager.addNotificationLifetimeExtender(extender1);
425 mEntryManager.addNotificationLifetimeExtender(extender2);
426
427 // GIVEN a notification was lifetime-extended and extender2 is managing it
Ned Burns00b4b2d2019-10-17 22:09:27 -0400428 mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Ned Burnsa5dad552019-01-29 17:59:10 -0500429 verify(extender1, never()).setShouldManageLifetime(mEntry, true);
430 verify(extender2).setShouldManageLifetime(mEntry, true);
431
432 // WHEN the extender1 changes its mind and wants to extend the lifetime of the notif
433 when(extender1.shouldExtendLifetime(mEntry)).thenReturn(true);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400434 mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Ned Burnsa5dad552019-01-29 17:59:10 -0500435
436 // THEN extender2 stops managing the notif and extender1 starts managing it
437 verify(extender1).setShouldManageLifetime(mEntry, true);
438 verify(extender2).setShouldManageLifetime(mEntry, false);
439 }
440
Gustav Sennton744e7e12019-01-31 14:45:03 +0000441 /**
442 * Ensure that calling NotificationEntryManager.performRemoveNotification() doesn't crash when
443 * given a notification that has already been removed from NotificationData.
444 */
445 @Test
446 public void testPerformRemoveNotification_removedEntry() {
Evan Laird181de622019-10-24 09:53:02 -0400447 mEntryManager.removeNotification(mSbn.getKey(), null, 0);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700448 mEntryManager.performRemoveNotification(mSbn, REASON_CANCEL);
449 }
450
451 @Test
Evan Laird181de622019-10-24 09:53:02 -0400452 public void testRemoveInterceptor_interceptsDontGetRemoved() throws InterruptedException {
Mady Mellorc2ff0112019-03-28 14:18:06 -0700453 // GIVEN an entry manager with a notification
Evan Laird181de622019-10-24 09:53:02 -0400454 mEntryManager.addActiveNotificationForTest(mEntry);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700455
456 // GIVEN interceptor that intercepts that entry
Evan Laird04373662020-01-24 17:37:39 -0500457 when(mRemoveInterceptor.onNotificationRemoveRequested(
458 eq(mEntry.getKey()), eq(mEntry), anyInt()))
Mady Mellorc2ff0112019-03-28 14:18:06 -0700459 .thenReturn(true);
460
461 // WHEN the notification is removed
Ned Burns00b4b2d2019-10-17 22:09:27 -0400462 mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700463
464 // THEN the interceptor intercepts & the entry is not removed & no listeners are called
Evan Laird181de622019-10-24 09:53:02 -0400465 assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
Mady Mellorc2ff0112019-03-28 14:18:06 -0700466 verify(mEntryListener, never()).onEntryRemoved(eq(mEntry),
Julia Reynolds138111f2020-02-26 11:17:39 -0500467 any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
Mady Mellorc2ff0112019-03-28 14:18:06 -0700468 }
469
470 @Test
471 public void testRemoveInterceptor_notInterceptedGetsRemoved() {
472 // GIVEN an entry manager with a notification
Evan Laird181de622019-10-24 09:53:02 -0400473 mEntryManager.addActiveNotificationForTest(mEntry);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700474
475 // GIVEN interceptor that doesn't intercept
Evan Laird04373662020-01-24 17:37:39 -0500476 when(mRemoveInterceptor.onNotificationRemoveRequested(
477 eq(mEntry.getKey()), eq(mEntry), anyInt()))
Mady Mellorc2ff0112019-03-28 14:18:06 -0700478 .thenReturn(false);
479
480 // WHEN the notification is removed
Ned Burns00b4b2d2019-10-17 22:09:27 -0400481 mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
Mady Mellorc2ff0112019-03-28 14:18:06 -0700482
483 // THEN the interceptor intercepts & the entry is not removed & no listeners are called
Evan Laird181de622019-10-24 09:53:02 -0400484 assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
Mady Mellorc2ff0112019-03-28 14:18:06 -0700485 verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry),
Julia Reynolds138111f2020-02-26 11:17:39 -0500486 any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
Gustav Sennton744e7e12019-01-31 14:45:03 +0000487 }
488
Beverly201cdd52019-10-18 14:30:46 -0400489 private NotificationEntry createNotification() {
Julia Reynoldsdcd70d62020-01-15 10:33:43 -0500490 Notification.Builder n = new Notification.Builder(mContext, "id")
Beverly201cdd52019-10-18 14:30:46 -0400491 .setSmallIcon(R.drawable.ic_person)
492 .setContentTitle("Title")
493 .setContentText("Text");
494
495 return new NotificationEntryBuilder()
496 .setPkg(TEST_PACKAGE_NAME)
497 .setOpPkg(TEST_PACKAGE_NAME)
498 .setUid(TEST_UID)
499 .setId(mId++)
500 .setNotification(n.build())
Julia Reynoldsdcd70d62020-01-15 10:33:43 -0500501 .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
Beverly201cdd52019-10-18 14:30:46 -0400502 .setUser(new UserHandle(ActivityManager.getCurrentUser()))
503 .build();
504 }
505
Evan Laird181de622019-10-24 09:53:02 -0400506 /* Tests annexed from NotificationDataTest go here */
507
508 @Test
509 public void testChannelIsSetWhenAdded() {
510 NotificationChannel nc = new NotificationChannel(
511 "testId",
512 "testName",
513 IMPORTANCE_DEFAULT);
514
515 Ranking r = new RankingBuilder()
516 .setKey(mEntry.getKey())
517 .setChannel(nc)
518 .build();
519
520 RankingMap rm = new RankingMap(new Ranking[] { r });
521
522 // GIVEN: a notification is added, and the ranking updated
523 mEntryManager.addActiveNotificationForTest(mEntry);
524 mEntryManager.updateRanking(rm, "testReason");
525
526 // THEN the notification entry better have a channel on it
527 assertEquals(
528 "Channel must be set when adding a notification",
529 nc.getName(),
530 mEntry.getChannel().getName());
531 }
532
533 @Test
534 public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
Julia Reynoldsdcd70d62020-01-15 10:33:43 -0500535 Notification.Builder n = new Notification.Builder(mContext, "di")
Evan Laird181de622019-10-24 09:53:02 -0400536 .setSmallIcon(R.drawable.ic_person)
537 .setContentTitle("Title")
538 .setContentText("Text");
539
540 NotificationEntry e2 = new NotificationEntryBuilder()
541 .setPkg(TEST_PACKAGE_NAME)
542 .setOpPkg(TEST_PACKAGE_NAME)
543 .setUid(TEST_UID)
544 .setId(mId++)
545 .setNotification(n.build())
546 .setUser(new UserHandle(ActivityManager.getCurrentUser()))
Julia Reynoldsdcd70d62020-01-15 10:33:43 -0500547 .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
Evan Laird181de622019-10-24 09:53:02 -0400548 .build();
549
550 mEntryManager.addActiveNotificationForTest(mEntry);
551 mEntryManager.addActiveNotificationForTest(e2);
552
553 when(mEnvironment.isNotificationForCurrentProfiles(mEntry.getSbn())).thenReturn(false);
554 when(mEnvironment.isNotificationForCurrentProfiles(e2.getSbn())).thenReturn(true);
555
556 List<NotificationEntry> result = mEntryManager.getActiveNotificationsForCurrentUser();
557 assertEquals(result.size(), 1);
558 junit.framework.Assert.assertEquals(result.get(0), e2);
559 }
560
561 /* End annex */
562
Beverly8ca664b2020-05-07 14:46:50 -0400563 private boolean entriesContainKey(Collection<NotificationEntry> entries, String key) {
564 for (NotificationEntry entry : entries) {
565 if (entry.getSbn().getKey().equals(key)) {
566 return true;
567 }
568 }
569 return false;
570 }
571
Tony Mak628cb932018-06-19 18:30:41 +0100572 private Notification.Action createAction() {
573 return new Notification.Action.Builder(
574 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
575 "action",
576 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
577 }
Ned Burnsa5dad552019-01-29 17:59:10 -0500578
579 private static class FakeNotificationLifetimeExtender implements NotificationLifetimeExtender {
580 private NotificationSafeToRemoveCallback mCallback;
581 private boolean mExtendLifetimes = true;
582 private Set<String> mManagedNotifs = new ArraySet<>();
583
584 @Override
585 public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
586 mCallback = callback;
587 }
588
589 @Override
590 public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
591 return mExtendLifetimes;
592 }
593
594 @Override
595 public void setShouldManageLifetime(
596 @NonNull NotificationEntry entry,
597 boolean shouldManage) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400598 final boolean hasEntry = mManagedNotifs.contains(entry.getKey());
Ned Burnsa5dad552019-01-29 17:59:10 -0500599 if (shouldManage) {
600 if (hasEntry) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400601 throw new RuntimeException("Already managing this entry: " + entry.getKey());
Ned Burnsa5dad552019-01-29 17:59:10 -0500602 }
Ned Burns00b4b2d2019-10-17 22:09:27 -0400603 mManagedNotifs.add(entry.getKey());
Ned Burnsa5dad552019-01-29 17:59:10 -0500604 } else {
605 if (!hasEntry) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400606 throw new RuntimeException("Not managing this entry: " + entry.getKey());
Ned Burnsa5dad552019-01-29 17:59:10 -0500607 }
Ned Burns00b4b2d2019-10-17 22:09:27 -0400608 mManagedNotifs.remove(entry.getKey());
Ned Burnsa5dad552019-01-29 17:59:10 -0500609 }
610 }
611
612 public void setExtendLifetimes(boolean extendLifetimes) {
613 mExtendLifetimes = extendLifetimes;
614 }
615
616 public NotificationSafeToRemoveCallback getCallback() {
617 return mCallback;
618 }
619
620 public boolean isManaging(String notificationKey) {
621 return mManagedNotifs.contains(notificationKey);
622 }
623 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900624}