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

Commit 8036c4fa authored by Julia Tuttle's avatar Julia Tuttle
Browse files

Convert HeadsUpManager et al to use an Executor

Note that BaseHeadsUpManager still needs a Handler to register its
ContentObserver to observe the snooze length setting.

Bug: 315192399
Test: atest AlertingNotificationManagerTest
Test: atest BaseHeadsUpManagerTest
Test: atest HeadsUpManagerPhoneTest
Flag: NA
Change-Id: I01c2bf31c88f58dc2aee207845abc61e2611bf97
parent 32247a09
Loading
Loading
Loading
Loading
+48 −17
Original line number Diff line number Diff line
@@ -18,16 +18,16 @@ package com.android.systemui.statusbar;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;

import java.util.stream.Stream;
@@ -46,13 +46,12 @@ public abstract class AlertingNotificationManager {
    protected int mMinimumDisplayTime;
    protected int mStickyForSomeTimeAutoDismissTime;
    protected int mAutoDismissTime;
    @VisibleForTesting
    public Handler mHandler;
    private DelayableExecutor mExecutor;

    public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler,
            SystemClock systemClock) {
    public AlertingNotificationManager(HeadsUpManagerLogger logger,
            SystemClock systemClock, @Main DelayableExecutor executor) {
        mLogger = logger;
        mHandler = handler;
        mExecutor = executor;
        mSystemClock = systemClock;
    }

@@ -264,6 +263,7 @@ public abstract class AlertingNotificationManager {
        public long mEarliestRemovalTime;

        @Nullable protected Runnable mRemoveAlertRunnable;
        @Nullable private Runnable mCancelRemoveAlertRunnable;

        public void setEntry(@NonNull final NotificationEntry entry) {
            setEntry(entry, () -> removeAlertEntry(entry.getKey()));
@@ -291,13 +291,15 @@ public abstract class AlertingNotificationManager {
            if (updatePostTime) {
                mPostTime = Math.max(mPostTime, now);
            }
            removeAutoRemovalCallbacks("updateEntry (will be rescheduled)");

            if (!isSticky()) {
            if (isSticky()) {
                removeAutoRemovalCallbacks("updateEntry (sticky)");
                return;
            }

            final long finishTime = calculateFinishTime();
            final long timeLeft = Math.max(finishTime - now, mMinimumDisplayTime);
                mHandler.postDelayed(mRemoveAlertRunnable, timeLeft);
            }
            scheduleAutoRemovalCallback(timeLeft, "updateEntry (not sticky)");
        }

        /**
@@ -340,21 +342,50 @@ public abstract class AlertingNotificationManager {
         * Clear any pending removal runnables.
         */
        public void removeAutoRemovalCallbacks(@Nullable String reason) {
            if (mRemoveAlertRunnable != null) {
            final boolean removed = removeAutoRemovalCallbackInternal();

            if (removed) {
                mLogger.logAutoRemoveCanceled(mEntry, reason);
                mHandler.removeCallbacks(mRemoveAlertRunnable);
            }
        }

        private void scheduleAutoRemovalCallback(long delayMillis, @NonNull String reason) {
            if (mRemoveAlertRunnable == null) {
                Log.wtf(TAG, "scheduleAutoRemovalCallback with no callback set");
                return;
            }

            final boolean removed = removeAutoRemovalCallbackInternal();

            if (removed) {
                mLogger.logAutoRemoveRescheduled(mEntry, delayMillis, reason);
            } else {
                mLogger.logAutoRemoveScheduled(mEntry, delayMillis, reason);
            }


            mCancelRemoveAlertRunnable = mExecutor.executeDelayed(mRemoveAlertRunnable,
                    delayMillis);
        }

        private boolean removeAutoRemovalCallbackInternal() {
            final boolean scheduled = (mCancelRemoveAlertRunnable != null);

            if (scheduled) {
                mCancelRemoveAlertRunnable.run();
                mCancelRemoveAlertRunnable = null;
            }

            return scheduled;
        }

        /**
         * Remove the alert at the earliest allowed removal time.
         */
        public void removeAsSoonAsPossible() {
            if (mRemoveAlertRunnable != null) {
                removeAutoRemovalCallbacks("removeAsSoonAsPossible (will be rescheduled)");

                final long timeLeft = mEarliestRemovalTime - mSystemClock.elapsedRealtime();
                mHandler.postDelayed(mRemoveAlertRunnable, timeLeft);
                scheduleAutoRemovalCallback(timeLeft, "removeAsSoonAsPossible");
            }
        }

+4 −2
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
@@ -119,12 +120,13 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp
            @Main Handler handler,
            GlobalSettings globalSettings,
            SystemClock systemClock,
            @Main DelayableExecutor executor,
            AccessibilityManagerWrapper accessibilityManagerWrapper,
            UiEventLogger uiEventLogger,
            JavaAdapter javaAdapter,
            ShadeInteractor shadeInteractor) {
        super(context, logger, handler, globalSettings, systemClock, accessibilityManagerWrapper,
                uiEventLogger);
        super(context, logger, handler, globalSettings, systemClock, executor,
                accessibilityManagerWrapper, uiEventLogger);
        Resources resources = mContext.getResources();
        mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
        statusBarStateController.addCallback(mStatusBarStateListener);
+3 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;

@@ -87,9 +88,10 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp
            @Main Handler handler,
            GlobalSettings globalSettings,
            SystemClock systemClock,
            @Main DelayableExecutor executor,
            AccessibilityManagerWrapper accessibilityManagerWrapper,
            UiEventLogger uiEventLogger) {
        super(logger, handler, systemClock);
        super(logger, systemClock, executor);
        mContext = context;
        mAccessibilityMgr = accessibilityManagerWrapper;
        mUiEventLogger = uiEventLogger;
+20 −0
Original line number Diff line number Diff line
@@ -66,6 +66,26 @@ class HeadsUpManagerLogger @Inject constructor(
        })
    }

    fun logAutoRemoveScheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
        buffer.log(TAG, INFO, {
            str1 = entry.logKey
            long1 = delayMillis
            str2 = reason
        }, {
            "schedule auto remove of $str1 in $long1 ms reason: $str2"
        })
    }

    fun logAutoRemoveRescheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
        buffer.log(TAG, INFO, {
            str1 = entry.logKey
            long1 = delayMillis
            str2 = reason
        }, {
            "reschedule auto remove of $str1 in $long1 ms reason: $str2"
        })
    }

    fun logAutoRemoveCanceled(entry: NotificationEntry, reason: String?) {
        buffer.log(TAG, INFO, {
            str1 = entry.logKey
+14 −54
Original line number Diff line number Diff line
@@ -30,8 +30,6 @@ import static org.mockito.Mockito.spy;

import android.app.ActivityManager;
import android.app.Notification;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
@@ -45,12 +43,12 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,27 +71,24 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
    protected static final int TEST_STICKY_AUTO_DISMISS_TIME = 800;
    // Number of notifications to use in tests requiring multiple notifications
    private static final int TEST_NUM_NOTIFICATIONS = 4;
    protected static final int TEST_TIMEOUT_TIME = 2_000;
    protected final Runnable mTestTimeoutRunnable = () -> mTimedOut = true;

    protected Handler mTestHandler;
    protected final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
    protected final SystemClock mSystemClock = new SystemClockImpl();
    protected boolean mTimedOut = false;
    protected final FakeSystemClock mSystemClock = new FakeSystemClock();
    protected final FakeExecutor mExecutor = new FakeExecutor(mSystemClock);

    @Mock protected ExpandableNotificationRow mRow;

    static {
        assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
        assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
        assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME);
    }

    private static class TestableAlertingNotificationManager extends AlertingNotificationManager {
        private AlertEntry mLastCreatedEntry;

        private TestableAlertingNotificationManager(Handler handler, SystemClock systemClock) {
            super(new HeadsUpManagerLogger(logcatLogBuffer()), handler, systemClock);
        private TestableAlertingNotificationManager(SystemClock systemClock,
                DelayableExecutor executor) {
            super(new HeadsUpManagerLogger(logcatLogBuffer()), systemClock, executor);
            mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
            mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
            mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME;
@@ -118,7 +113,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
    }

    protected AlertingNotificationManager createAlertingNotificationManager() {
        return new TestableAlertingNotificationManager(mTestHandler, mSystemClock);
        return new TestableAlertingNotificationManager(mSystemClock, mExecutor);
    }

    protected StatusBarNotification createSbn(int id, Notification n) {
@@ -155,35 +150,6 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
        return new NotificationEntryBuilder().setSbn(createSbn(id)).build();
    }

    protected void verifyAlertingAtTime(AlertingNotificationManager anm, NotificationEntry entry,
            boolean shouldBeAlerting, int whenToCheckAlertingMillis, String whenCondition) {
        final Boolean[] wasAlerting = {null};
        final Runnable checkAlerting =
                () -> wasAlerting[0] = anm.isAlerting(entry.getKey());

        mTestHandler.postDelayed(checkAlerting, whenToCheckAlertingMillis);
        mTestHandler.postDelayed(mTestTimeoutRunnable, TEST_TIMEOUT_TIME);
        TestableLooper.get(this).processMessages(2);

        assertFalse("Test timed out", mTimedOut);
        if (shouldBeAlerting) {
            assertTrue("Should still be alerting after " + whenCondition, wasAlerting[0]);
        } else {
            assertFalse("Should not still be alerting after " + whenCondition, wasAlerting[0]);
        }
        assertFalse("Should not still be alerting after processing",
                anm.isAlerting(entry.getKey()));
    }

    @Before
    public void setUp() {
        mTestHandler = Handler.createAsync(Looper.myLooper());
    }

    @After
    public void tearDown() {
        mTestHandler.removeCallbacksAndMessages(null);
    }

    @Test
    public void testShowNotification_addsEntry() {
@@ -203,9 +169,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
        final NotificationEntry entry = createEntry(/* id = */ 0);

        alm.showNotification(entry);

        verifyAlertingAtTime(alm, entry, false, TEST_AUTO_DISMISS_TIME * 3 / 2,
                "auto dismiss time");
        mSystemClock.advanceTime(TEST_AUTO_DISMISS_TIME * 3 / 2);

        assertFalse(alm.isAlerting(entry.getKey()));
    }
@@ -217,10 +181,8 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {

        alm.showNotification(entry);

        // Try to remove but defer, since the notification has not been shown long enough.
        final boolean removedImmediately = alm.removeNotification(entry.getKey(),
                false /* releaseImmediately */);

        final boolean removedImmediately = alm.removeNotification(
                entry.getKey(), /* releaseImmediately = */ false);
        assertFalse(removedImmediately);
        assertTrue(alm.isAlerting(entry.getKey()));
    }
@@ -232,10 +194,8 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {

        alm.showNotification(entry);

        // Remove forcibly with releaseImmediately = true.
        final boolean removedImmediately = alm.removeNotification(entry.getKey(),
                true /* releaseImmediately */);

        final boolean removedImmediately = alm.removeNotification(
                entry.getKey(), /* releaseImmediately = */ true);
        assertTrue(removedImmediately);
        assertFalse(alm.isAlerting(entry.getKey()));
    }
Loading