Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 48f2781a authored by András Kurucz's avatar András Kurucz
Browse files

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
parent d233902b
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1074,3 +1074,13 @@ flag {
    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
  }
}
+7 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting;
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 @@ public class DozeTriggers implements DozeMachine.Part {
            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) {
+71 −4
Original line number Diff line number Diff line
@@ -21,12 +21,14 @@ import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
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 static org.mockito.Mockito.when;
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 androidx.test.filters.SmallTest;

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.Before;
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 @@ public class DozeTriggersTest extends SysuiTestCase {
    private DozeHost mHost;
    @Mock
    private BroadcastDispatcher mBroadcastDispatcher;
    private final AmbientDisplayConfiguration mConfig = DozeConfigurationUtil.createMockConfig();
    @Mock
    private DockManager mDockManager;
    @Mock
@@ -105,6 +111,8 @@ public class DozeTriggersTest extends SysuiTestCase {
    private SelectedUserInteractor mSelectedUserInteractor;
    @Mock
    private SessionTracker mSessionTracker;
    @Captor
    private ArgumentCaptor<DozeHost.Callback> mHostCallbackCaptor;

    private DozeTriggers mTriggers;
    private FakeSensorManager mSensors;
@@ -116,7 +124,7 @@ public class DozeTriggersTest extends SysuiTestCase {
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        setupDozeTriggers(
                DozeConfigurationUtil.createMockConfig(),
                mConfig,
                DozeConfigurationUtil.createMockParameters());
    }

@@ -173,11 +181,70 @@ public class DozeTriggersTest extends SysuiTestCase {
        verify(mMachine).requestPulse(anyInt());
    }

    @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 @@ public class DozeTriggersTest extends SysuiTestCase {

        // 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