Don't suppress pulsing notifications if we are already pulsing
When we receive a new alerting notification while we are already
pulsing, we make another request to DozeTriggers.requestPulse, which
rejects it, because we are already in a pulse state. This rejection
executes a callback, which removes our 2nd pulsing notification from the
alerting entries.
This CL allows calling DozeTriggers.requestPulse multiple times for new
notifications, without executing their suppression callback.
Fixes: 335560575
Test: atest DozeTriggersTest
Test: setup AOD, receive 2 pulsing notifications, check if the 2nd one
shows up
Flag: com.android.systemui.notification_pulsing_fix
Change-Id: I5e04e9d76ddf5df54ff05512b6645af29b9f238e
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 034eba0..4311e79 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1074,3 +1074,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "notification_pulsing_fix"
+ namespace: "systemui"
+ description: "Allow showing new pulsing notifications when the device is already pulsing."
+ bug: "335560575"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 9311187..4a9f741 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -40,6 +40,7 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -564,6 +565,12 @@
return;
}
+ // When already in pulsing, we can show the new Notification without requesting a new pulse.
+ if (Flags.notificationPulsingFix()
+ && dozeState == State.DOZE_PULSING && reason == DozeLog.PULSE_REASON_NOTIFICATION) {
+ return;
+ }
+
if (!mAllowPulseTriggers || mDozeHost.isPulsePending()
|| !canPulse(dozeState, performedProxCheck)) {
if (!mAllowPulseTriggers) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 3a6b075..40b8fc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -21,12 +21,14 @@
import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -35,6 +37,7 @@
import android.app.StatusBarManager;
import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
@@ -43,6 +46,7 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -71,6 +75,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -85,6 +90,7 @@
private DozeHost mHost;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
+ private final AmbientDisplayConfiguration mConfig = DozeConfigurationUtil.createMockConfig();
@Mock
private DockManager mDockManager;
@Mock
@@ -105,6 +111,8 @@
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
private SessionTracker mSessionTracker;
+ @Captor
+ private ArgumentCaptor<DozeHost.Callback> mHostCallbackCaptor;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -116,7 +124,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
setupDozeTriggers(
- DozeConfigurationUtil.createMockConfig(),
+ mConfig,
DozeConfigurationUtil.createMockParameters());
}
@@ -174,10 +182,69 @@
}
@Test
+ public void testOnNotification_startsPulseRequest() {
+ // GIVEN device is dozing
+ Runnable pulseSuppressListener = mock(Runnable.class);
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+ doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
+ mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
+ mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+ clearInvocations(mMachine);
+
+ // WHEN receive an alerting notification
+ mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener);
+
+ // THEN entering to pulse
+ verify(mHost).setPulsePending(true);
+ // AND suppress listeners are NOT notified
+ verify(pulseSuppressListener, never()).run();
+ }
+
+ @Test
+ public void testOnNotification_cannotPulse_notificationSuppressed() {
+ // GIVEN device is dozing
+ Runnable pulseSuppressListener = mock(Runnable.class);
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+ doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
+ mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
+ mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+ clearInvocations(mMachine);
+ // AND pulsing is disabled
+ when(mConfig.pulseOnNotificationEnabled(anyInt())).thenReturn(false);
+
+ // WHEN receive an alerting notification
+ mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener);
+
+ // THEN NOT starting pulse
+ verify(mHost, never()).setPulsePending(anyBoolean());
+ // AND the notification is suppressed
+ verify(pulseSuppressListener).run();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_PULSING_FIX)
+ public void testOnNotification_alreadyPulsing_notificationNotSuppressed() {
+ // GIVEN device is pulsing
+ Runnable pulseSuppressListener = mock(Runnable.class);
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING);
+ doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
+ mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
+ mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE_PULSING);
+ clearInvocations(mMachine);
+
+ // WHEN receive an alerting notification
+ mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener);
+
+ // THEN entering to pulse
+ verify(mHost, never()).setPulsePending(anyBoolean());
+ // AND suppress listeners are NOT notified
+ verify(pulseSuppressListener, never()).run();
+ }
+
+ @Test
public void testOnNotification_noPulseIfPulseIsNotPendingAnymore() {
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
- ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class);
- doAnswer(invocation -> null).when(mHost).addCallback(captor.capture());
+ doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
@@ -189,7 +256,7 @@
// WHEN prox check returns FAR
mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2));
- captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
+ mHostCallbackCaptor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
mProximitySensor.alertListeners();
// THEN don't request pulse because the pending pulse was abandoned early