1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.notification.interruption; 18 19 import static android.app.Notification.FLAG_BUBBLE; 20 import static android.app.Notification.FLAG_FOREGROUND_SERVICE; 21 import static android.app.Notification.GROUP_ALERT_SUMMARY; 22 import static android.app.Notification.VISIBILITY_PRIVATE; 23 import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 24 import static android.app.NotificationManager.IMPORTANCE_HIGH; 25 import static android.app.NotificationManager.IMPORTANCE_LOW; 26 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; 27 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 28 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; 29 import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; 30 import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED; 31 import static android.provider.Settings.Global.HEADS_UP_ON; 32 33 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; 34 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; 35 import static com.android.systemui.statusbar.StatusBarState.SHADE; 36 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; 37 38 import static com.google.common.truth.Truth.assertThat; 39 40 import static org.mockito.ArgumentMatchers.any; 41 import static org.mockito.ArgumentMatchers.anyInt; 42 import static org.mockito.ArgumentMatchers.anyLong; 43 import static org.mockito.ArgumentMatchers.contains; 44 import static org.mockito.ArgumentMatchers.eq; 45 import static org.mockito.Mockito.clearInvocations; 46 import static org.mockito.Mockito.never; 47 import static org.mockito.Mockito.verify; 48 import static org.mockito.Mockito.verifyNoMoreInteractions; 49 import static org.mockito.Mockito.when; 50 51 import android.app.ActivityManager; 52 import android.app.Flags; 53 import android.app.Notification; 54 import android.app.NotificationChannel; 55 import android.app.PendingIntent; 56 import android.content.Intent; 57 import android.graphics.drawable.Icon; 58 import android.hardware.display.AmbientDisplayConfiguration; 59 import android.os.Handler; 60 import android.os.PowerManager; 61 import android.platform.test.annotations.DisableFlags; 62 63 import androidx.test.ext.junit.runners.AndroidJUnit4; 64 import androidx.test.filters.SmallTest; 65 66 import com.android.internal.logging.testing.UiEventLoggerFake; 67 import com.android.systemui.SysuiTestCase; 68 import com.android.systemui.plugins.statusbar.StatusBarStateController; 69 import com.android.systemui.res.R; 70 import com.android.systemui.settings.UserTracker; 71 import com.android.systemui.statusbar.notification.NotifPipelineFlags; 72 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 73 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; 74 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision; 75 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent; 76 import com.android.systemui.statusbar.policy.BatteryController; 77 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 78 import com.android.systemui.statusbar.policy.HeadsUpManager; 79 import com.android.systemui.statusbar.policy.KeyguardStateController; 80 import com.android.systemui.util.FakeEventLog; 81 import com.android.systemui.util.settings.FakeGlobalSettings; 82 import com.android.systemui.util.time.FakeSystemClock; 83 import com.android.wm.shell.bubbles.Bubbles; 84 85 import org.junit.Before; 86 import org.junit.Test; 87 import org.junit.runner.RunWith; 88 import org.mockito.Mock; 89 import org.mockito.MockitoAnnotations; 90 91 import java.util.Arrays; 92 import java.util.HashSet; 93 import java.util.Optional; 94 import java.util.Set; 95 96 /** 97 * Tests for the interruption state provider which understands whether the system & notification 98 * is in a state allowing a particular notification to hun, pulse, or bubble. 99 */ 100 @RunWith(AndroidJUnit4.class) 101 @SmallTest 102 public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { 103 104 @Mock 105 PowerManager mPowerManager; 106 @Mock 107 AmbientDisplayConfiguration mAmbientDisplayConfiguration; 108 @Mock 109 StatusBarStateController mStatusBarStateController; 110 @Mock 111 KeyguardStateController mKeyguardStateController; 112 @Mock 113 HeadsUpManager mHeadsUpManager; 114 @Mock 115 NotificationInterruptLogger mLogger; 116 @Mock 117 BatteryController mBatteryController; 118 @Mock 119 Handler mMockHandler; 120 @Mock 121 NotifPipelineFlags mFlags; 122 @Mock 123 KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; 124 UiEventLoggerFake mUiEventLoggerFake; 125 @Mock 126 PendingIntent mPendingIntent; 127 @Mock 128 UserTracker mUserTracker; 129 @Mock 130 DeviceProvisionedController mDeviceProvisionedController; 131 @Mock 132 Bubbles mBubbles; 133 FakeSystemClock mSystemClock; 134 FakeGlobalSettings mGlobalSettings; 135 FakeEventLog mEventLog; 136 137 private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; 138 139 @Before setup()140 public void setup() { 141 MockitoAnnotations.initMocks(this); 142 when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser()); 143 when(mBubbles.canShowBubbleNotification()).thenReturn(true); 144 145 mUiEventLoggerFake = new UiEventLoggerFake(); 146 mSystemClock = new FakeSystemClock(); 147 mGlobalSettings = new FakeGlobalSettings(); 148 mGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON); 149 mEventLog = new FakeEventLog(); 150 151 mNotifInterruptionStateProvider = 152 new NotificationInterruptStateProviderImpl( 153 mPowerManager, 154 mAmbientDisplayConfiguration, 155 mBatteryController, 156 mStatusBarStateController, 157 mKeyguardStateController, 158 mHeadsUpManager, 159 mLogger, 160 mMockHandler, 161 mFlags, 162 mKeyguardNotificationVisibilityProvider, 163 mUiEventLoggerFake, 164 mUserTracker, 165 mDeviceProvisionedController, 166 mSystemClock, 167 mGlobalSettings, 168 mEventLog, 169 Optional.of(mBubbles)); 170 mNotifInterruptionStateProvider.mUseHeadsUp = true; 171 } 172 173 /** 174 * Sets up the state such that any requests to 175 * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will 176 * pass as long its provided NotificationEntry fulfills importance & DND checks. 177 */ ensureStateForHeadsUpWhenAwake()178 private void ensureStateForHeadsUpWhenAwake() { 179 when(mHeadsUpManager.isSnoozed(any())).thenReturn(false); 180 181 when(mStatusBarStateController.isDozing()).thenReturn(false); 182 when(mStatusBarStateController.isDreaming()).thenReturn(false); 183 when(mPowerManager.isScreenOn()).thenReturn(true); 184 } 185 186 /** 187 * Sets up the state such that any requests to 188 * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will 189 * pass as long its provided NotificationEntry fulfills importance & DND checks. 190 */ ensureStateForHeadsUpWhenDozing()191 private void ensureStateForHeadsUpWhenDozing() { 192 when(mStatusBarStateController.isDozing()).thenReturn(true); 193 when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(true); 194 } 195 196 @Test testDefaultSuppressorDoesNotSuppress()197 public void testDefaultSuppressorDoesNotSuppress() { 198 // GIVEN a suppressor without any overrides 199 final NotificationInterruptSuppressor defaultSuppressor = 200 new NotificationInterruptSuppressor() { 201 @Override 202 public String getName() { 203 return "defaultSuppressor"; 204 } 205 }; 206 207 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 208 209 // THEN this suppressor doesn't suppress anything by default 210 assertThat(defaultSuppressor.suppressAwakeHeadsUp(entry)).isFalse(); 211 assertThat(defaultSuppressor.suppressAwakeInterruptions(entry)).isFalse(); 212 assertThat(defaultSuppressor.suppressInterruptions(entry)).isFalse(); 213 } 214 215 @Test testShouldHeadsUpAwake()216 public void testShouldHeadsUpAwake() { 217 ensureStateForHeadsUpWhenAwake(); 218 219 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 220 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 221 } 222 223 @Test testShouldNotHeadsUp_suppressedForGroups()224 public void testShouldNotHeadsUp_suppressedForGroups() { 225 // GIVEN state for "heads up when awake" is true 226 ensureStateForHeadsUpWhenAwake(); 227 228 // WHEN the alert for a grouped notification is suppressed 229 // see {@link android.app.Notification#GROUP_ALERT_CHILDREN} 230 NotificationEntry entry = new NotificationEntryBuilder() 231 .setPkg("a") 232 .setOpPkg("a") 233 .setTag("a") 234 .setNotification(new Notification.Builder(getContext(), "a") 235 .setGroup("a") 236 .setGroupSummary(true) 237 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN) 238 .build()) 239 .setImportance(IMPORTANCE_DEFAULT) 240 .build(); 241 242 // THEN this entry shouldn't HUN 243 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 244 } 245 246 @Test testShouldHeadsUpWhenDozing()247 public void testShouldHeadsUpWhenDozing() { 248 ensureStateForHeadsUpWhenDozing(); 249 250 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 251 modifyRanking(entry) 252 .setVisibilityOverride(VISIBILITY_NO_OVERRIDE) 253 .build(); 254 255 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 256 } 257 258 @Test testShouldHeadsUpWhenDozing_hiddenOnLockscreen()259 public void testShouldHeadsUpWhenDozing_hiddenOnLockscreen() { 260 ensureStateForHeadsUpWhenDozing(); 261 262 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 263 modifyRanking(entry) 264 .setVisibilityOverride(VISIBILITY_PRIVATE) 265 .build(); 266 267 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 268 } 269 270 @Test testShouldNotHeadsUpWhenDozing_pulseDisabled()271 public void testShouldNotHeadsUpWhenDozing_pulseDisabled() { 272 // GIVEN state for "heads up when dozing" is true 273 ensureStateForHeadsUpWhenDozing(); 274 275 // WHEN pulsing (HUNs when dozing) is disabled 276 when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(false); 277 278 // THEN this entry shouldn't HUN 279 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 280 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 281 } 282 283 @Test testShouldNotHeadsUpWhenDozing_notDozing()284 public void testShouldNotHeadsUpWhenDozing_notDozing() { 285 // GIVEN state for "heads up when dozing" is true 286 ensureStateForHeadsUpWhenDozing(); 287 288 // WHEN we're not dozing (in ambient display or sleeping) 289 when(mStatusBarStateController.isDozing()).thenReturn(false); 290 291 // THEN this entry shouldn't HUN 292 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 293 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 294 } 295 296 /** 297 * In DND ambient effects can be suppressed 298 * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}. 299 */ 300 @Test testShouldNotHeadsUpWhenDozing_suppressingAmbient()301 public void testShouldNotHeadsUpWhenDozing_suppressingAmbient() { 302 ensureStateForHeadsUpWhenDozing(); 303 304 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 305 modifyRanking(entry) 306 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT) 307 .build(); 308 309 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 310 } 311 312 @Test testShouldNotHeadsUpWhenDozing_lessImportant()313 public void testShouldNotHeadsUpWhenDozing_lessImportant() { 314 ensureStateForHeadsUpWhenDozing(); 315 316 // Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't 317 // get to pulse 318 NotificationEntry entry = createNotification(IMPORTANCE_LOW); 319 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 320 } 321 322 @Test testShouldHeadsUp()323 public void testShouldHeadsUp() { 324 ensureStateForHeadsUpWhenAwake(); 325 326 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 327 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 328 } 329 330 /** 331 * If the notification is a bubble, and the user is not on AOD / lockscreen, then 332 * the bubble is shown rather than the heads up. 333 */ 334 @Test testShouldNotHeadsUp_bubble()335 public void testShouldNotHeadsUp_bubble() { 336 ensureStateForHeadsUpWhenAwake(); 337 338 // Bubble bit only applies to interruption when we're in the shade 339 when(mStatusBarStateController.getState()).thenReturn(SHADE); 340 341 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(createBubble())).isFalse(); 342 } 343 344 /** 345 * If the notification is a bubble, and the user is not on AOD / lockscreen, but a bubble 346 * notification can't be shown, then show the heads up. 347 */ 348 @Test testShouldHeadsUp_bubble_bubblesCannotShowNotification()349 public void testShouldHeadsUp_bubble_bubblesCannotShowNotification() { 350 ensureStateForHeadsUpWhenAwake(); 351 352 // Bubble bit only applies to interruption when we're in the shade 353 when(mStatusBarStateController.getState()).thenReturn(SHADE); 354 355 when(mBubbles.canShowBubbleNotification()).thenReturn(false); 356 357 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(createBubble())).isTrue(); 358 } 359 360 /** 361 * If we're not allowed to alert in general, we shouldn't be shown as heads up. 362 */ 363 @Test testShouldNotHeadsUp_filtered()364 public void testShouldNotHeadsUp_filtered() { 365 ensureStateForHeadsUpWhenAwake(); 366 // Make canAlertCommon false by saying it's filtered out 367 when(mKeyguardNotificationVisibilityProvider.shouldHideNotification(any())) 368 .thenReturn(true); 369 370 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 371 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 372 } 373 374 /** 375 * In DND HUN peek effects can be suppressed 376 * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}. 377 */ 378 @Test testShouldNotHeadsUp_suppressPeek()379 public void testShouldNotHeadsUp_suppressPeek() { 380 ensureStateForHeadsUpWhenAwake(); 381 382 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 383 modifyRanking(entry) 384 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK) 385 .build(); 386 387 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 388 } 389 390 /** 391 * Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_HIGH} don't get 392 * to show as a heads up. 393 */ 394 @Test testShouldNotHeadsUp_lessImportant()395 public void testShouldNotHeadsUp_lessImportant() { 396 ensureStateForHeadsUpWhenAwake(); 397 398 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 399 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 400 } 401 402 /** 403 * If the device is not in use then we shouldn't be shown as heads up. 404 */ 405 @Test testShouldNotHeadsUp_deviceNotInUse()406 public void testShouldNotHeadsUp_deviceNotInUse() { 407 ensureStateForHeadsUpWhenAwake(); 408 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 409 410 // Device is not in use if screen is not on 411 when(mPowerManager.isScreenOn()).thenReturn(false); 412 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 413 414 // Also not in use if screen is on but we're showing screen saver / "dreaming" 415 when(mPowerManager.isDeviceIdleMode()).thenReturn(true); 416 when(mStatusBarStateController.isDreaming()).thenReturn(true); 417 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 418 } 419 420 @Test testShouldNotHeadsUp_headsUpSuppressed()421 public void testShouldNotHeadsUp_headsUpSuppressed() { 422 ensureStateForHeadsUpWhenAwake(); 423 424 // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up. 425 mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeHeadsUp); 426 427 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 428 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 429 } 430 431 @Test testShouldNotHeadsUpAwake_awakeInterruptsSuppressed()432 public void testShouldNotHeadsUpAwake_awakeInterruptsSuppressed() { 433 ensureStateForHeadsUpWhenAwake(); 434 435 // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up. 436 mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeInterruptions); 437 438 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 439 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 440 } 441 442 /** 443 * On screen alerts don't happen when the notification is snoozed. 444 */ 445 @Test testShouldNotHeadsUp_snoozedPackage()446 public void testShouldNotHeadsUp_snoozedPackage() { 447 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 448 449 when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true); 450 451 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 452 } 453 454 455 @Test testShouldNotHeadsUp_justLaunchedFullscreen()456 public void testShouldNotHeadsUp_justLaunchedFullscreen() { 457 458 // On screen alerts don't happen when that package has just launched fullscreen. 459 NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); 460 entry.notifyFullScreenIntentLaunched(); 461 462 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 463 } 464 makeWhenHoursAgo(long hoursAgo)465 private long makeWhenHoursAgo(long hoursAgo) { 466 return mSystemClock.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo); 467 } 468 469 @Test testShouldHeadsUp_oldWhen_whenNow()470 public void testShouldHeadsUp_oldWhen_whenNow() { 471 ensureStateForHeadsUpWhenAwake(); 472 473 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 474 475 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 476 477 verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong()); 478 verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any()); 479 } 480 481 @Test testShouldHeadsUp_oldWhen_whenRecent()482 public void testShouldHeadsUp_oldWhen_whenRecent() { 483 ensureStateForHeadsUpWhenAwake(); 484 485 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 486 entry.getSbn().getNotification().when = makeWhenHoursAgo(13); 487 488 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 489 490 verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong()); 491 verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any()); 492 } 493 494 @Test 495 @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) testShouldHeadsUp_oldWhen_whenZero()496 public void testShouldHeadsUp_oldWhen_whenZero() { 497 ensureStateForHeadsUpWhenAwake(); 498 499 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 500 entry.getSbn().getNotification().when = 0L; 501 502 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 503 504 verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong()); 505 verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(0L), anyLong(), 506 eq("when <= 0")); 507 } 508 509 @Test testShouldHeadsUp_oldWhen_whenNegative()510 public void testShouldHeadsUp_oldWhen_whenNegative() { 511 ensureStateForHeadsUpWhenAwake(); 512 513 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 514 entry.getSbn().getNotification().when = -1L; 515 516 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 517 verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong()); 518 verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(-1L), anyLong(), 519 eq("when <= 0")); 520 } 521 522 @Test testShouldHeadsUp_oldWhen_hasFullScreenIntent()523 public void testShouldHeadsUp_oldWhen_hasFullScreenIntent() { 524 ensureStateForHeadsUpWhenAwake(); 525 long when = makeWhenHoursAgo(25); 526 527 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silent= */ false); 528 entry.getSbn().getNotification().when = when; 529 530 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 531 532 verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong()); 533 verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(), 534 eq("full-screen intent")); 535 } 536 537 @Test testShouldHeadsUp_oldWhen_isForegroundService()538 public void testShouldHeadsUp_oldWhen_isForegroundService() { 539 ensureStateForHeadsUpWhenAwake(); 540 long when = makeWhenHoursAgo(25); 541 542 NotificationEntry entry = createFgsNotification(IMPORTANCE_HIGH); 543 entry.getSbn().getNotification().when = when; 544 545 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 546 547 verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong()); 548 verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(), 549 eq("foreground service")); 550 } 551 552 @Test testShouldNotHeadsUp_oldWhen()553 public void testShouldNotHeadsUp_oldWhen() { 554 ensureStateForHeadsUpWhenAwake(); 555 long when = makeWhenHoursAgo(25); 556 557 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 558 entry.getSbn().getNotification().when = when; 559 560 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); 561 562 verify(mLogger).logNoHeadsUpOldWhen(eq(entry), eq(when), anyLong()); 563 verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any()); 564 } 565 566 @Test testShouldNotFullScreen_notPendingIntent()567 public void testShouldNotFullScreen_notPendingIntent() { 568 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 569 when(mPowerManager.isInteractive()).thenReturn(true); 570 when(mStatusBarStateController.isDreaming()).thenReturn(false); 571 when(mStatusBarStateController.getState()).thenReturn(SHADE); 572 573 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 574 .isEqualTo(FullScreenIntentDecision.NO_FULL_SCREEN_INTENT); 575 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 576 .isFalse(); 577 verify(mLogger, never()).logNoFullscreen(any(), any()); 578 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 579 verify(mLogger, never()).logFullscreen(any(), any()); 580 } 581 582 @Test testShouldNotFullScreen_suppressedOnlyByDND()583 public void testShouldNotFullScreen_suppressedOnlyByDND() { 584 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 585 modifyRanking(entry) 586 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) 587 .build(); 588 when(mPowerManager.isInteractive()).thenReturn(false); 589 when(mStatusBarStateController.isDreaming()).thenReturn(false); 590 when(mStatusBarStateController.getState()).thenReturn(SHADE); 591 592 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 593 .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND); 594 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 595 .isFalse(); 596 verify(mLogger, never()).logFullscreen(any(), any()); 597 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 598 verify(mLogger).logNoFullscreen(entry, "NO_FSI_SUPPRESSED_ONLY_BY_DND"); 599 } 600 601 @Test testShouldNotFullScreen_suppressedByDNDAndOther()602 public void testShouldNotFullScreen_suppressedByDNDAndOther() { 603 NotificationEntry entry = createFsiNotification(IMPORTANCE_LOW, /* silenced */ false); 604 modifyRanking(entry) 605 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) 606 .build(); 607 when(mPowerManager.isInteractive()).thenReturn(false); 608 when(mStatusBarStateController.isDreaming()).thenReturn(false); 609 when(mStatusBarStateController.getState()).thenReturn(SHADE); 610 611 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 612 .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND); 613 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 614 .isFalse(); 615 verify(mLogger, never()).logFullscreen(any(), any()); 616 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 617 verify(mLogger).logNoFullscreen(entry, "NO_FSI_SUPPRESSED_BY_DND"); 618 } 619 620 @Test testShouldNotFullScreen_notHighImportance()621 public void testShouldNotFullScreen_notHighImportance() { 622 NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false); 623 when(mPowerManager.isInteractive()).thenReturn(true); 624 when(mStatusBarStateController.isDreaming()).thenReturn(false); 625 when(mStatusBarStateController.getState()).thenReturn(SHADE); 626 627 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 628 .isEqualTo(FullScreenIntentDecision.NO_FSI_NOT_IMPORTANT_ENOUGH); 629 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 630 .isFalse(); 631 verify(mLogger).logNoFullscreen(entry, "NO_FSI_NOT_IMPORTANT_ENOUGH"); 632 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 633 verify(mLogger, never()).logFullscreen(any(), any()); 634 } 635 636 @Test testShouldNotFullScreen_isGroupAlertSilenced()637 public void testShouldNotFullScreen_isGroupAlertSilenced() { 638 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true); 639 when(mPowerManager.isInteractive()).thenReturn(false); 640 when(mStatusBarStateController.isDreaming()).thenReturn(true); 641 when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); 642 643 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 644 .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR); 645 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 646 .isFalse(); 647 verify(mLogger, never()).logNoFullscreen(any(), any()); 648 verify(mLogger).logNoFullscreenWarning(entry, 649 "NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR: GroupAlertBehavior will prevent HUN"); 650 verify(mLogger, never()).logFullscreen(any(), any()); 651 652 assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1); 653 UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0); 654 assertThat(fakeUiEvent.eventId).isEqualTo( 655 NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR.getId()); 656 assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid()); 657 assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName()); 658 } 659 660 @Test testShouldNotFullScreen_isSuppressedByBubbleMetadata()661 public void testShouldNotFullScreen_isSuppressedByBubbleMetadata() { 662 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 663 Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") 664 .setSuppressNotification(true).build(); 665 entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); 666 when(mPowerManager.isInteractive()).thenReturn(false); 667 when(mStatusBarStateController.isDreaming()).thenReturn(true); 668 when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); 669 670 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 671 .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA); 672 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 673 .isFalse(); 674 verify(mLogger, never()).logNoFullscreen(any(), any()); 675 verify(mLogger).logNoFullscreenWarning(entry, 676 "NO_FSI_SUPPRESSIVE_BUBBLE_METADATA: BubbleMetadata may prevent HUN"); 677 verify(mLogger, never()).logFullscreen(any(), any()); 678 679 assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1); 680 UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0); 681 assertThat(fakeUiEvent.eventId).isEqualTo( 682 NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA.getId()); 683 assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid()); 684 assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName()); 685 } 686 687 @Test testShouldFullScreen_notInteractive()688 public void testShouldFullScreen_notInteractive() { 689 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 690 Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") 691 .setSuppressNotification(false).build(); 692 entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); 693 when(mPowerManager.isInteractive()).thenReturn(false); 694 when(mStatusBarStateController.isDreaming()).thenReturn(false); 695 when(mStatusBarStateController.getState()).thenReturn(SHADE); 696 697 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 698 .isEqualTo(FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE); 699 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 700 .isTrue(); 701 verify(mLogger, never()).logNoFullscreen(any(), any()); 702 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 703 verify(mLogger).logFullscreen(entry, "FSI_DEVICE_NOT_INTERACTIVE"); 704 } 705 706 @Test testShouldFullScreen_isDreaming()707 public void testShouldFullScreen_isDreaming() { 708 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 709 when(mPowerManager.isInteractive()).thenReturn(true); 710 when(mStatusBarStateController.isDreaming()).thenReturn(true); 711 when(mStatusBarStateController.getState()).thenReturn(SHADE); 712 713 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 714 .isEqualTo(FullScreenIntentDecision.FSI_DEVICE_IS_DREAMING); 715 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 716 .isTrue(); 717 verify(mLogger, never()).logNoFullscreen(any(), any()); 718 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 719 verify(mLogger).logFullscreen(entry, "FSI_DEVICE_IS_DREAMING"); 720 } 721 722 @Test testShouldFullScreen_onKeyguard()723 public void testShouldFullScreen_onKeyguard() { 724 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 725 when(mPowerManager.isInteractive()).thenReturn(true); 726 when(mStatusBarStateController.isDreaming()).thenReturn(false); 727 when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); 728 729 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 730 .isEqualTo(FullScreenIntentDecision.FSI_KEYGUARD_SHOWING); 731 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 732 .isTrue(); 733 verify(mLogger, never()).logNoFullscreen(any(), any()); 734 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 735 verify(mLogger).logFullscreen(entry, "FSI_KEYGUARD_SHOWING"); 736 } 737 738 @Test testShouldFullscreen_suppressedInterruptionsWhenNotProvisioned()739 public void testShouldFullscreen_suppressedInterruptionsWhenNotProvisioned() { 740 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 741 when(mPowerManager.isInteractive()).thenReturn(true); 742 when(mStatusBarStateController.getState()).thenReturn(SHADE); 743 when(mStatusBarStateController.isDreaming()).thenReturn(false); 744 when(mPowerManager.isScreenOn()).thenReturn(true); 745 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false); 746 mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions); 747 748 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 749 .isEqualTo(FullScreenIntentDecision.FSI_NOT_PROVISIONED); 750 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 751 .isTrue(); 752 verify(mLogger, never()).logNoFullscreen(any(), any()); 753 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 754 verify(mLogger).logFullscreen(entry, "FSI_NOT_PROVISIONED"); 755 } 756 757 @Test testShouldNotFullScreen_willHun()758 public void testShouldNotFullScreen_willHun() { 759 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 760 when(mPowerManager.isInteractive()).thenReturn(true); 761 when(mPowerManager.isScreenOn()).thenReturn(true); 762 when(mStatusBarStateController.isDreaming()).thenReturn(false); 763 when(mStatusBarStateController.getState()).thenReturn(SHADE); 764 765 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 766 .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN); 767 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 768 .isFalse(); 769 verify(mLogger).logNoFullscreen(entry, "NO_FSI_EXPECTED_TO_HUN"); 770 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 771 verify(mLogger, never()).logFullscreen(any(), any()); 772 } 773 774 @Test testShouldNotFullScreen_snoozed_occluding()775 public void testShouldNotFullScreen_snoozed_occluding() { 776 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 777 when(mPowerManager.isInteractive()).thenReturn(true); 778 when(mPowerManager.isScreenOn()).thenReturn(true); 779 when(mStatusBarStateController.isDreaming()).thenReturn(false); 780 when(mStatusBarStateController.getState()).thenReturn(SHADE); 781 when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); 782 when(mKeyguardStateController.isShowing()).thenReturn(true); 783 when(mKeyguardStateController.isOccluded()).thenReturn(true); 784 785 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 786 .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN); 787 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 788 .isFalse(); 789 verify(mLogger).logNoFullscreen(entry, "NO_FSI_EXPECTED_TO_HUN"); 790 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 791 verify(mLogger, never()).logFullscreen(any(), any()); 792 } 793 794 @Test testShouldHeadsUp_snoozed_occluding()795 public void testShouldHeadsUp_snoozed_occluding() { 796 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 797 when(mPowerManager.isInteractive()).thenReturn(true); 798 when(mPowerManager.isScreenOn()).thenReturn(true); 799 when(mStatusBarStateController.isDreaming()).thenReturn(false); 800 when(mStatusBarStateController.getState()).thenReturn(SHADE); 801 when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); 802 when(mKeyguardStateController.isShowing()).thenReturn(true); 803 when(mKeyguardStateController.isOccluded()).thenReturn(true); 804 805 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 806 807 verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry); 808 verify(mLogger, never()).logHeadsUp(any()); 809 810 assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1); 811 UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0); 812 assertThat(fakeUiEvent.eventId).isEqualTo( 813 NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId()); 814 assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid()); 815 assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName()); 816 } 817 818 @Test testShouldNotFullScreen_snoozed_lockedShade()819 public void testShouldNotFullScreen_snoozed_lockedShade() { 820 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 821 when(mPowerManager.isInteractive()).thenReturn(true); 822 when(mPowerManager.isScreenOn()).thenReturn(true); 823 when(mStatusBarStateController.isDreaming()).thenReturn(false); 824 when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED); 825 when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); 826 when(mKeyguardStateController.isShowing()).thenReturn(true); 827 when(mKeyguardStateController.isOccluded()).thenReturn(false); 828 829 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 830 .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN); 831 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 832 .isFalse(); 833 verify(mLogger).logNoFullscreen(entry, "NO_FSI_EXPECTED_TO_HUN"); 834 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 835 verify(mLogger, never()).logFullscreen(any(), any()); 836 } 837 838 @Test testShouldHeadsUp_snoozed_lockedShade()839 public void testShouldHeadsUp_snoozed_lockedShade() { 840 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 841 when(mPowerManager.isInteractive()).thenReturn(true); 842 when(mPowerManager.isScreenOn()).thenReturn(true); 843 when(mStatusBarStateController.isDreaming()).thenReturn(false); 844 when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED); 845 when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); 846 when(mKeyguardStateController.isShowing()).thenReturn(true); 847 when(mKeyguardStateController.isOccluded()).thenReturn(false); 848 849 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 850 851 verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry); 852 verify(mLogger, never()).logHeadsUp(any()); 853 854 assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1); 855 UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0); 856 assertThat(fakeUiEvent.eventId).isEqualTo( 857 NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId()); 858 assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid()); 859 assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName()); 860 } 861 862 @Test testShouldNotFullScreen_snoozed_unlocked()863 public void testShouldNotFullScreen_snoozed_unlocked() { 864 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 865 when(mPowerManager.isInteractive()).thenReturn(true); 866 when(mPowerManager.isScreenOn()).thenReturn(true); 867 when(mStatusBarStateController.isDreaming()).thenReturn(false); 868 when(mStatusBarStateController.getState()).thenReturn(SHADE); 869 when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); 870 when(mKeyguardStateController.isShowing()).thenReturn(false); 871 when(mKeyguardStateController.isOccluded()).thenReturn(false); 872 873 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 874 .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN); 875 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 876 .isFalse(); 877 verify(mLogger).logNoFullscreen(entry, "NO_FSI_EXPECTED_TO_HUN"); 878 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 879 verify(mLogger, never()).logFullscreen(any(), any()); 880 } 881 882 @Test testShouldNotScreen_appSuspended()883 public void testShouldNotScreen_appSuspended() { 884 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 885 when(mPowerManager.isInteractive()).thenReturn(false); 886 when(mStatusBarStateController.isDreaming()).thenReturn(false); 887 when(mStatusBarStateController.getState()).thenReturn(SHADE); 888 modifyRanking(entry).setSuspended(true).build(); 889 890 assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry)) 891 .isEqualTo(FullScreenIntentDecision.NO_FSI_SUSPENDED); 892 assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) 893 .isFalse(); 894 verify(mLogger).logNoFullscreen(entry, "NO_FSI_SUSPENDED"); 895 verify(mLogger, never()).logNoFullscreenWarning(any(), any()); 896 verify(mLogger, never()).logFullscreen(any(), any()); 897 } 898 899 @Test logFullScreenIntentDecision_shouldAlmostAlwaysLogOneTime()900 public void logFullScreenIntentDecision_shouldAlmostAlwaysLogOneTime() { 901 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 902 Set<FullScreenIntentDecision> warnings = new HashSet<>(Arrays.asList( 903 FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, 904 FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA, 905 FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD 906 )); 907 for (FullScreenIntentDecision decision : FullScreenIntentDecision.values()) { 908 clearInvocations(mLogger); 909 boolean expectedToLog = decision != FullScreenIntentDecision.NO_FULL_SCREEN_INTENT; 910 boolean isWarning = warnings.contains(decision); 911 mNotifInterruptionStateProvider.logFullScreenIntentDecision(entry, decision); 912 if (decision.shouldLaunch) { 913 verify(mLogger).logFullscreen(eq(entry), contains(decision.name())); 914 } else if (expectedToLog) { 915 if (isWarning) { 916 verify(mLogger).logNoFullscreenWarning(eq(entry), contains(decision.name())); 917 } else { 918 verify(mLogger).logNoFullscreen(eq(entry), contains(decision.name())); 919 } 920 } 921 verifyNoMoreInteractions(mLogger); 922 } 923 } 924 925 @Test testShouldHeadsUp_snoozed_unlocked()926 public void testShouldHeadsUp_snoozed_unlocked() { 927 NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); 928 when(mPowerManager.isInteractive()).thenReturn(true); 929 when(mPowerManager.isScreenOn()).thenReturn(true); 930 when(mStatusBarStateController.isDreaming()).thenReturn(false); 931 when(mStatusBarStateController.getState()).thenReturn(SHADE); 932 when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); 933 when(mKeyguardStateController.isShowing()).thenReturn(false); 934 when(mKeyguardStateController.isOccluded()).thenReturn(false); 935 936 assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); 937 938 verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry); 939 verify(mLogger, never()).logHeadsUp(any()); 940 941 assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1); 942 UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0); 943 assertThat(fakeUiEvent.eventId).isEqualTo( 944 NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId()); 945 assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid()); 946 assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName()); 947 } 948 949 /* TODO: Verify the FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD UiEvent some other way. */ 950 951 /** 952 * Bubbles can happen. 953 */ 954 @Test testShouldBubbleUp()955 public void testShouldBubbleUp() { 956 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue(); 957 } 958 959 /** 960 * Test that notification can bubble even if it is a child in a group and group settings are 961 * set to alert only for summary notifications. 962 */ 963 @Test testShouldBubbleUp_notifInGroupWithOnlySummaryAlerts()964 public void testShouldBubbleUp_notifInGroupWithOnlySummaryAlerts() { 965 NotificationEntry bubble = createBubble("testgroup", GROUP_ALERT_SUMMARY); 966 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(bubble)).isTrue(); 967 } 968 969 /** 970 * If the notification doesn't have permission to bubble, it shouldn't bubble. 971 */ 972 @Test shouldNotBubbleUp_notAllowedToBubble()973 public void shouldNotBubbleUp_notAllowedToBubble() { 974 NotificationEntry entry = createBubble(); 975 modifyRanking(entry) 976 .setCanBubble(false) 977 .build(); 978 979 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse(); 980 } 981 982 /** 983 * If the notification isn't a bubble, it should definitely not show as a bubble. 984 */ 985 @Test shouldNotBubbleUp_notABubble()986 public void shouldNotBubbleUp_notABubble() { 987 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 988 modifyRanking(entry) 989 .setCanBubble(true) 990 .build(); 991 992 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse(); 993 } 994 995 /** 996 * If the notification doesn't have bubble metadata, it shouldn't bubble. 997 */ 998 @Test shouldNotBubbleUp_invalidMetadata()999 public void shouldNotBubbleUp_invalidMetadata() { 1000 NotificationEntry entry = createNotification(IMPORTANCE_HIGH); 1001 modifyRanking(entry) 1002 .setCanBubble(true) 1003 .build(); 1004 entry.getSbn().getNotification().flags |= FLAG_BUBBLE; 1005 1006 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse(); 1007 } 1008 1009 @Test shouldNotBubbleUp_suppressedInterruptions()1010 public void shouldNotBubbleUp_suppressedInterruptions() { 1011 // If the notification can't heads up in general, it shouldn't bubble. 1012 mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions); 1013 1014 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse(); 1015 } 1016 1017 @Test shouldNotBubbleUp_filteredOut()1018 public void shouldNotBubbleUp_filteredOut() { 1019 // Make canAlertCommon false by saying it's filtered out 1020 when(mKeyguardNotificationVisibilityProvider.shouldHideNotification(any())) 1021 .thenReturn(true); 1022 1023 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse(); 1024 } 1025 1026 @Test shouldNotBubbleUp_suspended()1027 public void shouldNotBubbleUp_suspended() { 1028 assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createSuspendedBubble())) 1029 .isFalse(); 1030 } 1031 createSuspendedBubble()1032 private NotificationEntry createSuspendedBubble() { 1033 return createBubble(null, null, true); 1034 } 1035 createBubble()1036 private NotificationEntry createBubble() { 1037 return createBubble(null, null, false); 1038 } 1039 createBubble(String groupKey, Integer groupAlert)1040 private NotificationEntry createBubble(String groupKey, Integer groupAlert) { 1041 return createBubble(groupKey, groupAlert, false); 1042 } 1043 createBubble(String groupKey, Integer groupAlert, Boolean suspended)1044 private NotificationEntry createBubble(String groupKey, Integer groupAlert, Boolean suspended) { 1045 Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder( 1046 PendingIntent.getActivity(mContext, 0, 1047 new Intent().setPackage(mContext.getPackageName()), 1048 PendingIntent.FLAG_MUTABLE), 1049 Icon.createWithResource(mContext.getResources(), R.drawable.android)) 1050 .build(); 1051 Notification.Builder nb = new Notification.Builder(getContext(), "a") 1052 .setContentTitle("title") 1053 .setContentText("content text") 1054 .setBubbleMetadata(data); 1055 if (groupKey != null) { 1056 nb.setGroup(groupKey); 1057 nb.setGroupSummary(false); 1058 } 1059 if (groupAlert != null) { 1060 nb.setGroupAlertBehavior(groupAlert); 1061 } 1062 Notification n = nb.build(); 1063 n.flags |= FLAG_BUBBLE; 1064 1065 return new NotificationEntryBuilder() 1066 .setPkg("a") 1067 .setOpPkg("a") 1068 .setTag("a") 1069 .setNotification(n) 1070 .setImportance(IMPORTANCE_HIGH) 1071 .setCanBubble(true) 1072 .setSuspended(suspended) 1073 .build(); 1074 } 1075 createNotification(int importance)1076 private NotificationEntry createNotification(int importance) { 1077 Notification n = new Notification.Builder(getContext(), "a") 1078 .setContentTitle("title") 1079 .setContentText("content text") 1080 .build(); 1081 1082 return createNotification(importance, n); 1083 } 1084 createNotification(int importance, Notification n)1085 private NotificationEntry createNotification(int importance, Notification n) { 1086 return new NotificationEntryBuilder() 1087 .setPkg("a") 1088 .setOpPkg("a") 1089 .setTag("a") 1090 .setChannel(new NotificationChannel("a", null, importance)) 1091 .setNotification(n) 1092 .setImportance(importance) 1093 .build(); 1094 } 1095 createFsiNotification(int importance, boolean silent)1096 private NotificationEntry createFsiNotification(int importance, boolean silent) { 1097 Notification n = new Notification.Builder(getContext(), "a") 1098 .setContentTitle("title") 1099 .setContentText("content text") 1100 .setFullScreenIntent(mPendingIntent, true) 1101 .setGroup("fsi") 1102 .setGroupAlertBehavior(silent ? GROUP_ALERT_SUMMARY : Notification.GROUP_ALERT_ALL) 1103 .build(); 1104 1105 return createNotification(importance, n); 1106 } 1107 createFgsNotification(int importance)1108 private NotificationEntry createFgsNotification(int importance) { 1109 Notification n = new Notification.Builder(getContext(), "a") 1110 .setContentTitle("title") 1111 .setContentText("content text") 1112 .setFlag(FLAG_FOREGROUND_SERVICE, true) 1113 .build(); 1114 1115 return createNotification(importance, n); 1116 } 1117 1118 private final NotificationInterruptSuppressor 1119 mSuppressAwakeHeadsUp = 1120 new NotificationInterruptSuppressor() { 1121 @Override 1122 public String getName() { 1123 return "suppressAwakeHeadsUp"; 1124 } 1125 1126 @Override 1127 public boolean suppressAwakeHeadsUp(NotificationEntry entry) { 1128 return true; 1129 } 1130 }; 1131 1132 private final NotificationInterruptSuppressor 1133 mSuppressAwakeInterruptions = 1134 new NotificationInterruptSuppressor() { 1135 @Override 1136 public String getName() { 1137 return "suppressAwakeInterruptions"; 1138 } 1139 1140 @Override 1141 public boolean suppressAwakeInterruptions(NotificationEntry entry) { 1142 return true; 1143 } 1144 }; 1145 1146 private final NotificationInterruptSuppressor 1147 mSuppressInterruptions = 1148 new NotificationInterruptSuppressor() { 1149 @Override 1150 public String getName() { 1151 return "suppressInterruptions"; 1152 } 1153 1154 @Override 1155 public boolean suppressInterruptions(NotificationEntry entry) { 1156 return true; 1157 } 1158 }; 1159 } 1160