Ned Burns | f36c625 | 2019-01-09 19:12:33 -0500 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.systemui.statusbar.notification; |
| 18 | |
| 19 | import static junit.framework.Assert.assertEquals; |
| 20 | import static junit.framework.Assert.assertTrue; |
| 21 | |
| 22 | import static org.mockito.ArgumentMatchers.anyInt; |
| 23 | import static org.mockito.ArgumentMatchers.anyString; |
| 24 | import static org.mockito.Mockito.never; |
| 25 | import static org.mockito.Mockito.times; |
| 26 | import static org.mockito.Mockito.verify; |
| 27 | import static org.mockito.Mockito.when; |
| 28 | |
| 29 | import android.app.ActivityManager; |
| 30 | import android.app.AppOpsManager; |
| 31 | import android.app.Notification; |
| 32 | import android.os.UserHandle; |
| 33 | import android.service.notification.StatusBarNotification; |
| 34 | import android.support.test.filters.SmallTest; |
| 35 | import android.testing.AndroidTestingRunner; |
| 36 | import android.testing.TestableLooper; |
| 37 | import android.util.ArraySet; |
| 38 | |
| 39 | import com.android.internal.statusbar.NotificationVisibility; |
| 40 | import com.android.systemui.ForegroundServiceController; |
| 41 | import com.android.systemui.R; |
| 42 | import com.android.systemui.SysuiTestCase; |
| 43 | import com.android.systemui.statusbar.notification.collection.NotificationData; |
| 44 | import com.android.systemui.statusbar.notification.collection.NotificationEntry; |
| 45 | import com.android.systemui.statusbar.notification.stack.NotificationListContainer; |
| 46 | import com.android.systemui.statusbar.policy.DeviceProvisionedController; |
| 47 | import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; |
| 48 | |
| 49 | import org.junit.Before; |
| 50 | import org.junit.Test; |
| 51 | import org.junit.runner.RunWith; |
| 52 | import org.mockito.ArgumentCaptor; |
| 53 | import org.mockito.Captor; |
| 54 | import org.mockito.Mock; |
| 55 | import org.mockito.MockitoAnnotations; |
| 56 | |
| 57 | @SmallTest |
| 58 | @RunWith(AndroidTestingRunner.class) |
| 59 | @TestableLooper.RunWithLooper |
| 60 | public 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 | } |