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