1 /* 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 */ 16 17 package com.android.systemui; 18 19 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; 20 21 import static junit.framework.Assert.assertEquals; 22 import static junit.framework.Assert.assertNull; 23 import static junit.framework.TestCase.fail; 24 25 import static org.junit.Assert.assertFalse; 26 import static org.junit.Assert.assertTrue; 27 import static org.mockito.ArgumentMatchers.anyString; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 34 import android.annotation.UserIdInt; 35 import android.app.AppOpsManager; 36 import android.app.Notification; 37 import android.app.NotificationManager; 38 import android.os.Bundle; 39 import android.os.Handler; 40 import android.os.UserHandle; 41 import android.service.notification.StatusBarNotification; 42 import android.testing.AndroidTestingRunner; 43 import android.testing.TestableLooper; 44 import android.widget.RemoteViews; 45 46 import androidx.test.filters.SmallTest; 47 48 import com.android.internal.messages.nano.SystemMessageProto; 49 import com.android.systemui.appops.AppOpsController; 50 import com.android.systemui.statusbar.notification.NotificationEntryListener; 51 import com.android.systemui.statusbar.notification.NotificationEntryManager; 52 import com.android.systemui.statusbar.notification.collection.NotifPipeline; 53 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 54 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; 55 import com.android.systemui.util.time.FakeSystemClock; 56 57 import junit.framework.Assert; 58 59 import org.junit.Before; 60 import org.junit.Test; 61 import org.junit.runner.RunWith; 62 import org.mockito.ArgumentCaptor; 63 import org.mockito.Mock; 64 import org.mockito.MockitoAnnotations; 65 66 @SmallTest 67 @RunWith(AndroidTestingRunner.class) 68 @TestableLooper.RunWithLooper 69 public class ForegroundServiceControllerTest extends SysuiTestCase { 70 private ForegroundServiceController mFsc; 71 private ForegroundServiceNotificationListener mListener; 72 private NotificationEntryListener mEntryListener; 73 private final FakeSystemClock mClock = new FakeSystemClock(); 74 @Mock private NotificationEntryManager mEntryManager; 75 @Mock private AppOpsController mAppOpsController; 76 @Mock private Handler mMainHandler; 77 @Mock private NotifPipeline mNotifPipeline; 78 79 @Before setUp()80 public void setUp() throws Exception { 81 // allow the TestLooper to be asserted as the main thread these tests 82 allowTestableLooperAsMainThread(); 83 84 MockitoAnnotations.initMocks(this); 85 mFsc = new ForegroundServiceController( 86 mEntryManager, mAppOpsController, mMainHandler); 87 mListener = new ForegroundServiceNotificationListener( 88 mContext, mFsc, mEntryManager, mNotifPipeline, 89 mock(ForegroundServiceLifetimeExtender.class), mClock); 90 ArgumentCaptor<NotificationEntryListener> entryListenerCaptor = 91 ArgumentCaptor.forClass(NotificationEntryListener.class); 92 verify(mEntryManager).addNotificationEntryListener( 93 entryListenerCaptor.capture()); 94 mEntryListener = entryListenerCaptor.getValue(); 95 } 96 97 @Test testAppOpsChangedCalledFromBgThread()98 public void testAppOpsChangedCalledFromBgThread() { 99 try { 100 // WHEN onAppOpChanged is called from a different thread than the MainLooper 101 disallowTestableLooperAsMainThread(); 102 NotificationEntry entry = createFgEntry(); 103 mFsc.onAppOpChanged( 104 AppOpsManager.OP_CAMERA, 105 entry.getSbn().getUid(), 106 entry.getSbn().getPackageName(), 107 true); 108 109 // This test is run on the TestableLooper, which is not the MainLooper, so 110 // we expect an exception to be thrown 111 fail("onAppOpChanged shouldn't be allowed to be called from a bg thread."); 112 } catch (IllegalStateException e) { 113 // THEN expect an exception 114 } 115 } 116 117 @Test testAppOps_appOpChangedBeforeNotificationExists()118 public void testAppOps_appOpChangedBeforeNotificationExists() { 119 // GIVEN app op exists, but notification doesn't exist in NEM yet 120 NotificationEntry entry = createFgEntry(); 121 mFsc.onAppOpChanged( 122 AppOpsManager.OP_CAMERA, 123 entry.getSbn().getUid(), 124 entry.getSbn().getPackageName(), 125 true); 126 assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); 127 128 // WHEN the notification is added 129 mEntryListener.onPendingEntryAdded(entry); 130 131 // THEN the app op is added to the entry 132 Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); 133 } 134 135 @Test testAppOps_appOpAddedToForegroundNotif()136 public void testAppOps_appOpAddedToForegroundNotif() { 137 // GIVEN a notification associated with a foreground service 138 NotificationEntry entry = addFgEntry(); 139 when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); 140 141 // WHEN we are notified of a new app op for this notification 142 mFsc.onAppOpChanged( 143 AppOpsManager.OP_CAMERA, 144 entry.getSbn().getUid(), 145 entry.getSbn().getPackageName(), 146 true); 147 148 // THEN the app op is added to the entry 149 Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); 150 151 // THEN notification views are updated since the notification is visible 152 verify(mEntryManager, times(1)).updateNotifications(anyString()); 153 } 154 155 @Test testAppOpsAlreadyAdded()156 public void testAppOpsAlreadyAdded() { 157 // GIVEN a foreground service associated notification that already has the correct app op 158 NotificationEntry entry = addFgEntry(); 159 entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA); 160 when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); 161 162 // WHEN we are notified of the same app op for this notification 163 mFsc.onAppOpChanged( 164 AppOpsManager.OP_CAMERA, 165 entry.getSbn().getUid(), 166 entry.getSbn().getPackageName(), 167 true); 168 169 // THEN the app op still exists in the notification entry 170 Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); 171 172 // THEN notification views aren't updated since nothing changed 173 verify(mEntryManager, never()).updateNotifications(anyString()); 174 } 175 176 @Test testAppOps_appOpNotAddedToUnrelatedNotif()177 public void testAppOps_appOpNotAddedToUnrelatedNotif() { 178 // GIVEN no notification entries correspond to the newly updated appOp 179 NotificationEntry entry = addFgEntry(); 180 when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null); 181 182 // WHEN a new app op is detected 183 mFsc.onAppOpChanged( 184 AppOpsManager.OP_CAMERA, 185 entry.getSbn().getUid(), 186 entry.getSbn().getPackageName(), 187 true); 188 189 // THEN we won't see appOps on the entry 190 Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); 191 192 // THEN notification views aren't updated since nothing changed 193 verify(mEntryManager, never()).updateNotifications(anyString()); 194 } 195 196 @Test testAppOpsCRUD()197 public void testAppOpsCRUD() { 198 // no crash on remove that doesn't exist 199 mFsc.onAppOpChanged(9, 1000, "pkg1", false); 200 assertNull(mFsc.getAppOps(0, "pkg1")); 201 202 // multiuser & multipackage 203 mFsc.onAppOpChanged(8, 50, "pkg1", true); 204 mFsc.onAppOpChanged(1, 60, "pkg3", true); 205 mFsc.onAppOpChanged(7, 500000, "pkg2", true); 206 207 assertEquals(1, mFsc.getAppOps(0, "pkg1").size()); 208 assertTrue(mFsc.getAppOps(0, "pkg1").contains(8)); 209 210 assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size()); 211 assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7)); 212 213 assertEquals(1, mFsc.getAppOps(0, "pkg3").size()); 214 assertTrue(mFsc.getAppOps(0, "pkg3").contains(1)); 215 216 // multiple ops for the same package 217 mFsc.onAppOpChanged(9, 50, "pkg1", true); 218 mFsc.onAppOpChanged(5, 50, "pkg1", true); 219 220 assertEquals(3, mFsc.getAppOps(0, "pkg1").size()); 221 assertTrue(mFsc.getAppOps(0, "pkg1").contains(8)); 222 assertTrue(mFsc.getAppOps(0, "pkg1").contains(9)); 223 assertTrue(mFsc.getAppOps(0, "pkg1").contains(5)); 224 225 assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size()); 226 assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7)); 227 228 // remove one of the multiples 229 mFsc.onAppOpChanged(9, 50, "pkg1", false); 230 assertEquals(2, mFsc.getAppOps(0, "pkg1").size()); 231 assertTrue(mFsc.getAppOps(0, "pkg1").contains(8)); 232 assertTrue(mFsc.getAppOps(0, "pkg1").contains(5)); 233 234 // remove last op 235 mFsc.onAppOpChanged(1, 60, "pkg3", false); 236 assertNull(mFsc.getAppOps(0, "pkg3")); 237 } 238 239 @Test testDisclosurePredicate()240 public void testDisclosurePredicate() { 241 StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1", 242 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); 243 StatusBarNotification sbn_user1_disclosure = makeMockSBN(USERID_ONE, "android", 244 SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES, 245 null, Notification.FLAG_NO_CLEAR); 246 247 assertTrue(mFsc.isDisclosureNotification(sbn_user1_disclosure)); 248 assertFalse(mFsc.isDisclosureNotification(sbn_user1_app1)); 249 } 250 251 @Test testNeedsDisclosureAfterRemovingUnrelatedNotification()252 public void testNeedsDisclosureAfterRemovingUnrelatedNotification() { 253 final String PKG1 = "com.example.app100"; 254 255 StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1, 256 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); 257 StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1); 258 259 // first add a normal notification 260 entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); 261 // nothing required yet 262 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); 263 // now the app starts a fg service 264 entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}), 265 NotificationManager.IMPORTANCE_DEFAULT); 266 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required! 267 // add the fg notification 268 entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); 269 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered 270 // remove the boring notification 271 entryRemoved(sbn_user1_app1); 272 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has STILL got it covered 273 entryRemoved(sbn_user1_app1_fg); 274 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required! 275 } 276 277 @Test testSimpleAddRemove()278 public void testSimpleAddRemove() { 279 final String PKG1 = "com.example.app1"; 280 final String PKG2 = "com.example.app2"; 281 282 StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1, 283 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); 284 entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); 285 286 // no services are "running" 287 entryAdded(makeMockDisclosure(USERID_ONE, null), 288 NotificationManager.IMPORTANCE_DEFAULT); 289 290 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); 291 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 292 293 entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG1}), 294 NotificationManager.IMPORTANCE_DEFAULT); 295 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required! 296 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 297 298 // switch to different package 299 entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2}), 300 NotificationManager.IMPORTANCE_DEFAULT); 301 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); 302 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 303 304 entryUpdated(makeMockDisclosure(USERID_TWO, new String[]{PKG1}), 305 NotificationManager.IMPORTANCE_DEFAULT); 306 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); 307 assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO)); // finally user2 needs one too 308 309 entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2, PKG1}), 310 NotificationManager.IMPORTANCE_DEFAULT); 311 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); 312 assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO)); 313 314 entryRemoved(makeMockDisclosure(USERID_ONE, null /*unused*/)); 315 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); 316 assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO)); 317 318 entryRemoved(makeMockDisclosure(USERID_TWO, null /*unused*/)); 319 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); 320 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 321 } 322 323 @Test testDisclosureBasic()324 public void testDisclosureBasic() { 325 final String PKG1 = "com.example.app0"; 326 327 StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1, 328 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); 329 StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1); 330 331 entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); // not fg 332 entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}), 333 NotificationManager.IMPORTANCE_DEFAULT); 334 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required! 335 entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); 336 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered 337 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 338 339 // let's take out the other notification and see what happens. 340 341 entryRemoved(sbn_user1_app1); 342 assertFalse( 343 mFsc.isDisclosureNeededForUser(USERID_ONE)); // still covered by sbn_user1_app1_fg 344 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 345 346 // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get 347 StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1); 348 sbn_user1_app1_fg_sneaky.getNotification().flags = 0; 349 entryUpdated(sbn_user1_app1_fg_sneaky, 350 NotificationManager.IMPORTANCE_DEFAULT); 351 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required! 352 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 353 354 // ok, ok, we'll put it back 355 sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; 356 entryUpdated(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); 357 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); 358 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 359 360 entryRemoved(sbn_user1_app1_fg_sneaky); 361 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required! 362 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 363 364 // now let's test an upgrade 365 entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); 366 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); 367 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 368 sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; 369 entryUpdated(sbn_user1_app1, 370 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification 371 372 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 373 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); 374 375 // remove it, make sure we're out of compliance again 376 entryRemoved(sbn_user1_app1); // was fg, should return true 377 entryRemoved(sbn_user1_app1); 378 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 379 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); 380 381 // importance upgrade 382 entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN); 383 assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); 384 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 385 sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; 386 entryUpdated(sbn_user1_app1_fg, 387 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification 388 389 // finally, let's turn off the service 390 entryAdded(makeMockDisclosure(USERID_ONE, null), 391 NotificationManager.IMPORTANCE_DEFAULT); 392 393 assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); 394 assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO)); 395 } 396 397 @Test testOverlayPredicate()398 public void testOverlayPredicate() { 399 StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1", 400 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); 401 StatusBarNotification sbn_user1_overlay = makeMockSBN(USERID_ONE, "android", 402 0, "AlertWindowNotification", Notification.FLAG_NO_CLEAR); 403 404 assertTrue(mFsc.isSystemAlertNotification(sbn_user1_overlay)); 405 assertFalse(mFsc.isSystemAlertNotification(sbn_user1_app1)); 406 } 407 408 @Test testNoNotifsNorAppOps_noSystemAlertWarningRequired()409 public void testNoNotifsNorAppOps_noSystemAlertWarningRequired() { 410 // no notifications nor app op signals that this package/userId requires system alert 411 // warning 412 assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, "any")); 413 } 414 415 @Test testCustomLayouts_systemAlertWarningRequired()416 public void testCustomLayouts_systemAlertWarningRequired() { 417 // GIVEN a notification with a custom layout 418 final String pkg = "com.example.app0"; 419 StatusBarNotification customLayoutNotif = makeMockSBN(USERID_ONE, pkg, 0, 420 false); 421 422 // WHEN the custom layout entry is added 423 entryAdded(customLayoutNotif, NotificationManager.IMPORTANCE_MIN); 424 425 // THEN a system alert warning is required since there aren't any notifications that can 426 // display the app ops 427 assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg)); 428 } 429 430 @Test testStandardLayoutExists_noSystemAlertWarningRequired()431 public void testStandardLayoutExists_noSystemAlertWarningRequired() { 432 // GIVEN two notifications (one with a custom layout, the other with a standard layout) 433 final String pkg = "com.example.app0"; 434 StatusBarNotification customLayoutNotif = makeMockSBN(USERID_ONE, pkg, 0, 435 false); 436 StatusBarNotification standardLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, true); 437 438 // WHEN the entries are added 439 entryAdded(customLayoutNotif, NotificationManager.IMPORTANCE_MIN); 440 entryAdded(standardLayoutNotif, NotificationManager.IMPORTANCE_MIN); 441 442 // THEN no system alert warning is required, since there is at least one notification 443 // with a standard layout that can display the app ops on the notification 444 assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg)); 445 } 446 447 @Test testStandardLayoutRemoved_systemAlertWarningRequired()448 public void testStandardLayoutRemoved_systemAlertWarningRequired() { 449 // GIVEN two notifications (one with a custom layout, the other with a standard layout) 450 final String pkg = "com.example.app0"; 451 StatusBarNotification customLayoutNotif = makeMockSBN(USERID_ONE, pkg, 0, 452 false); 453 StatusBarNotification standardLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, true); 454 455 // WHEN the entries are added and then the standard layout notification is removed 456 entryAdded(customLayoutNotif, NotificationManager.IMPORTANCE_MIN); 457 entryAdded(standardLayoutNotif, NotificationManager.IMPORTANCE_MIN); 458 entryRemoved(standardLayoutNotif); 459 460 // THEN a system alert warning is required since there aren't any notifications that can 461 // display the app ops 462 assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg)); 463 } 464 465 @Test testStandardLayoutUpdatedToCustomLayout_systemAlertWarningRequired()466 public void testStandardLayoutUpdatedToCustomLayout_systemAlertWarningRequired() { 467 // GIVEN a standard layout notification and then an updated version with a customLayout 468 final String pkg = "com.example.app0"; 469 StatusBarNotification standardLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, true); 470 StatusBarNotification updatedToCustomLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, false); 471 472 // WHEN the entries is added and then updated to a custom layout 473 entryAdded(standardLayoutNotif, NotificationManager.IMPORTANCE_MIN); 474 entryUpdated(updatedToCustomLayoutNotif, NotificationManager.IMPORTANCE_MIN); 475 476 // THEN a system alert warning is required since there aren't any notifications that can 477 // display the app ops 478 assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg)); 479 } 480 makeMockSBN(int userId, String pkg, int id, String tag, int flags)481 private StatusBarNotification makeMockSBN(int userId, String pkg, int id, String tag, 482 int flags) { 483 final Notification n = mock(Notification.class); 484 n.extras = new Bundle(); 485 n.flags = flags; 486 return makeMockSBN(userId, pkg, id, tag, n); 487 } 488 makeMockSBN(int userid, String pkg, int id, String tag, Notification n)489 private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag, 490 Notification n) { 491 final StatusBarNotification sbn = mock(StatusBarNotification.class); 492 when(sbn.getNotification()).thenReturn(n); 493 when(sbn.getId()).thenReturn(id); 494 when(sbn.getPackageName()).thenReturn(pkg); 495 when(sbn.getTag()).thenReturn(tag); 496 when(sbn.getUserId()).thenReturn(userid); 497 when(sbn.getUser()).thenReturn(new UserHandle(userid)); 498 when(sbn.getKey()).thenReturn("MOCK:"+userid+"|"+pkg+"|"+id+"|"+tag); 499 return sbn; 500 } 501 makeMockSBN(int uid, String pkg, int id, boolean usesStdLayout)502 private StatusBarNotification makeMockSBN(int uid, String pkg, int id, 503 boolean usesStdLayout) { 504 StatusBarNotification sbn = makeMockSBN(uid, pkg, id, "foo", 0); 505 if (usesStdLayout) { 506 sbn.getNotification().contentView = null; 507 sbn.getNotification().headsUpContentView = null; 508 sbn.getNotification().bigContentView = null; 509 } else { 510 sbn.getNotification().contentView = mock(RemoteViews.class); 511 } 512 return sbn; 513 } 514 makeMockFgSBN(int uid, String pkg, int id, boolean usesStdLayout)515 private StatusBarNotification makeMockFgSBN(int uid, String pkg, int id, 516 boolean usesStdLayout) { 517 StatusBarNotification sbn = 518 makeMockSBN(uid, pkg, id, "foo", Notification.FLAG_FOREGROUND_SERVICE); 519 if (usesStdLayout) { 520 sbn.getNotification().contentView = null; 521 sbn.getNotification().headsUpContentView = null; 522 sbn.getNotification().bigContentView = null; 523 } else { 524 sbn.getNotification().contentView = mock(RemoteViews.class); 525 } 526 return sbn; 527 } 528 makeMockFgSBN(int uid, String pkg)529 private StatusBarNotification makeMockFgSBN(int uid, String pkg) { 530 return makeMockSBN(uid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE); 531 } 532 makeMockDisclosure(int userid, String[] pkgs)533 private StatusBarNotification makeMockDisclosure(int userid, String[] pkgs) { 534 final Notification n = mock(Notification.class); 535 n.flags = Notification.FLAG_ONGOING_EVENT; 536 final Bundle extras = new Bundle(); 537 if (pkgs != null) extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, pkgs); 538 n.extras = extras; 539 n.when = System.currentTimeMillis() - 10000; // ten seconds ago 540 final StatusBarNotification sbn = makeMockSBN(userid, "android", 541 SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES, 542 null, n); 543 sbn.getNotification().extras = extras; 544 return sbn; 545 } 546 addFgEntry()547 private NotificationEntry addFgEntry() { 548 NotificationEntry entry = createFgEntry(); 549 mEntryListener.onPendingEntryAdded(entry); 550 return entry; 551 } 552 createFgEntry()553 private NotificationEntry createFgEntry() { 554 return new NotificationEntryBuilder() 555 .setSbn(makeMockFgSBN(0, TEST_PACKAGE_NAME, 1000, true)) 556 .setImportance(NotificationManager.IMPORTANCE_DEFAULT) 557 .build(); 558 } 559 entryRemoved(StatusBarNotification notification)560 private void entryRemoved(StatusBarNotification notification) { 561 mEntryListener.onEntryRemoved( 562 new NotificationEntryBuilder() 563 .setSbn(notification) 564 .build(), 565 null, 566 false, 567 REASON_APP_CANCEL); 568 } 569 entryAdded(StatusBarNotification notification, int importance)570 private void entryAdded(StatusBarNotification notification, int importance) { 571 NotificationEntry entry = new NotificationEntryBuilder() 572 .setSbn(notification) 573 .setImportance(importance) 574 .build(); 575 mEntryListener.onPendingEntryAdded(entry); 576 } 577 entryUpdated(StatusBarNotification notification, int importance)578 private void entryUpdated(StatusBarNotification notification, int importance) { 579 NotificationEntry entry = new NotificationEntryBuilder() 580 .setSbn(notification) 581 .setImportance(importance) 582 .build(); 583 mEntryListener.onPreEntryUpdated(entry); 584 } 585 586 @UserIdInt private static final int USERID_ONE = 10; // UserManagerService.MIN_USER_ID; 587 @UserIdInt private static final int USERID_TWO = USERID_ONE + 1; 588 private static final String TEST_PACKAGE_NAME = "test"; 589 } 590