1 /* <lambda>null2 * Copyright (C) 2023 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.interruption 18 19 import android.app.ActivityManager 20 import android.app.Notification 21 import android.app.Notification.BubbleMetadata 22 import android.app.Notification.EXTRA_COLORIZED 23 import android.app.Notification.EXTRA_TEMPLATE 24 import android.app.Notification.FLAG_BUBBLE 25 import android.app.Notification.FLAG_CAN_COLORIZE 26 import android.app.Notification.FLAG_FOREGROUND_SERVICE 27 import android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED 28 import android.app.Notification.FLAG_USER_INITIATED_JOB 29 import android.app.Notification.GROUP_ALERT_ALL 30 import android.app.Notification.GROUP_ALERT_CHILDREN 31 import android.app.Notification.GROUP_ALERT_SUMMARY 32 import android.app.Notification.VISIBILITY_PRIVATE 33 import android.app.NotificationChannel 34 import android.app.NotificationManager.IMPORTANCE_DEFAULT 35 import android.app.NotificationManager.IMPORTANCE_HIGH 36 import android.app.NotificationManager.IMPORTANCE_LOW 37 import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT 38 import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT 39 import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK 40 import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE 41 import android.app.PendingIntent 42 import android.app.PendingIntent.FLAG_MUTABLE 43 import android.content.Context 44 import android.content.Intent 45 import android.content.pm.PackageManager 46 import android.content.pm.UserInfo 47 import android.graphics.drawable.Icon 48 import android.hardware.display.FakeAmbientDisplayConfiguration 49 import android.os.Looper 50 import android.os.PowerManager 51 import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED 52 import android.provider.Settings.Global.HEADS_UP_OFF 53 import android.provider.Settings.Global.HEADS_UP_ON 54 import com.android.internal.logging.UiEventLogger.UiEventEnum 55 import com.android.internal.logging.testing.UiEventLoggerFake 56 import com.android.systemui.SysuiTestCase 57 import com.android.systemui.log.LogBuffer 58 import com.android.systemui.log.LogcatEchoTracker 59 import com.android.systemui.log.core.LogLevel 60 import com.android.systemui.res.R 61 import com.android.systemui.settings.FakeUserTracker 62 import com.android.systemui.statusbar.FakeStatusBarStateController 63 import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking 64 import com.android.systemui.statusbar.StatusBarState.KEYGUARD 65 import com.android.systemui.statusbar.StatusBarState.SHADE 66 import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED 67 import com.android.systemui.statusbar.notification.NotifPipelineFlags 68 import com.android.systemui.statusbar.notification.collection.NotificationEntry 69 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder 70 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS 71 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD 72 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA 73 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR 74 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN 75 import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController 76 import com.android.systemui.statusbar.policy.HeadsUpManager 77 import com.android.systemui.util.FakeEventLog 78 import com.android.systemui.util.settings.FakeGlobalSettings 79 import com.android.systemui.util.settings.FakeSettings 80 import com.android.systemui.util.settings.SystemSettings 81 import com.android.systemui.util.time.FakeSystemClock 82 import com.android.systemui.utils.leaks.FakeBatteryController 83 import com.android.systemui.utils.leaks.FakeKeyguardStateController 84 import com.android.systemui.utils.leaks.LeakCheckedTest 85 import com.android.systemui.utils.os.FakeHandler 86 import com.android.wm.shell.bubbles.Bubbles 87 import junit.framework.Assert.assertFalse 88 import junit.framework.Assert.assertTrue 89 import org.junit.Assert.assertEquals 90 import org.junit.Before 91 import org.junit.Test 92 import org.mockito.kotlin.any 93 import org.mockito.kotlin.mock 94 import org.mockito.kotlin.whenever 95 96 abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { 97 private val fakeLogBuffer = 98 LogBuffer( 99 name = "FakeLog", 100 maxSize = 1, 101 logcatEchoTracker = 102 object : LogcatEchoTracker { 103 override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = 104 true 105 106 override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true 107 }, 108 systrace = false 109 ) 110 111 private val leakCheck = LeakCheckedTest.SysuiLeakCheck() 112 113 protected val ambientDisplayConfiguration = FakeAmbientDisplayConfiguration(context) 114 protected val batteryController = FakeBatteryController(leakCheck) 115 protected val deviceProvisionedController = FakeDeviceProvisionedController() 116 protected val eventLog = FakeEventLog() 117 protected val flags: NotifPipelineFlags = mock() 118 protected val globalSettings = 119 FakeGlobalSettings().also { it.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON) } 120 protected val headsUpManager: HeadsUpManager = mock() 121 protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider = 122 mock() 123 protected val keyguardStateController = FakeKeyguardStateController(leakCheck) 124 protected val mainHandler = FakeHandler(Looper.getMainLooper()) 125 protected val newLogger = VisualInterruptionDecisionLogger(fakeLogBuffer) 126 protected val oldLogger = NotificationInterruptLogger(fakeLogBuffer) 127 protected val powerManager: PowerManager = mock() 128 protected val statusBarStateController = FakeStatusBarStateController() 129 protected val systemClock = FakeSystemClock() 130 protected val uiEventLogger = UiEventLoggerFake() 131 protected val userTracker = FakeUserTracker() 132 protected val avalancheProvider: AvalancheProvider = mock() 133 protected val bubbles: Bubbles = mock() 134 lateinit var systemSettings: SystemSettings 135 protected val packageManager: PackageManager = mock() 136 137 protected abstract val provider: VisualInterruptionDecisionProvider 138 139 private val neverSuppresses = object : NotificationInterruptSuppressor {} 140 141 private val alwaysSuppressesInterruptions = 142 object : NotificationInterruptSuppressor { 143 override fun suppressInterruptions(entry: NotificationEntry?) = true 144 } 145 146 private val alwaysSuppressesAwakeInterruptions = 147 object : NotificationInterruptSuppressor { 148 override fun suppressAwakeInterruptions(entry: NotificationEntry?) = true 149 } 150 151 private val alwaysSuppressesAwakeHeadsUp = 152 object : NotificationInterruptSuppressor { 153 override fun suppressAwakeHeadsUp(entry: NotificationEntry?) = true 154 } 155 156 @Before 157 fun setUp() { 158 val userId = ActivityManager.getCurrentUser() 159 val user = UserInfo(userId, "Current user", /* flags = */ 0) 160 161 deviceProvisionedController.currentUser = userId 162 userTracker.set(listOf(user), /* currentUserIndex = */ 0) 163 systemSettings = FakeSettings() 164 whenever(bubbles.canShowBubbleNotification()).thenReturn(true) 165 166 provider.start() 167 } 168 169 @Test 170 fun testShouldPeek() { 171 ensurePeekState() 172 assertShouldHeadsUp(buildPeekEntry()) 173 assertNoEventsLogged() 174 } 175 176 @Test 177 fun testShouldNotPeek_settingDisabled() { 178 ensurePeekState { hunSettingEnabled = false } 179 assertShouldNotHeadsUp(buildPeekEntry()) 180 assertNoEventsLogged() 181 } 182 183 @Test 184 fun testShouldNotPeek_packageSnoozed_withoutFsi() { 185 ensurePeekState { hunSnoozed = true } 186 assertShouldNotHeadsUp(buildPeekEntry()) 187 assertNoEventsLogged() 188 } 189 190 @Test 191 fun testShouldPeek_packageSnoozed_withFsi() { 192 val entry = buildFsiEntry() 193 forEachPeekableFsiState { 194 ensurePeekState { hunSnoozed = true } 195 assertShouldHeadsUp(entry) 196 197 // The old code logs a UiEvent when a HUN snooze is bypassed because the notification 198 // has an FSI, but that doesn't fit into the new code's suppressor-based logic, so we're 199 // not reimplementing it. 200 if (provider !is NotificationInterruptStateProviderWrapper) { 201 assertNoEventsLogged() 202 } 203 } 204 } 205 206 @Test 207 fun testShouldNotPeek_alreadyBubbled() { 208 ensurePeekState { statusBarState = SHADE } 209 assertShouldNotHeadsUp(buildPeekEntry { isBubble = true }) 210 assertNoEventsLogged() 211 } 212 213 @Test 214 fun testShouldPeek_bubblesCannotShowNotification() { 215 whenever(bubbles.canShowBubbleNotification()).thenReturn(false) 216 ensurePeekState { statusBarState = SHADE } 217 assertShouldHeadsUp(buildPeekEntry { isBubble = true }) 218 assertNoEventsLogged() 219 } 220 221 @Test 222 fun testShouldPeek_isBubble_shadeLocked() { 223 ensurePeekState { statusBarState = SHADE_LOCKED } 224 assertShouldHeadsUp(buildPeekEntry { isBubble = true }) 225 assertNoEventsLogged() 226 } 227 228 @Test 229 fun testShouldPeek_isBubble_keyguard() { 230 ensurePeekState { statusBarState = KEYGUARD } 231 assertShouldHeadsUp(buildPeekEntry { isBubble = true }) 232 assertNoEventsLogged() 233 } 234 235 @Test 236 fun testShouldNotPeek_dnd() { 237 ensurePeekState() 238 assertShouldNotHeadsUp(buildPeekEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK }) 239 assertNoEventsLogged() 240 } 241 242 @Test 243 fun testShouldNotPeek_notImportant() { 244 ensurePeekState() 245 assertShouldNotHeadsUp(buildPeekEntry { importance = IMPORTANCE_DEFAULT }) 246 assertNoEventsLogged() 247 } 248 249 @Test 250 fun testShouldNotPeek_screenOff() { 251 ensurePeekState { isScreenOn = false } 252 assertShouldNotHeadsUp(buildPeekEntry()) 253 assertNoEventsLogged() 254 } 255 256 @Test 257 fun testShouldNotPeek_dreaming() { 258 ensurePeekState { isDreaming = true } 259 assertShouldNotHeadsUp(buildPeekEntry()) 260 assertNoEventsLogged() 261 } 262 263 @Test 264 fun testShouldNotPeek_oldWhen() { 265 ensurePeekState() 266 assertShouldNotHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) }) 267 } 268 269 @Test 270 fun testLogsHunOldWhen() { 271 assertNoEventsLogged() 272 273 ensurePeekState() 274 val entry = buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) } 275 276 // The old code logs the "old when" UiEvent unconditionally, so don't expect that it hasn't. 277 if (provider !is NotificationInterruptStateProviderWrapper) { 278 provider.makeUnloggedHeadsUpDecision(entry) 279 assertNoEventsLogged() 280 } 281 282 provider.makeAndLogHeadsUpDecision(entry) 283 assertUiEventLogged(HUN_SUPPRESSED_OLD_WHEN, entry.sbn.uid, entry.sbn.packageName) 284 assertNoSystemEventLogged() 285 } 286 287 @Test 288 fun testShouldPeek_oldWhen_now() { 289 ensurePeekState() 290 assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(0) }) 291 assertNoEventsLogged() 292 } 293 294 @Test 295 fun testShouldPeek_oldWhen_notOldEnough() { 296 ensurePeekState() 297 assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS - 1) }) 298 assertNoEventsLogged() 299 } 300 301 @Test 302 fun testShouldPeek_oldWhen_zeroWhen() { 303 ensurePeekState() 304 assertShouldHeadsUp(buildPeekEntry { whenMs = 0L }) 305 assertNoEventsLogged() 306 } 307 308 @Test 309 fun testShouldPeek_oldWhen_negativeWhen() { 310 ensurePeekState() 311 assertShouldHeadsUp(buildPeekEntry { whenMs = -1L }) 312 assertNoEventsLogged() 313 } 314 315 @Test 316 fun testShouldPeek_oldWhen_fullScreenIntent() { 317 ensurePeekState() 318 assertShouldHeadsUp(buildFsiEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) }) 319 assertNoEventsLogged() 320 } 321 322 @Test 323 fun testShouldPeek_oldWhen_foregroundService() { 324 ensurePeekState() 325 assertShouldHeadsUp( 326 buildPeekEntry { 327 whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) 328 isForegroundService = true 329 } 330 ) 331 assertNoEventsLogged() 332 } 333 334 @Test 335 fun testShouldPeek_oldWhen_userInitiatedJob() { 336 ensurePeekState() 337 assertShouldHeadsUp( 338 buildPeekEntry { 339 whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) 340 isUserInitiatedJob = true 341 } 342 ) 343 assertNoEventsLogged() 344 } 345 346 @Test 347 fun testShouldNotPeek_appSuspended() { 348 ensurePeekState() 349 assertShouldNotBubble(buildPeekEntry { packageSuspended = true }) 350 assertNoEventsLogged() 351 } 352 353 @Test 354 fun testShouldNotPeek_hiddenOnKeyguard() { 355 ensurePeekState({ keyguardShouldHideNotification = true }) 356 assertShouldNotHeadsUp(buildPeekEntry()) 357 assertNoEventsLogged() 358 } 359 360 @Test 361 fun testShouldPeek_defaultLegacySuppressor() { 362 ensurePeekState() 363 withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPeekEntry()) } 364 assertNoEventsLogged() 365 } 366 367 @Test 368 fun testShouldNotPeek_legacySuppressInterruptions() { 369 ensurePeekState() 370 withLegacySuppressor(alwaysSuppressesInterruptions) { 371 assertShouldNotHeadsUp(buildPeekEntry()) 372 } 373 assertNoEventsLogged() 374 } 375 376 @Test 377 fun testShouldNotPeek_legacySuppressAwakeInterruptions() { 378 ensurePeekState() 379 withLegacySuppressor(alwaysSuppressesAwakeInterruptions) { 380 assertShouldNotHeadsUp(buildPeekEntry()) 381 } 382 assertNoEventsLogged() 383 } 384 385 @Test 386 fun testShouldNotPeek_legacySuppressAwakeHeadsUp() { 387 ensurePeekState() 388 withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) { 389 assertShouldNotHeadsUp(buildPeekEntry()) 390 } 391 assertNoEventsLogged() 392 } 393 394 @Test 395 fun testShouldPulse() { 396 ensurePulseState() 397 assertShouldHeadsUp(buildPulseEntry()) 398 assertNoEventsLogged() 399 } 400 401 @Test 402 fun testShouldNotPulse_disabled() { 403 ensurePulseState { pulseOnNotificationsEnabled = false } 404 assertShouldNotHeadsUp(buildPulseEntry()) 405 assertNoEventsLogged() 406 } 407 408 @Test 409 fun testShouldNotPulse_batterySaver() { 410 ensurePulseState { isAodPowerSave = true } 411 assertShouldNotHeadsUp(buildPulseEntry()) 412 assertNoEventsLogged() 413 } 414 415 @Test 416 fun testShouldNotPulse_effectSuppressed() { 417 ensurePulseState() 418 assertShouldNotHeadsUp( 419 buildPulseEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_AMBIENT } 420 ) 421 assertNoEventsLogged() 422 } 423 424 @Test 425 fun testShouldNotPulse_visibilityOverridePrivate() { 426 ensurePulseState() 427 assertShouldNotHeadsUp(buildPulseEntry { visibilityOverride = VISIBILITY_PRIVATE }) 428 assertNoEventsLogged() 429 } 430 431 @Test 432 fun testShouldNotPulse_importanceLow() { 433 ensurePulseState() 434 assertShouldNotHeadsUp(buildPulseEntry { importance = IMPORTANCE_LOW }) 435 assertNoEventsLogged() 436 } 437 438 @Test 439 fun testShouldNotPulse_appSuspended() { 440 ensurePulseState() 441 assertShouldNotHeadsUp(buildPulseEntry { packageSuspended = true }) 442 assertNoEventsLogged() 443 } 444 445 @Test 446 fun testShouldNotPulse_hiddenOnKeyguard() { 447 ensurePulseState({ keyguardShouldHideNotification = true }) 448 assertShouldNotHeadsUp(buildPulseEntry()) 449 assertNoEventsLogged() 450 } 451 452 @Test 453 fun testShouldPulse_defaultLegacySuppressor() { 454 ensurePulseState() 455 withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPulseEntry()) } 456 assertNoEventsLogged() 457 } 458 459 @Test 460 fun testShouldNotPulse_legacySuppressInterruptions() { 461 ensurePulseState() 462 withLegacySuppressor(alwaysSuppressesInterruptions) { 463 assertShouldNotHeadsUp(buildPulseEntry()) 464 } 465 assertNoEventsLogged() 466 } 467 468 @Test 469 fun testShouldPulse_legacySuppressAwakeInterruptions() { 470 ensurePulseState() 471 withLegacySuppressor(alwaysSuppressesAwakeInterruptions) { 472 assertShouldHeadsUp(buildPulseEntry()) 473 } 474 assertNoEventsLogged() 475 } 476 477 @Test 478 fun testShouldPulse_legacySuppressAwakeHeadsUp() { 479 ensurePulseState() 480 withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) { 481 assertShouldHeadsUp(buildPulseEntry()) 482 } 483 assertNoEventsLogged() 484 } 485 486 private fun withPeekAndPulseEntry( 487 extendEntry: EntryBuilder.() -> Unit, 488 block: (NotificationEntry) -> Unit 489 ) { 490 ensurePeekState() 491 block(buildPeekEntry(extendEntry)) 492 493 ensurePulseState() 494 block(buildPulseEntry(extendEntry)) 495 } 496 497 @Test 498 fun testShouldNotHeadsUp_suppressiveGroupAlertBehavior() { 499 withPeekAndPulseEntry({ 500 isGrouped = true 501 isGroupSummary = false 502 groupAlertBehavior = GROUP_ALERT_SUMMARY 503 }) { 504 assertShouldNotHeadsUp(it) 505 assertNoEventsLogged() 506 } 507 } 508 509 @Test 510 fun testShouldHeadsUp_suppressiveGroupAlertBehavior_notSuppressive() { 511 withPeekAndPulseEntry({ 512 isGrouped = true 513 isGroupSummary = false 514 groupAlertBehavior = GROUP_ALERT_CHILDREN 515 }) { 516 assertShouldHeadsUp(it) 517 assertNoEventsLogged() 518 } 519 } 520 521 @Test 522 fun testShouldHeadsUp_suppressiveGroupAlertBehavior_notGrouped() { 523 withPeekAndPulseEntry({ 524 isGrouped = false 525 isGroupSummary = false 526 groupAlertBehavior = GROUP_ALERT_SUMMARY 527 }) { 528 assertShouldHeadsUp(it) 529 assertNoEventsLogged() 530 } 531 } 532 533 @Test 534 fun testShouldNotHeadsUp_justLaunchedFsi() { 535 withPeekAndPulseEntry({ hasJustLaunchedFsi = true }) { 536 assertShouldNotHeadsUp(it) 537 assertNoEventsLogged() 538 } 539 } 540 541 @Test 542 fun testShouldBubble_withIntentAndIcon() { 543 ensureBubbleState() 544 assertShouldBubble(buildBubbleEntry { bubbleIsShortcut = false }) 545 assertNoEventsLogged() 546 } 547 548 @Test 549 fun testShouldBubble_withShortcut() { 550 ensureBubbleState() 551 assertShouldBubble(buildBubbleEntry { bubbleIsShortcut = true }) 552 assertNoEventsLogged() 553 } 554 555 @Test 556 fun testShouldBubble_suppressiveGroupAlertBehavior() { 557 ensureBubbleState() 558 assertShouldBubble( 559 buildBubbleEntry { 560 isGrouped = true 561 isGroupSummary = false 562 groupAlertBehavior = GROUP_ALERT_SUMMARY 563 } 564 ) 565 assertNoEventsLogged() 566 } 567 568 @Test 569 fun testShouldNotBubble_notABubble() { 570 ensureBubbleState() 571 assertShouldNotBubble( 572 buildBubbleEntry { 573 isBubble = false 574 hasBubbleMetadata = false 575 } 576 ) 577 assertNoEventsLogged() 578 } 579 580 @Test 581 fun testShouldNotBubble_missingBubbleMetadata() { 582 ensureBubbleState() 583 assertShouldNotBubble(buildBubbleEntry { hasBubbleMetadata = false }) 584 assertNoEventsLogged() 585 } 586 587 @Test 588 fun testShouldNotBubble_notAllowedToBubble() { 589 ensureBubbleState() 590 assertShouldNotBubble(buildBubbleEntry { canBubble = false }) 591 assertNoEventsLogged() 592 } 593 594 @Test 595 fun testShouldBubble_defaultLegacySuppressor() { 596 ensureBubbleState() 597 withLegacySuppressor(neverSuppresses) { assertShouldBubble(buildBubbleEntry()) } 598 assertNoEventsLogged() 599 } 600 601 @Test 602 fun testShouldNotBubble_legacySuppressInterruptions() { 603 ensureBubbleState() 604 withLegacySuppressor(alwaysSuppressesInterruptions) { 605 assertShouldNotBubble(buildBubbleEntry()) 606 } 607 assertNoEventsLogged() 608 } 609 610 @Test 611 fun testShouldNotBubble_legacySuppressAwakeInterruptions() { 612 ensureBubbleState() 613 withLegacySuppressor(alwaysSuppressesAwakeInterruptions) { 614 assertShouldNotBubble(buildBubbleEntry()) 615 } 616 assertNoEventsLogged() 617 } 618 619 @Test 620 fun testShouldBubble_legacySuppressAwakeHeadsUp() { 621 ensureBubbleState() 622 withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) { 623 assertShouldBubble(buildBubbleEntry()) 624 } 625 assertNoEventsLogged() 626 } 627 628 @Test 629 fun testShouldNotBubble_appSuspended() { 630 ensureBubbleState() 631 assertShouldNotBubble(buildBubbleEntry { packageSuspended = true }) 632 assertNoEventsLogged() 633 } 634 635 @Test 636 fun testShouldNotBubble_hiddenOnKeyguard() { 637 ensureBubbleState({ keyguardShouldHideNotification = true }) 638 assertShouldNotBubble(buildBubbleEntry()) 639 assertNoEventsLogged() 640 } 641 642 @Test 643 fun testShouldNotFsi_noFullScreenIntent() { 644 forEachFsiState { 645 assertShouldNotFsi(buildFsiEntry { hasFsi = false }) 646 assertNoEventsLogged() 647 } 648 } 649 650 @Test 651 fun testShouldNotFsi_showStickyHun() { 652 forEachFsiState { 653 assertShouldNotFsi( 654 buildFsiEntry { 655 hasFsi = false 656 isStickyAndNotDemoted = true 657 } 658 ) 659 assertNoEventsLogged() 660 } 661 } 662 663 @Test 664 fun testShouldNotFsi_onlyDnd() { 665 forEachFsiState { 666 assertShouldNotFsi( 667 buildFsiEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT }, 668 expectWouldInterruptWithoutDnd = true 669 ) 670 assertNoEventsLogged() 671 } 672 } 673 674 @Test 675 fun testShouldNotFsi_notImportantEnough() { 676 forEachFsiState { 677 assertShouldNotFsi(buildFsiEntry { importance = IMPORTANCE_DEFAULT }) 678 assertNoEventsLogged() 679 } 680 } 681 682 @Test 683 fun testShouldNotFsi_notOnlyDnd() { 684 forEachFsiState { 685 assertShouldNotFsi( 686 buildFsiEntry { 687 suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT 688 importance = IMPORTANCE_DEFAULT 689 }, 690 expectWouldInterruptWithoutDnd = false 691 ) 692 assertNoEventsLogged() 693 } 694 } 695 696 @Test 697 fun testShouldNotFsi_suppressiveGroupAlertBehavior() { 698 forEachFsiState { 699 assertShouldNotFsi( 700 buildFsiEntry { 701 isGrouped = true 702 isGroupSummary = true 703 groupAlertBehavior = GROUP_ALERT_CHILDREN 704 } 705 ) 706 } 707 } 708 709 @Test 710 fun testLogsFsiSuppressiveGroupAlertBehavior() { 711 ensureNotInteractiveFsiState() 712 val entry = buildFsiEntry { 713 isGrouped = true 714 isGroupSummary = true 715 groupAlertBehavior = GROUP_ALERT_CHILDREN 716 } 717 718 val decision = provider.makeUnloggedFullScreenIntentDecision(entry) 719 assertNoEventsLogged() 720 721 provider.logFullScreenIntentDecision(decision) 722 assertUiEventLogged( 723 FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, 724 entry.sbn.uid, 725 entry.sbn.packageName 726 ) 727 assertSystemEventLogged("231322873", entry.sbn.uid, "groupAlertBehavior") 728 } 729 730 @Test 731 fun testShouldFsi_suppressiveGroupAlertBehavior_notGrouped() { 732 forEachFsiState { 733 assertShouldFsi( 734 buildFsiEntry { 735 isGrouped = false 736 isGroupSummary = true 737 groupAlertBehavior = GROUP_ALERT_CHILDREN 738 } 739 ) 740 assertNoEventsLogged() 741 } 742 } 743 744 @Test 745 fun testShouldFsi_suppressiveGroupAlertBehavior_notSuppressive() { 746 forEachFsiState { 747 assertShouldFsi( 748 buildFsiEntry { 749 isGrouped = true 750 isGroupSummary = true 751 groupAlertBehavior = GROUP_ALERT_ALL 752 } 753 ) 754 } 755 } 756 757 @Test 758 fun testShouldNotFsi_suppressiveBubbleMetadata() { 759 forEachFsiState { 760 assertShouldNotFsi( 761 buildFsiEntry { 762 hasBubbleMetadata = true 763 bubbleSuppressesNotification = true 764 } 765 ) 766 } 767 } 768 769 @Test 770 fun testLogsFsiSuppressiveBubbleMetadata() { 771 ensureNotInteractiveFsiState() 772 val entry = buildFsiEntry { 773 hasBubbleMetadata = true 774 bubbleSuppressesNotification = true 775 } 776 777 val decision = provider.makeUnloggedFullScreenIntentDecision(entry) 778 assertNoEventsLogged() 779 780 provider.logFullScreenIntentDecision(decision) 781 assertUiEventLogged( 782 FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA, 783 entry.sbn.uid, 784 entry.sbn.packageName 785 ) 786 assertSystemEventLogged("274759612", entry.sbn.uid, "bubbleMetadata") 787 } 788 789 @Test 790 fun testShouldNotFsi_packageSuspended() { 791 forEachFsiState { 792 assertShouldNotFsi(buildFsiEntry { packageSuspended = true }) 793 assertNoEventsLogged() 794 } 795 } 796 797 @Test 798 fun testShouldFsi_notInteractive() { 799 ensureNotInteractiveFsiState() 800 assertShouldFsi(buildFsiEntry()) 801 assertNoEventsLogged() 802 } 803 804 @Test 805 fun testShouldFsi_dreaming() { 806 ensureDreamingFsiState() 807 assertShouldFsi(buildFsiEntry()) 808 assertNoEventsLogged() 809 } 810 811 @Test 812 fun testShouldFsi_keyguard() { 813 ensureKeyguardFsiState() 814 assertShouldFsi(buildFsiEntry()) 815 assertNoEventsLogged() 816 } 817 818 @Test 819 fun testShouldNotFsi_expectedToHun() { 820 forEachPeekableFsiState { 821 ensurePeekState() 822 assertShouldNotFsi(buildFsiEntry()) 823 assertNoEventsLogged() 824 } 825 } 826 827 @Test 828 fun testShouldNotFsi_expectedToHun_hunSnoozed() { 829 forEachPeekableFsiState { 830 ensurePeekState { hunSnoozed = true } 831 assertShouldNotFsi(buildFsiEntry()) 832 assertNoEventsLogged() 833 } 834 } 835 836 @Test 837 fun testShouldFsi_lockedShade() { 838 ensureLockedShadeFsiState() 839 assertShouldFsi(buildFsiEntry()) 840 assertNoEventsLogged() 841 } 842 843 @Test 844 fun testShouldFsi_keyguardOccluded() { 845 ensureKeyguardOccludedFsiState() 846 assertShouldFsi(buildFsiEntry()) 847 assertNoEventsLogged() 848 } 849 850 @Test 851 fun testShouldFsi_deviceNotProvisioned() { 852 ensureDeviceNotProvisionedFsiState() 853 assertShouldFsi(buildFsiEntry()) 854 assertNoEventsLogged() 855 } 856 857 @Test 858 fun testShouldFsi_userSetupIncomplete() { 859 ensureUserSetupIncompleteFsiState() 860 assertShouldFsi(buildFsiEntry()) 861 assertNoEventsLogged() 862 } 863 864 @Test 865 fun testShouldNotFsi_noHunOrKeyguard() { 866 ensureNoHunOrKeyguardFsiState() 867 assertShouldNotFsi(buildFsiEntry()) 868 } 869 870 @Test 871 fun testLogsFsiNoHunOrKeyguard() { 872 ensureNoHunOrKeyguardFsiState() 873 val entry = buildFsiEntry() 874 875 val decision = provider.makeUnloggedFullScreenIntentDecision(entry) 876 assertNoEventsLogged() 877 878 provider.logFullScreenIntentDecision(decision) 879 assertUiEventLogged(FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD, entry.sbn.uid, entry.sbn.packageName) 880 assertSystemEventLogged("231322873", entry.sbn.uid, "no hun or keyguard") 881 } 882 883 @Test 884 fun testShouldFsi_defaultLegacySuppressor() { 885 forEachFsiState { 886 withLegacySuppressor(neverSuppresses) { assertShouldFsi(buildFsiEntry()) } 887 assertNoEventsLogged() 888 } 889 } 890 891 @Test 892 fun testShouldFsi_suppressInterruptions() { 893 forEachFsiState { 894 withLegacySuppressor(alwaysSuppressesInterruptions) { assertShouldFsi(buildFsiEntry()) } 895 assertNoEventsLogged() 896 } 897 } 898 899 @Test 900 fun testShouldFsi_suppressAwakeInterruptions() { 901 forEachFsiState { 902 withLegacySuppressor(alwaysSuppressesAwakeInterruptions) { 903 assertShouldFsi(buildFsiEntry()) 904 } 905 assertNoEventsLogged() 906 } 907 } 908 909 @Test 910 fun testShouldFsi_suppressAwakeHeadsUp() { 911 forEachFsiState { 912 withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) { assertShouldFsi(buildFsiEntry()) } 913 assertNoEventsLogged() 914 } 915 } 916 917 protected data class State( 918 var hunSettingEnabled: Boolean? = null, 919 var hunSnoozed: Boolean? = null, 920 var isAodPowerSave: Boolean? = null, 921 var isDozing: Boolean? = null, 922 var isDreaming: Boolean? = null, 923 var isInteractive: Boolean? = null, 924 var isScreenOn: Boolean? = null, 925 var keyguardShouldHideNotification: Boolean? = null, 926 var pulseOnNotificationsEnabled: Boolean? = null, 927 var statusBarState: Int? = null, 928 var keyguardIsShowing: Boolean = false, 929 var keyguardIsOccluded: Boolean = false, 930 var deviceProvisioned: Boolean = true, 931 var currentUserSetup: Boolean = true 932 ) 933 934 protected fun setState(state: State): Unit = 935 state.run { 936 hunSettingEnabled?.let { 937 val newSetting = if (it) HEADS_UP_ON else HEADS_UP_OFF 938 globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, newSetting) 939 } 940 941 hunSnoozed?.let { whenever(headsUpManager.isSnoozed(TEST_PACKAGE)).thenReturn(it) } 942 943 isAodPowerSave?.let { batteryController.setIsAodPowerSave(it) } 944 945 isDozing?.let { statusBarStateController.dozing = it } 946 947 isDreaming?.let { statusBarStateController.dreaming = it } 948 949 isInteractive?.let { whenever(powerManager.isInteractive).thenReturn(it) } 950 951 isScreenOn?.let { whenever(powerManager.isScreenOn).thenReturn(it) } 952 953 keyguardShouldHideNotification?.let { 954 whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any())) 955 .thenReturn(it) 956 } 957 958 pulseOnNotificationsEnabled?.let { 959 ambientDisplayConfiguration.fakePulseOnNotificationEnabled = it 960 } 961 962 statusBarState?.let { statusBarStateController.state = it } 963 964 keyguardStateController.isOccluded = keyguardIsOccluded 965 keyguardStateController.isShowing = keyguardIsShowing 966 967 deviceProvisionedController.deviceProvisioned = deviceProvisioned 968 deviceProvisionedController.isCurrentUserSetup = currentUserSetup 969 } 970 971 protected fun ensureState(block: State.() -> Unit) = 972 State() 973 .apply { 974 keyguardShouldHideNotification = false 975 apply(block) 976 } 977 .run(this::setState) 978 979 protected fun ensurePeekState(block: State.() -> Unit = {}) = ensureState { 980 hunSettingEnabled = true 981 hunSnoozed = false 982 isDozing = false 983 isDreaming = false 984 isScreenOn = true 985 run(block) 986 } 987 988 protected fun ensurePulseState(block: State.() -> Unit = {}) = ensureState { 989 isAodPowerSave = false 990 isDozing = true 991 pulseOnNotificationsEnabled = true 992 run(block) 993 } 994 995 protected fun ensureBubbleState(block: State.() -> Unit = {}) = ensureState(block) 996 997 protected fun ensureNotInteractiveFsiState(block: State.() -> Unit = {}) = ensureState { 998 isInteractive = false 999 run(block) 1000 } 1001 1002 protected fun ensureDreamingFsiState(block: State.() -> Unit = {}) = ensureState { 1003 isInteractive = true 1004 isDreaming = true 1005 run(block) 1006 } 1007 1008 protected fun ensureKeyguardFsiState(block: State.() -> Unit = {}) = ensureState { 1009 isInteractive = true 1010 isDreaming = false 1011 statusBarState = KEYGUARD 1012 run(block) 1013 } 1014 1015 protected fun ensureLockedShadeFsiState(block: State.() -> Unit = {}) = ensureState { 1016 // It is assumed *but not checked in the code* that statusBarState is SHADE_LOCKED. 1017 isInteractive = true 1018 isDreaming = false 1019 statusBarState = SHADE 1020 hunSettingEnabled = false 1021 keyguardIsShowing = true 1022 keyguardIsOccluded = false 1023 run(block) 1024 } 1025 1026 protected fun ensureKeyguardOccludedFsiState(block: State.() -> Unit = {}) = ensureState { 1027 isInteractive = true 1028 isDreaming = false 1029 statusBarState = SHADE 1030 hunSettingEnabled = false 1031 keyguardIsShowing = true 1032 keyguardIsOccluded = true 1033 run(block) 1034 } 1035 1036 protected fun ensureDeviceNotProvisionedFsiState(block: State.() -> Unit = {}) = ensureState { 1037 isInteractive = true 1038 isDreaming = false 1039 statusBarState = SHADE 1040 hunSettingEnabled = false 1041 keyguardIsShowing = false 1042 deviceProvisioned = false 1043 currentUserSetup = true 1044 run(block) 1045 } 1046 1047 protected fun ensureUserSetupIncompleteFsiState(block: State.() -> Unit = {}) = ensureState { 1048 isInteractive = true 1049 isDreaming = false 1050 statusBarState = SHADE 1051 hunSettingEnabled = false 1052 keyguardIsShowing = false 1053 deviceProvisioned = true 1054 currentUserSetup = false 1055 run(block) 1056 } 1057 1058 protected fun ensureNoHunOrKeyguardFsiState(block: State.() -> Unit = {}) = ensureState { 1059 isInteractive = true 1060 isDreaming = false 1061 statusBarState = SHADE 1062 hunSettingEnabled = false 1063 keyguardIsShowing = false 1064 deviceProvisioned = true 1065 currentUserSetup = true 1066 run(block) 1067 } 1068 1069 protected fun forEachFsiState(block: () -> Unit) { 1070 ensureNotInteractiveFsiState() 1071 block() 1072 1073 ensureDreamingFsiState() 1074 block() 1075 1076 ensureKeyguardFsiState() 1077 block() 1078 1079 ensureLockedShadeFsiState() 1080 block() 1081 1082 ensureKeyguardOccludedFsiState() 1083 block() 1084 1085 ensureDeviceNotProvisionedFsiState() 1086 block() 1087 } 1088 1089 private fun forEachPeekableFsiState(extendState: State.() -> Unit = {}, block: () -> Unit) { 1090 ensureLockedShadeFsiState(extendState) 1091 block() 1092 1093 ensureKeyguardOccludedFsiState(extendState) 1094 block() 1095 1096 ensureDeviceNotProvisionedFsiState(extendState) 1097 block() 1098 } 1099 1100 protected fun withLegacySuppressor( 1101 suppressor: NotificationInterruptSuppressor, 1102 block: () -> Unit 1103 ) { 1104 provider.addLegacySuppressor(suppressor) 1105 block() 1106 provider.removeLegacySuppressor(suppressor) 1107 } 1108 1109 protected fun assertShouldHeadsUp(entry: NotificationEntry) = 1110 provider.makeAndLogHeadsUpDecision(entry).let { 1111 assertTrue("unexpected suppressed HUN: ${it.logReason}", it.shouldInterrupt) 1112 } 1113 1114 protected fun assertShouldNotHeadsUp(entry: NotificationEntry) = 1115 provider.makeAndLogHeadsUpDecision(entry).let { 1116 assertFalse("unexpected unsuppressed HUN: ${it.logReason}", it.shouldInterrupt) 1117 } 1118 1119 protected fun assertShouldBubble(entry: NotificationEntry) = 1120 provider.makeAndLogBubbleDecision(entry).let { 1121 assertTrue("unexpected suppressed bubble: ${it.logReason}", it.shouldInterrupt) 1122 } 1123 1124 protected fun assertShouldNotBubble(entry: NotificationEntry) = 1125 provider.makeAndLogBubbleDecision(entry).let { 1126 assertFalse("unexpected unsuppressed bubble: ${it.logReason}", it.shouldInterrupt) 1127 } 1128 1129 protected fun assertShouldFsi(entry: NotificationEntry) = 1130 provider.makeUnloggedFullScreenIntentDecision(entry).let { 1131 provider.logFullScreenIntentDecision(it) 1132 assertTrue("unexpected suppressed FSI: ${it.logReason}", it.shouldInterrupt) 1133 } 1134 1135 protected fun assertShouldNotFsi( 1136 entry: NotificationEntry, 1137 expectWouldInterruptWithoutDnd: Boolean? = null 1138 ) = 1139 provider.makeUnloggedFullScreenIntentDecision(entry).let { 1140 provider.logFullScreenIntentDecision(it) 1141 assertFalse("unexpected unsuppressed FSI: ${it.logReason}", it.shouldInterrupt) 1142 if (expectWouldInterruptWithoutDnd != null) { 1143 assertEquals( 1144 "unexpected wouldInterruptWithoutDnd for FSI: ${it.logReason}", 1145 expectWouldInterruptWithoutDnd, 1146 it.wouldInterruptWithoutDnd 1147 ) 1148 } 1149 } 1150 1151 protected class EntryBuilder(val context: Context) { 1152 // Set on BubbleMetadata: 1153 var bubbleIsShortcut = false 1154 var bubbleSuppressesNotification = false 1155 1156 // Set on Notification.Builder: 1157 var whenMs: Long? = null 1158 var isGrouped = false 1159 var isGroupSummary = false 1160 var isCall = false 1161 var category: String? = null 1162 var groupAlertBehavior: Int? = null 1163 var hasBubbleMetadata = false 1164 var hasFsi = false 1165 1166 // Set on Notification: 1167 var isForegroundService = false 1168 var isUserInitiatedJob = false 1169 var isBubble = false 1170 var isStickyAndNotDemoted = false 1171 var isColorized = false 1172 1173 // Set on NotificationEntryBuilder: 1174 var importance = IMPORTANCE_DEFAULT 1175 var canBubble: Boolean? = null 1176 var isImportantConversation = false 1177 1178 // Set on NotificationEntry: 1179 var hasJustLaunchedFsi = false 1180 1181 // Set on ModifiedRankingBuilder: 1182 var packageSuspended = false 1183 var visibilityOverride: Int? = null 1184 var suppressedVisualEffects: Int? = null 1185 var isConversation = false 1186 1187 private fun buildBubbleMetadata(): BubbleMetadata { 1188 val builder = 1189 if (bubbleIsShortcut) { 1190 BubbleMetadata.Builder(context.packageName + ":test_shortcut_id") 1191 } else { 1192 BubbleMetadata.Builder( 1193 PendingIntent.getActivity( 1194 context, 1195 /* requestCode = */ 0, 1196 Intent().setPackage(context.packageName), 1197 FLAG_MUTABLE 1198 ), 1199 Icon.createWithResource(context.resources, R.drawable.android) 1200 ) 1201 } 1202 1203 if (bubbleSuppressesNotification) { 1204 builder.setSuppressNotification(true) 1205 } 1206 1207 return builder.build() 1208 } 1209 1210 fun build() = 1211 Notification.Builder(context, TEST_CHANNEL_ID) 1212 .also { nb -> 1213 nb.setContentTitle(TEST_CONTENT_TITLE) 1214 nb.setContentText(TEST_CONTENT_TEXT) 1215 1216 whenMs?.let { nb.setWhen(it) } 1217 1218 if (isGrouped) { 1219 nb.setGroup(TEST_GROUP_KEY) 1220 } 1221 1222 if (isGroupSummary) { 1223 nb.setGroupSummary(true) 1224 } 1225 1226 if (isCall) { 1227 nb.extras.putString(EXTRA_TEMPLATE, Notification.CallStyle::class.java.name) 1228 } 1229 1230 if (category != null) { 1231 nb.setCategory(category) 1232 } 1233 groupAlertBehavior?.let { nb.setGroupAlertBehavior(it) } 1234 1235 if (hasBubbleMetadata) { 1236 nb.setBubbleMetadata(buildBubbleMetadata()) 1237 } 1238 1239 if (hasFsi) { 1240 nb.setFullScreenIntent(mock(), /* highPriority = */ true) 1241 } 1242 } 1243 .build() 1244 .also { n -> 1245 if (isForegroundService) { 1246 n.flags = n.flags or FLAG_FOREGROUND_SERVICE 1247 } 1248 1249 if (isUserInitiatedJob) { 1250 n.flags = n.flags or FLAG_USER_INITIATED_JOB 1251 } 1252 1253 if (isBubble) { 1254 n.flags = n.flags or FLAG_BUBBLE 1255 } 1256 1257 if (isStickyAndNotDemoted) { 1258 n.flags = n.flags or FLAG_FSI_REQUESTED_BUT_DENIED 1259 } 1260 if (isColorized) { 1261 n.extras.putBoolean(EXTRA_COLORIZED, true) 1262 n.flags = n.flags or FLAG_CAN_COLORIZE 1263 } 1264 } 1265 .let { NotificationEntryBuilder().setNotification(it) } 1266 .also { neb -> 1267 neb.setPkg(TEST_PACKAGE) 1268 neb.setOpPkg(TEST_PACKAGE) 1269 neb.setTag(TEST_TAG) 1270 1271 neb.setImportance(importance) 1272 val channel = 1273 NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance) 1274 channel.isImportantConversation = isImportantConversation 1275 neb.setChannel(channel) 1276 1277 canBubble?.let { neb.setCanBubble(it) } 1278 } 1279 .build()!! 1280 .also { ne -> 1281 if (hasJustLaunchedFsi) { 1282 ne.notifyFullScreenIntentLaunched() 1283 } 1284 1285 if (isStickyAndNotDemoted) { 1286 assertFalse(ne.isDemoted) 1287 } 1288 1289 modifyRanking(ne) 1290 .also { mrb -> 1291 if (packageSuspended) { 1292 mrb.setSuspended(true) 1293 } 1294 visibilityOverride?.let { mrb.setVisibilityOverride(it) } 1295 suppressedVisualEffects?.let { mrb.setSuppressedVisualEffects(it) } 1296 mrb.setIsConversation(isConversation) 1297 } 1298 .build() 1299 } 1300 } 1301 1302 protected fun buildEntry(block: EntryBuilder.() -> Unit) = 1303 EntryBuilder(context).also(block).build() 1304 1305 protected fun buildPeekEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { 1306 importance = IMPORTANCE_HIGH 1307 run(block) 1308 } 1309 1310 protected fun buildPulseEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { 1311 importance = IMPORTANCE_DEFAULT 1312 visibilityOverride = VISIBILITY_NO_OVERRIDE 1313 run(block) 1314 } 1315 1316 protected fun buildBubbleEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { 1317 isBubble = true 1318 canBubble = true 1319 hasBubbleMetadata = true 1320 run(block) 1321 } 1322 1323 protected fun buildFsiEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { 1324 importance = IMPORTANCE_HIGH 1325 hasFsi = true 1326 run(block) 1327 } 1328 1329 private fun assertNoEventsLogged() { 1330 assertNoUiEventLogged() 1331 assertNoSystemEventLogged() 1332 } 1333 1334 private fun assertNoUiEventLogged() { 1335 assertEquals(0, uiEventLogger.numLogs()) 1336 } 1337 1338 private fun assertUiEventLogged(uiEventId: UiEventEnum, uid: Int, packageName: String) { 1339 assertEquals(1, uiEventLogger.numLogs()) 1340 1341 val event = uiEventLogger.get(0) 1342 assertEquals(uiEventId.id, event.eventId) 1343 assertEquals(uid, event.uid) 1344 assertEquals(packageName, event.packageName) 1345 } 1346 1347 private fun assertNoSystemEventLogged() { 1348 assertEquals(0, eventLog.events.size) 1349 } 1350 1351 private fun assertSystemEventLogged(number: String, uid: Int, description: String) { 1352 assertEquals(1, eventLog.events.size) 1353 1354 val event = eventLog.events[0] 1355 assertEquals(0x534e4554, event.tag) 1356 1357 val value = event.value 1358 assertTrue(value is Array<*>) 1359 1360 if (value is Array<*>) { 1361 assertEquals(3, value.size) 1362 assertEquals(number, value[0]) 1363 assertEquals(uid, value[1]) 1364 assertEquals(description, value[2]) 1365 } 1366 } 1367 1368 protected fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs 1369 } 1370 1371 private const val TEST_CONTENT_TITLE = "Test Content Title" 1372 private const val TEST_CONTENT_TEXT = "Test content text" 1373 private const val TEST_CHANNEL_ID = "test_channel" 1374 private const val TEST_CHANNEL_NAME = "Test Channel" 1375 private const val TEST_PACKAGE = "test_package" 1376 private const val TEST_TAG = "test_tag" 1377 private const val TEST_GROUP_KEY = "test_group_key" 1378