blob: 4b5037bb3f6493ff1981407d0722e4d6cda3b201 [file] [log] [blame]
Ned Burnsf36c6252019-01-09 19:12:33 -05001/*
2 * Copyright (C) 2019 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 */
16
17package com.android.systemui.statusbar.notification;
18
19import static junit.framework.Assert.assertEquals;
20import static junit.framework.Assert.assertTrue;
21
22import static org.mockito.ArgumentMatchers.anyInt;
23import static org.mockito.ArgumentMatchers.anyString;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.times;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.when;
28
29import android.app.ActivityManager;
30import android.app.AppOpsManager;
31import android.app.Notification;
32import android.os.UserHandle;
33import android.service.notification.StatusBarNotification;
34import android.support.test.filters.SmallTest;
35import android.testing.AndroidTestingRunner;
36import android.testing.TestableLooper;
37import android.util.ArraySet;
38
39import com.android.internal.statusbar.NotificationVisibility;
40import com.android.systemui.ForegroundServiceController;
41import com.android.systemui.R;
42import com.android.systemui.SysuiTestCase;
43import com.android.systemui.statusbar.notification.collection.NotificationData;
44import com.android.systemui.statusbar.notification.collection.NotificationEntry;
45import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
46import com.android.systemui.statusbar.policy.DeviceProvisionedController;
47import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
48
49import org.junit.Before;
50import org.junit.Test;
51import org.junit.runner.RunWith;
52import org.mockito.ArgumentCaptor;
53import org.mockito.Captor;
54import org.mockito.Mock;
55import org.mockito.MockitoAnnotations;
56
57@SmallTest
58@RunWith(AndroidTestingRunner.class)
59@TestableLooper.RunWithLooper
60public class NotificationListControllerTest extends SysuiTestCase {
61 private NotificationListController mController;
62
63 @Mock private NotificationEntryManager mEntryManager;
64 @Mock private NotificationListContainer mListContainer;
65 @Mock private ForegroundServiceController mForegroundServiceController;
66 @Mock private DeviceProvisionedController mDeviceProvisionedController;
67
68 @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
69 @Captor private ArgumentCaptor<DeviceProvisionedListener> mProvisionedCaptor;
70
71 private NotificationEntryListener mEntryListener;
72 private DeviceProvisionedListener mProvisionedListener;
73
74 // TODO: Remove this once EntryManager no longer needs to be mocked
75 private NotificationData mNotificationData = new NotificationData();
76
77 private int mNextNotifId = 0;
78
79 @Before
80 public void setUp() {
81 MockitoAnnotations.initMocks(this);
82
83 when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
84
85 mController = new NotificationListController(
86 mEntryManager,
87 mListContainer,
88 mForegroundServiceController,
89 mDeviceProvisionedController);
90 mController.bind();
91
92 // Capture callbacks passed to mocks
93 verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
94 mEntryListener = mEntryListenerCaptor.getValue();
95 verify(mDeviceProvisionedController).addCallback(mProvisionedCaptor.capture());
96 mProvisionedListener = mProvisionedCaptor.getValue();
97 }
98
99 @Test
100 public void testCleanUpViewStateOnEntryRemoved() {
101 final NotificationEntry entry = buildEntry();
102 mEntryListener.onEntryRemoved(
103 entry,
104 NotificationVisibility.obtain(entry.key, 0, 0, true),
105 false);
106 verify(mListContainer).cleanUpViewStateForEntry(entry);
107 }
108
109 @Test
110 public void testCallUpdateNotificationsOnDeviceProvisionedChange() {
111 mProvisionedListener.onDeviceProvisionedChanged();
112 verify(mEntryManager).updateNotifications();
113 }
114
115 @Test
116 public void testAppOps_appOpAddedToForegroundNotif() {
117 // GIVEN a notification associated with a foreground service
118 final NotificationEntry entry = buildEntry();
119 mNotificationData.add(entry);
120 when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
121 .thenReturn(entry.key);
122
123 // WHEN we are notified of a new app op
124 mController.updateNotificationsForAppOp(
125 AppOpsManager.OP_CAMERA,
126 entry.notification.getUid(),
127 entry.notification.getPackageName(),
128 true);
129
130 // THEN the app op is added to the entry
131 assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
132 // THEN updateNotifications() is called
133 verify(mEntryManager, times(1)).updateNotifications();
134 }
135
136 @Test
137 public void testAppOps_appOpAddedToUnrelatedNotif() {
138 // GIVEN No current foreground notifs
139 when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
140 .thenReturn(null);
141
142 // WHEN An unrelated notification gets a new app op
143 mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
144
145 // THEN We never call updateNotifications()
146 verify(mEntryManager, never()).updateNotifications();
147 }
148
149 @Test
150 public void testAppOps_addNotificationWithExistingAppOps() {
151 // GIVEN a notification with three associated app ops that is associated with a foreground
152 // service
153 final NotificationEntry entry = buildEntry();
154 mNotificationData.add(entry);
155 ArraySet<Integer> expected = new ArraySet<>();
156 expected.add(3);
157 expected.add(235);
158 expected.add(1);
159 when(mForegroundServiceController.getStandardLayoutKey(
160 entry.notification.getUserId(),
161 entry.notification.getPackageName())).thenReturn(entry.key);
162 when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
163 entry.notification.getPackageName())).thenReturn(expected);
164
165 // WHEN the notification is added
166 mEntryListener.onBeforeNotificationAdded(entry);
167
168 // THEN the entry is tagged with all three app ops
169 assertEquals(expected.size(), entry.mActiveAppOps.size());
170 for (int op : expected) {
171 assertTrue("Entry missing op " + op, entry.mActiveAppOps.contains(op));
172 }
173 }
174
175 @Test
176 public void testAdd_addNotificationWithNoExistingAppOps() {
177 // GIVEN a notification with NO associated app ops
178 final NotificationEntry entry = buildEntry();
179
180 mNotificationData.add(entry);
181 when(mForegroundServiceController.getStandardLayoutKey(
182 entry.notification.getUserId(),
183 entry.notification.getPackageName())).thenReturn(entry.key);
184 when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
185 entry.notification.getPackageName())).thenReturn(null);
186
187 // WHEN the notification is added
188 mEntryListener.onBeforeNotificationAdded(entry);
189
190 // THEN the entry doesn't have any app ops associated with it
191 assertEquals(0, entry.mActiveAppOps.size());
192 }
193
194 @Test
195 public void testAdd_addNonForegroundNotificationWithExistingAppOps() {
196 // GIVEN a notification with app ops that isn't associated with a foreground service
197 final NotificationEntry entry = buildEntry();
198 mNotificationData.add(entry);
199 ArraySet<Integer> ops = new ArraySet<>();
200 ops.add(3);
201 ops.add(235);
202 ops.add(1);
203 when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
204 entry.notification.getPackageName())).thenReturn(ops);
205 when(mForegroundServiceController.getStandardLayoutKey(
206 entry.notification.getUserId(),
207 entry.notification.getPackageName())).thenReturn("something else");
208
209 // WHEN the notification is added
210 mEntryListener.onBeforeNotificationAdded(entry);
211
212 // THEN the entry doesn't have any app ops associated with it
213 assertEquals(0, entry.mActiveAppOps.size());
214 }
215
216 private NotificationEntry buildEntry() {
217 mNextNotifId++;
218
219 Notification.Builder n = new Notification.Builder(mContext, "")
220 .setSmallIcon(R.drawable.ic_person)
221 .setContentTitle("Title")
222 .setContentText("Text");
223
224 StatusBarNotification notification =
225 new StatusBarNotification(
226 TEST_PACKAGE_NAME,
227 TEST_PACKAGE_NAME,
228 mNextNotifId,
229 null,
230 TEST_UID,
231 0,
232 n.build(),
233 new UserHandle(ActivityManager.getCurrentUser()),
234 null,
235 0);
236 return new NotificationEntry(notification);
237 }
238
239 private static final String TEST_PACKAGE_NAME = "test";
240 private static final int TEST_UID = 0;
241}