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