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

Commit a379689f authored by Peter Kalauskas's avatar Peter Kalauskas
Browse files

Separate thread for back panel

Create a new thread for showing the navigation back panel.

Bug: 304583132
Flag: com.android.systemui.edge_back_gesture_handler_thread
Test: atest SystemUITests:com.android.systemui.navigationbar.gestural
Change-Id: I97b8a67e4fbe07040a7113a83a57cc83dedb8f5c
parent 328a9f17
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -79,6 +79,21 @@ public class Assert {
        }
    }

    /**
     * Asserts that the current thread is the same as the given thread, or that the current thread
     * is the test thread.
     * @param expected The looper we expected to be running on
     */
    public static void isCurrentThread(Looper expected) {
        if (!expected.isCurrentThread()
                && (sTestThread == null || sTestThread != Thread.currentThread())) {
            throw new IllegalStateException("Called on wrong thread thread."
                    + " wanted " + expected.getThread().getName()
                    + " but instead got Thread.currentThread()="
                    + Thread.currentThread().getName());
        }
    }

    public static void isNotMainThread() {
        if (sMainLooper.isCurrentThread()
                && (sTestThread == null || sTestThread == Thread.currentThread())) {
+9 −9
Original line number Diff line number Diff line
@@ -36,11 +36,12 @@ import androidx.dynamicanimation.animation.DynamicAnimation
import com.android.internal.jank.Cuj
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.NavigationEdgeBackPlugin
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
import com.android.systemui.util.concurrency.BackPanelUiThread
import com.android.systemui.util.concurrency.UiThreadContext
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
@@ -84,11 +85,11 @@ internal constructor(
    context: Context,
    private val windowManager: WindowManager,
    private val viewConfiguration: ViewConfiguration,
    @Main private val mainHandler: Handler,
    private val mainHandler: Handler,
    private val systemClock: SystemClock,
    private val vibratorHelper: VibratorHelper,
    private val configurationController: ConfigurationController,
    private val latencyTracker: LatencyTracker,
    latencyTracker: LatencyTracker,
    private val interactionJankMonitor: InteractionJankMonitor,
) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {

@@ -103,7 +104,7 @@ internal constructor(
    constructor(
        private val windowManager: WindowManager,
        private val viewConfiguration: ViewConfiguration,
        @Main private val mainHandler: Handler,
        @BackPanelUiThread private val uiThreadContext: UiThreadContext,
        private val systemClock: SystemClock,
        private val vibratorHelper: VibratorHelper,
        private val configurationController: ConfigurationController,
@@ -112,20 +113,19 @@ internal constructor(
    ) {
        /** Construct a [BackPanelController]. */
        fun create(context: Context): BackPanelController {
            val backPanelController =
                BackPanelController(
            uiThreadContext.isCurrentThread()
            return BackPanelController(
                    context,
                    windowManager,
                    viewConfiguration,
                    mainHandler,
                    uiThreadContext.handler,
                    systemClock,
                    vibratorHelper,
                    configurationController,
                    latencyTracker,
                    interactionJankMonitor
                )
            backPanelController.init()
            return backPanelController
                .also { it.init() }
        }
    }

+64 −66
Original line number Diff line number Diff line
@@ -44,8 +44,6 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.icu.text.SimpleDateFormat;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -55,7 +53,6 @@ import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Choreographer;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
import android.view.InputDevice;
@@ -75,7 +72,6 @@ import androidx.annotation.DimenRes;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
@@ -94,7 +90,8 @@ import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.BackPanelUiThread;
import com.android.systemui.util.concurrency.UiThreadContext;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.pip.Pip;
@@ -136,7 +133,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
                public void onSystemGestureExclusionChanged(int displayId,
                        Region systemGestureExclusion, Region unrestrictedOrNull) {
                    if (displayId == mDisplayId) {
                        mMainExecutor.execute(() -> {
                        mUiThreadContext.getExecutor().execute(() -> {
                            mExcludeRegion.set(systemGestureExclusion);
                            mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
                                    ? unrestrictedOrNull : systemGestureExclusion);
@@ -215,8 +212,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
    private final Point mDisplaySize = new Point();
    private final int mDisplayId;

    private final Executor mMainExecutor;
    private final Handler mMainHandler;
    private final UiThreadContext mUiThreadContext;
    private final Executor mBackgroundExecutor;

    private final Rect mPipExcludedBounds = new Rect();
@@ -411,8 +407,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
            OverviewProxyService overviewProxyService,
            SysUiState sysUiState,
            PluginManager pluginManager,
            @Main Executor executor,
            @Main Handler handler,
            @BackPanelUiThread UiThreadContext uiThreadContext,
            @Background Executor backgroundExecutor,
            UserTracker userTracker,
            NavigationModeController navigationModeController,
@@ -428,8 +423,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
            Provider<LightBarController> lightBarControllerProvider) {
        mContext = context;
        mDisplayId = context.getDisplayId();
        mMainExecutor = executor;
        mMainHandler = handler;
        mUiThreadContext = uiThreadContext;
        mBackgroundExecutor = backgroundExecutor;
        mUserTracker = userTracker;
        mOverviewProxyService = overviewProxyService;
@@ -478,7 +472,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
                ViewConfiguration.getLongPressTimeout());

        mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
                mMainHandler, mContext, this::onNavigationSettingsChanged);
                mUiThreadContext.getHandler(), mContext, this::onNavigationSettingsChanged);

        updateCurrentUserResources();
    }
@@ -564,13 +558,15 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
        mIsAttached = true;
        mOverviewProxyService.addCallback(mQuickSwitchListener);
        mSysUiState.addCallback(mSysUiStateCallback);
        mInputManager.registerInputDeviceListener(mInputDeviceListener, mMainHandler);
        mInputManager.registerInputDeviceListener(
                mInputDeviceListener,
                mUiThreadContext.getHandler());
        int[] inputDevices = mInputManager.getInputDeviceIds();
        for (int inputDeviceId : inputDevices) {
            mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
        }
        updateIsEnabled();
        mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
        mUserTracker.addCallback(mUserChangedCallback, mUiThreadContext.getExecutor());
    }

    /**
@@ -617,6 +613,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
    }

    private void updateIsEnabled() {
        mUiThreadContext.runWithScissors(this::updateIsEnabledInner);
    }

    private void updateIsEnabledInner() {
        try {
            Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");

@@ -661,12 +661,12 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
                TaskStackChangeListeners.getInstance().registerTaskStackListener(
                        mTaskStackListener);
                DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                        mMainExecutor::execute, mOnPropertiesChangedListener);
                        mUiThreadContext.getExecutor()::execute, mOnPropertiesChangedListener);
                mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(
                        mOnIsInPipStateChangedListener));
                mDesktopModeOptional.ifPresent(
                        dm -> dm.addDesktopGestureExclusionRegionListener(
                                mDesktopCornersChangedListener, mMainExecutor));
                                mDesktopCornersChangedListener, mUiThreadContext.getExecutor()));

                try {
                    mWindowManagerService.registerSystemGestureExclusionListener(
@@ -677,8 +677,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack

                // Register input event receiver
                mInputMonitor = new InputMonitorCompat("edge-swipe", mDisplayId);
                mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
                        Choreographer.getInstance(), this::onInputEvent);
                mInputEventReceiver = mInputMonitor.getInputReceiver(mUiThreadContext.getLooper(),
                        mUiThreadContext.getChoreographer(), this::onInputEvent);

                // Add a nav bar panel window
                resetEdgeBackPlugin();
@@ -773,7 +773,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
        mUseMLModel = newState;

        if (mUseMLModel) {
            Assert.isMainThread();
            mUiThreadContext.isCurrentThread();
            if (mMLModelIsLoading) {
                Log.d(TAG, "Model tried to load while already loading.");
                return;
@@ -804,12 +804,13 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
        }
        BackGestureTfClassifierProvider finalProvider = provider;
        Map<String, Integer> finalVocab = vocab;
        mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
        mUiThreadContext.getExecutor().execute(
                () -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
    }

    private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
            Map<String, Integer> vocab, float threshold) {
        Assert.isMainThread();
        mUiThreadContext.isCurrentThread();
        mMLModelIsLoading = false;
        if (!mUseMLModel) {
            // This can happen if the user disables Gesture Nav while the model is loading.
@@ -1291,7 +1292,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
        updateBackAnimationThresholds();
        if (mLightBarControllerProvider.get() != null) {
            mBackAnimation.setStatusBarCustomizer((appearance) -> {
                mMainExecutor.execute(() ->
                mUiThreadContext.getExecutor().execute(() ->
                        mLightBarControllerProvider.get()
                                .customizeStatusBarAppearance(appearance));
            });
@@ -1308,8 +1309,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
        private final OverviewProxyService mOverviewProxyService;
        private final SysUiState mSysUiState;
        private final PluginManager mPluginManager;
        private final Executor mExecutor;
        private final Handler mHandler;
        private final UiThreadContext mUiThreadContext;
        private final Executor mBackgroundExecutor;
        private final UserTracker mUserTracker;
        private final NavigationModeController mNavigationModeController;
@@ -1329,8 +1329,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
        public Factory(OverviewProxyService overviewProxyService,
                        SysUiState sysUiState,
                        PluginManager pluginManager,
                       @Main Executor executor,
                       @Main Handler handler,
                        @BackPanelUiThread UiThreadContext uiThreadContext,
                        @Background Executor backgroundExecutor,
                        UserTracker userTracker,
                        NavigationModeController navigationModeController,
@@ -1348,8 +1347,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
            mOverviewProxyService = overviewProxyService;
            mSysUiState = sysUiState;
            mPluginManager = pluginManager;
            mExecutor = executor;
            mHandler = handler;
            mUiThreadContext = uiThreadContext;
            mBackgroundExecutor = backgroundExecutor;
            mUserTracker = userTracker;
            mNavigationModeController = navigationModeController;
@@ -1367,13 +1365,13 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack

        /** Construct a {@link EdgeBackGestureHandler}. */
        public EdgeBackGestureHandler create(Context context) {
            return new EdgeBackGestureHandler(
            return mUiThreadContext.runWithScissors(
                    () -> new EdgeBackGestureHandler(
                            context,
                            mOverviewProxyService,
                            mSysUiState,
                            mPluginManager,
                    mExecutor,
                    mHandler,
                            mUiThreadContext,
                            mBackgroundExecutor,
                            mUserTracker,
                            mNavigationModeController,
@@ -1386,7 +1384,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
                            mDesktopModeOptional,
                            mFalsingManager,
                            mBackGestureTfClassifierProviderProvider,
                    mLightBarControllerProvider);
                            mLightBarControllerProvider));
        }
    }

+40 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Process
import android.view.Choreographer
import com.android.systemui.Dependency
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
@@ -31,6 +32,12 @@ import dagger.Module
import dagger.Provides
import java.util.concurrent.Executor
import javax.inject.Named
import javax.inject.Qualifier

@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class BackPanelUiThread

/** Dagger Module for classes found within the concurrent package. */
@Module
@@ -106,6 +113,39 @@ object SysUIConcurrencyModule {
        return looper
    }

    @Provides
    @SysUISingleton
    @BackPanelUiThread
    fun provideBackPanelUiThreadContext(
        @Main mainLooper: Looper,
        @Main mainHandler: Handler,
        @Main mainExecutor: Executor
    ): UiThreadContext {
        return if (Flags.edgeBackGestureHandlerThread()) {
            val thread =
                HandlerThread("BackPanelUiThread", Process.THREAD_PRIORITY_DISPLAY).apply {
                    start()
                    looper.setSlowLogThresholdMs(
                        LONG_SLOW_DISPATCH_THRESHOLD,
                        LONG_SLOW_DELIVERY_THRESHOLD
                    )
                }
            UiThreadContext(
                thread.looper,
                thread.threadHandler,
                thread.threadExecutor,
                thread.threadHandler.runWithScissors { Choreographer.getInstance() }
            )
        } else {
            UiThreadContext(
                mainLooper,
                mainHandler,
                mainExecutor,
                mainHandler.runWithScissors { Choreographer.getInstance() }
            )
        }
    }

    /**
     * Background Handler.
     *
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.util.concurrency

import android.os.Handler
import android.os.Looper
import android.view.Choreographer
import com.android.systemui.util.Assert
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicReference

private const val DEFAULT_TIMEOUT = 150L

class UiThreadContext(
    val looper: Looper,
    val handler: Handler,
    val executor: Executor,
    val choreographer: Choreographer
) {
    fun isCurrentThread() {
        Assert.isCurrentThread(looper)
    }

    fun <T> runWithScissors(block: () -> T): T {
        return handler.runWithScissors(block)
    }

    fun runWithScissors(block: Runnable) {
        handler.runWithScissors(block, DEFAULT_TIMEOUT)
    }
}

fun <T> Handler.runWithScissors(block: () -> T): T {
    val returnedValue = AtomicReference<T>()
    runWithScissors({ returnedValue.set(block()) }, DEFAULT_TIMEOUT)
    return returnedValue.get()!!
}