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

Commit a7c21be2 authored by Kevin Han's avatar Kevin Han
Browse files

Dispatch bind pipeline at end of UI thread handler

To prevent constantly starting and stopping async tasks in the inflation
pipeline when one change causes multiple rebind requests, we push the
actual starting of the bind pipeline onto the UI thread to be handled
after all of the current message is finished. Now, multiple rebind
requests simply put ONE message on the NotifBindPipeline handler to
dispatch the pipeline.

Bug: 150719232
Test: atest SystemUITests
Change-Id: I92ee530ad5513f3ca015e82c4935e9e5693365b4
parent b0fefa4c
Loading
Loading
Loading
Loading
+53 −4
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.statusbar.notification.row;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.widget.FrameLayout;
@@ -25,6 +28,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.CancellationSignal;

import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -78,14 +82,17 @@ public final class NotifBindPipeline {
    private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>();
    private final NotifBindPipelineLogger mLogger;
    private final List<BindCallback> mScratchCallbacksList = new ArrayList<>();
    private final Handler mMainHandler;
    private BindStage mStage;

    @Inject
    NotifBindPipeline(
            CommonNotifCollection collection,
            NotifBindPipelineLogger logger) {
            NotifBindPipelineLogger logger,
            @Main Looper mainLooper) {
        collection.addCollectionListener(mCollectionListener);
        mLogger = logger;
        mMainHandler = new NotifBindPipelineHandler(mainLooper);
    }

    /**
@@ -110,7 +117,7 @@ public final class NotifBindPipeline {
        final BindEntry bindEntry = getBindEntry(entry);
        bindEntry.row = row;
        if (bindEntry.invalidated) {
            startPipeline(entry);
            requestPipelineRun(entry);
        }
    }

@@ -133,7 +140,28 @@ public final class NotifBindPipeline {
            signal.setOnCancelListener(() -> callbacks.remove(callback));
        }

        startPipeline(entry);
        requestPipelineRun(entry);
    }

    /**
     * Request pipeline to start.
     *
     * We avoid starting the pipeline immediately as multiple clients may request rebinds
     * back-to-back due to a single change (e.g. notification update), and it's better to start
     * the real work once rather than repeatedly start and cancel it.
     */
    private void requestPipelineRun(NotificationEntry entry) {
        mLogger.logRequestPipelineRun(entry.getKey());

        final BindEntry bindEntry = getBindEntry(entry);

        // Abort any existing pipeline run
        mStage.abortStage(entry, bindEntry.row);

        if (!mMainHandler.hasMessages(START_PIPELINE_MSG, entry)) {
            Message msg = Message.obtain(mMainHandler, START_PIPELINE_MSG, entry);
            mMainHandler.sendMessage(msg);
        }
    }

    /**
@@ -154,7 +182,6 @@ public final class NotifBindPipeline {
            return;
        }

        mStage.abortStage(entry, row);
        mStage.executeStage(entry, row, (en) -> onPipelineComplete(en));
    }

@@ -191,6 +218,7 @@ public final class NotifBindPipeline {
                mStage.abortStage(entry, row);
            }
            mStage.deleteStageParams(entry);
            mMainHandler.removeMessages(START_PIPELINE_MSG, entry);
        }
    };

@@ -219,4 +247,25 @@ public final class NotifBindPipeline {
        public final Set<BindCallback> callbacks = new ArraySet<>();
        public boolean invalidated;
    }

    private static final int START_PIPELINE_MSG = 1;

    private class NotifBindPipelineHandler extends Handler {

        NotifBindPipelineHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case START_PIPELINE_MSG:
                    NotificationEntry entry = (NotificationEntry) msg.obj;
                    startPipeline(entry);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown message type: " + msg.what);
            }
        }
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -40,6 +40,14 @@ class NotifBindPipelineLogger @Inject constructor(
        })
    }

    fun logRequestPipelineRun(notifKey: String) {
        buffer.log(TAG, INFO, {
            str1 = notifKey
        }, {
            "Request pipeline run for notif: $str1"
        })
    }

    fun logStartPipeline(notifKey: String) {
        buffer.log(TAG, INFO, {
            str1 = notifKey
+6 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;

import androidx.test.annotation.UiThreadTest;
@@ -52,7 +53,11 @@ public class ExpandHelperTest extends SysuiTestCase {
        mDependency.injectMockDependency(NotificationMediaManager.class);
        allowTestableLooperAsMainThread();
        Context context = getContext();
        mRow = new NotificationTestHelper(context, mDependency).createRow();
        NotificationTestHelper helper = new NotificationTestHelper(
                mContext,
                mDependency,
                TestableLooper.get(this));
        mRow = helper.createRow();
        mCallback = mock(ExpandHelper.Callback.class);
        mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
    }
+4 −1
Original line number Diff line number Diff line
@@ -223,7 +223,10 @@ public class BubbleControllerTest extends SysuiTestCase {
        mNotificationShadeWindowController.attach();

        // Need notifications for bubbles
        mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
        mNotificationTestHelper = new NotificationTestHelper(
                mContext,
                mDependency,
                TestableLooper.get(this));
        mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
        mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
        mNonBubbleNotifRow = mNotificationTestHelper.createRow();
+4 −1
Original line number Diff line number Diff line
@@ -109,7 +109,10 @@ public class BubbleDataTest extends SysuiTestCase {

    @Before
    public void setUp() throws Exception {
        mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
        mNotificationTestHelper = new NotificationTestHelper(
                mContext,
                mDependency,
                TestableLooper.get(this));
        MockitoAnnotations.initMocks(this);

        mEntryA1 = createBubbleEntry(1, "a1", "package.a");
Loading