Merge "Block FullScreenIntent while device is in use if notification has a silencing GroupAlertBehavior." into tm-qpr-dev am: 7d3188b7c3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19324468

Change-Id: Ie80b338627fc303182e28a8416e7036b3244e726
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jeff DeCew
2022-07-19 22:54:23 +00:00
committed by Automerger Merge Worker
3 changed files with 259 additions and 41 deletions

View File

@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.interruption
import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.dagger.NotificationInterruptLog import com.android.systemui.log.dagger.NotificationInterruptLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.logKey
@@ -211,6 +212,33 @@ class NotificationInterruptLogger @Inject constructor(
}) })
} }
fun logNoFullscreen(entry: NotificationEntry, reason: String) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
str2 = reason
}, {
"No FullScreenIntent: $str2: $str1"
})
}
fun logNoFullscreenWarning(entry: NotificationEntry, reason: String) {
buffer.log(TAG, WARNING, {
str1 = entry.logKey
str2 = reason
}, {
"No FullScreenIntent: WARNING: $str2: $str1"
})
}
fun logFullscreen(entry: NotificationEntry, reason: String) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
str2 = reason
}, {
"FullScreenIntent: $str2: $str1"
})
}
fun keyguardHideNotification(entry: NotificationEntry) { fun keyguardHideNotification(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, { buffer.log(TAG, DEBUG, {
str1 = entry.logKey str1 = entry.logKey

View File

@@ -177,9 +177,69 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
*/ */
@Override @Override
public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
return entry.getSbn().getNotification().fullScreenIntent != null if (entry.getSbn().getNotification().fullScreenIntent == null) {
&& (!shouldHeadsUp(entry) return false;
|| mStatusBarStateController.getState() == StatusBarState.KEYGUARD); }
// Never show FSI when suppressed by DND
if (entry.shouldSuppressFullScreenIntent()) {
mLogger.logNoFullscreen(entry, "Suppressed by DND");
return false;
}
// Never show FSI if importance is not HIGH
if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
mLogger.logNoFullscreen(entry, "Not important enough");
return false;
}
// If the notification has suppressive GroupAlertBehavior, block FSI and warn.
StatusBarNotification sbn = entry.getSbn();
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
// b/231322873: Detect and report an event when a notification has both an FSI and a
// suppressive groupAlertBehavior, and now correctly block the FSI from firing.
final int uid = entry.getSbn().getUid();
android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "groupAlertBehavior");
mLogger.logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
return false;
}
// If the screen is off, then launch the FullScreenIntent
if (!mPowerManager.isInteractive()) {
mLogger.logFullscreen(entry, "Device is not interactive");
return true;
}
// If the device is currently dreaming, then launch the FullScreenIntent
if (isDreaming()) {
mLogger.logFullscreen(entry, "Device is dreaming");
return true;
}
// If the keyguard is showing, then launch the FullScreenIntent
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mLogger.logFullscreen(entry, "Keyguard is showing");
return true;
}
// If the notification should HUN, then we don't need FSI
if (shouldHeadsUp(entry)) {
mLogger.logNoFullscreen(entry, "Expected to HUN");
return false;
}
// If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI.
mLogger.logFullscreen(entry, "Expected not to HUN");
return true;
}
private boolean isDreaming() {
try {
return mDreamManager.isDreaming();
} catch (RemoteException e) {
Log.e(TAG, "Failed to query dream manager.", e);
return false;
}
} }
private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
@@ -223,13 +283,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
return false; return false;
} }
boolean isDreaming = false; boolean inUse = mPowerManager.isScreenOn() && !isDreaming();
try {
isDreaming = mDreamManager.isDreaming();
} catch (RemoteException e) {
Log.e(TAG, "Failed to query dream manager.", e);
}
boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
if (!inUse) { if (!inUse) {
mLogger.logNoHeadsUpNotInUse(entry); mLogger.logNoHeadsUpNotInUse(entry);

View File

@@ -25,6 +25,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -96,6 +98,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
NotifPipelineFlags mFlags; NotifPipelineFlags mFlags;
@Mock @Mock
KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
@Mock
PendingIntent mPendingIntent;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@@ -197,7 +201,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
ensureStateForHeadsUpWhenAwake(); ensureStateForHeadsUpWhenAwake();
// WHEN this entry should be filtered out // WHEN this entry should be filtered out
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true); when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true);
// THEN we shouldn't heads up this entry // THEN we shouldn't heads up this entry
@@ -207,7 +211,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Test @Test
public void testDoNotRunFilterOnNewPipeline() { public void testDoNotRunFilterOnNewPipeline() {
// WHEN this entry should be filtered out // WHEN this entry should be filtered out
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
mNotifInterruptionStateProvider.shouldHeadsUp(entry); mNotifInterruptionStateProvider.shouldHeadsUp(entry);
verify(mNotificationFilter, times(0)).shouldFilterOut(eq(entry)); verify(mNotificationFilter, times(0)).shouldFilterOut(eq(entry));
} }
@@ -422,6 +426,122 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
} }
@Test
public void testShouldNotFullScreen_notPendingIntent() throws RemoteException {
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isFalse();
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger, never()).logFullscreen(any(), any());
}
@Test
public void testShouldNotFullScreen_notHighImportance() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isFalse();
verify(mLogger).logNoFullscreen(entry, "Not important enough");
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger, never()).logFullscreen(any(), any());
}
@Test
public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true);
when(mPowerManager.isInteractive()).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(true);
when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isFalse();
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger).logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
verify(mLogger, never()).logFullscreen(any(), any());
}
@Test
public void testShouldFullScreen_notInteractive() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isTrue();
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger).logFullscreen(entry, "Device is not interactive");
}
@Test
public void testShouldFullScreen_isDreaming() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(true);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isTrue();
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger).logFullscreen(entry, "Device is dreaming");
}
@Test
public void testShouldFullScreen_onKeyguard() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isTrue();
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger).logFullscreen(entry, "Keyguard is showing");
}
@Test
public void testShouldNotFullScreen_willHun() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isFalse();
verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger, never()).logFullscreen(any(), any());
}
@Test
public void testShouldFullScreen_packageSnoozed() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isTrue();
verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger).logFullscreen(entry, "Expected not to HUN");
}
/** /**
* Bubbles can happen. * Bubbles can happen.
*/ */
@@ -516,8 +636,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
private NotificationEntry createBubble(String groupKey, Integer groupAlert) { private NotificationEntry createBubble(String groupKey, Integer groupAlert) {
Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder( Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.getActivity(mContext, 0, new Intent(),
PendingIntent.FLAG_MUTABLE), PendingIntent.FLAG_MUTABLE),
Icon.createWithResource(mContext.getResources(), R.drawable.android)) Icon.createWithResource(mContext.getResources(), R.drawable.android))
.build(); .build();
Notification.Builder nb = new Notification.Builder(getContext(), "a") Notification.Builder nb = new Notification.Builder(getContext(), "a")
.setContentTitle("title") .setContentTitle("title")
@@ -549,6 +669,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
.setContentText("content text") .setContentText("content text")
.build(); .build();
return createNotification(importance, n);
}
private NotificationEntry createNotification(int importance, Notification n) {
return new NotificationEntryBuilder() return new NotificationEntryBuilder()
.setPkg("a") .setPkg("a")
.setOpPkg("a") .setOpPkg("a")
@@ -559,45 +683,57 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
.build(); .build();
} }
private NotificationEntry createFsiNotification(int importance, boolean silent) {
Notification n = new Notification.Builder(getContext(), "a")
.setContentTitle("title")
.setContentText("content text")
.setFullScreenIntent(mPendingIntent, true)
.setGroup("fsi")
.setGroupAlertBehavior(silent ? GROUP_ALERT_SUMMARY : Notification.GROUP_ALERT_ALL)
.build();
return createNotification(importance, n);
}
private final NotificationInterruptSuppressor private final NotificationInterruptSuppressor
mSuppressAwakeHeadsUp = mSuppressAwakeHeadsUp =
new NotificationInterruptSuppressor() { new NotificationInterruptSuppressor() {
@Override @Override
public String getName() { public String getName() {
return "suppressAwakeHeadsUp"; return "suppressAwakeHeadsUp";
} }
@Override @Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) { public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
return true; return true;
} }
}; };
private final NotificationInterruptSuppressor private final NotificationInterruptSuppressor
mSuppressAwakeInterruptions = mSuppressAwakeInterruptions =
new NotificationInterruptSuppressor() { new NotificationInterruptSuppressor() {
@Override @Override
public String getName() { public String getName() {
return "suppressAwakeInterruptions"; return "suppressAwakeInterruptions";
} }
@Override @Override
public boolean suppressAwakeInterruptions(NotificationEntry entry) { public boolean suppressAwakeInterruptions(NotificationEntry entry) {
return true; return true;
} }
}; };
private final NotificationInterruptSuppressor private final NotificationInterruptSuppressor
mSuppressInterruptions = mSuppressInterruptions =
new NotificationInterruptSuppressor() { new NotificationInterruptSuppressor() {
@Override @Override
public String getName() { public String getName() {
return "suppressInterruptions"; return "suppressInterruptions";
} }
@Override @Override
public boolean suppressInterruptions(NotificationEntry entry) { public boolean suppressInterruptions(NotificationEntry entry) {
return true; return true;
} }
}; };
} }