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

Commit e7b1be60 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[NavBar Jank] Avoid calling RotationPolicy.isRotationLocked() on main thread" into main

parents c6493c39 ad4442fb
Loading
Loading
Loading
Loading
+15 −25
Original line number Diff line number Diff line
@@ -53,6 +53,9 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;

import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -142,12 +145,14 @@ public class RotationButtonController {
    };

    private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() {
        @WorkerThread
        @Override
        public void onRotationChanged(final int rotation) {
            @Nullable Boolean rotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
            // We need this to be scheduled as early as possible to beat the redrawing of
            // window in response to the orientation change.
            mMainThreadHandler.postAtFrontOfQueue(() -> {
                onRotationWatcherChanged(rotation);
                onRotationWatcherChanged(rotation, rotationLocked);
            });
        }
    };
@@ -281,8 +286,8 @@ public class RotationButtonController {
        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
    }

    public void setRotationLockedAtAngle(int rotationSuggestion, String caller) {
        final Boolean isLocked = isRotationLocked();
    public void setRotationLockedAtAngle(
            @Nullable Boolean isLocked, int rotationSuggestion, String caller) {
        if (isLocked == null) {
            // Ignore if we can't read the setting for the current user
            return;
@@ -291,21 +296,6 @@ public class RotationButtonController {
                /* rotation= */ rotationSuggestion, caller);
    }

    /**
     * @return whether rotation is currently locked, or <code>null</code> if the setting couldn't
     *         be read
     */
    public Boolean isRotationLocked() {
        try {
            return RotationPolicy.isRotationLocked(mContext);
        } catch (SecurityException e) {
            // TODO(b/279561841): RotationPolicy uses the current user to resolve the setting which
            //                    may change before the rotation watcher can be unregistered
            Log.e(TAG, "Failed to get isRotationLocked", e);
            return null;
        }
    }

    public void setRotateSuggestionButtonState(boolean visible) {
        setRotateSuggestionButtonState(visible, false /* force */);
    }
@@ -469,7 +459,7 @@ public class RotationButtonController {
     * Called when the rotation watcher rotation changes, either from the watcher registered
     * internally in this class, or a signal propagated from NavBarHelper.
     */
    public void onRotationWatcherChanged(int rotation) {
    public void onRotationWatcherChanged(int rotation, @Nullable Boolean isRotationLocked) {
        if (!mListenersRegistered) {
            // Ignore if not registered
            return;
@@ -477,17 +467,16 @@ public class RotationButtonController {

        // If the screen rotation changes while locked, potentially update lock to flow with
        // new screen rotation and hide any showing suggestions.
        Boolean rotationLocked = isRotationLocked();
        if (rotationLocked == null) {
        if (isRotationLocked == null) {
            // Ignore if we can't read the setting for the current user
            return;
        }
        // The isVisible check makes the rotation button disappear when we are not locked
        // (e.g. for tabletop auto-rotate).
        if (rotationLocked || mRotationButton.isVisible()) {
        if (isRotationLocked || mRotationButton.isVisible()) {
            // Do not allow a change in rotation to set user rotation when docked.
            if (shouldOverrideUserLockPrefs(rotation) && rotationLocked && !mDocked) {
                setRotationLockedAtAngle(rotation, /* caller= */
            if (shouldOverrideUserLockPrefs(rotation) && isRotationLocked && !mDocked) {
                setRotationLockedAtAngle(true, rotation, /* caller= */
                        "RotationButtonController#onRotationWatcherChanged");
            }
            setRotateSuggestionButtonState(false /* visible */, true /* forced */);
@@ -592,7 +581,8 @@ public class RotationButtonController {
    private void onRotateSuggestionClick(View v) {
        mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
        incrementNumAcceptedRotationSuggestionsIfNeeded();
        setRotationLockedAtAngle(mLastRotationSuggestion,
        setRotationLockedAtAngle(
                RotationPolicyUtil.isRotationLocked(mContext), mLastRotationSuggestion,
                /* caller= */ "RotationButtonController#onRotateSuggestionClick");
        Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion);
        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+44 −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.shared.rotation

import android.content.Context
import android.util.Log
import com.android.internal.view.RotationPolicy

class RotationPolicyUtil {
    companion object {
        /**
         * Recommend to be called on bg thread, or reuse the results. It's because
         * [RotationPolicy.isRotationLocked] may make a binder call to query settings.
         *
         * @return whether rotation is currently locked, or <code>null</code> if the setting
         *   couldn't be read
         */
        @JvmStatic
        fun isRotationLocked(context: Context): Boolean? {
            try {
                return RotationPolicy.isRotationLocked(context)
            } catch (e: SecurityException) {
                // TODO(b/279561841): RotationPolicy uses the current user to resolve the setting
                // which may change before the rotation watcher can be unregistered
                Log.e("RotationPolicy", "Failed to get isRotationLocked", e)
                return null
            }
        }
    }
}
+21 −6
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;

import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.systemui.Dumpable;
@@ -64,12 +66,14 @@ import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.rotation.RotationPolicyUtil;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -108,6 +112,7 @@ public final class NavBarHelper implements

    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private final Executor mMainExecutor;
    private final Handler mBgHandler;
    private final AccessibilityManager mAccessibilityManager;
    private final Lazy<AssistManager> mAssistManagerLazy;
    private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -160,13 +165,15 @@ public final class NavBarHelper implements

    // Listens for changes to display rotation
    private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
        @WorkerThread
        @Override
        public void onRotationChanged(final int rotation) {
            // We need this to be scheduled as early as possible to beat the redrawing of
            // window in response to the orientation change.
            @Nullable Boolean isRotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
            mHandler.postAtFrontOfQueue(() -> {
                mRotationWatcherRotation = rotation;
                dispatchRotationChanged(rotation);
                dispatchRotationChanged(rotation, isRotationLocked);
            });
        }
    };
@@ -194,7 +201,8 @@ public final class NavBarHelper implements
            ConfigurationController configurationController,
            DumpManager dumpManager,
            CommandQueue commandQueue,
            @Main Executor mainExecutor) {
            @Main Executor mainExecutor,
            @Background Handler bgHandler) {
        // b/319489709: This component shouldn't be running for a non-primary user
        if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) {
            Log.wtf(TAG, "Unexpected initialization for non-primary user", new Throwable());
@@ -215,6 +223,7 @@ public final class NavBarHelper implements
        mDefaultDisplayId = displayTracker.getDefaultDisplayId();
        mEdgeBackGestureHandler = edgeBackGestureHandlerFactory.create(context);
        mMainExecutor = mainExecutor;
        mBgHandler = bgHandler;

        mNavBarMode = navigationModeController.addListener(this);
        mCommandQueue.addCallback(this);
@@ -322,7 +331,13 @@ public final class NavBarHelper implements
            listener.updateAssistantAvailable(mAssistantAvailable, mLongPressHomeEnabled);
        }
        listener.updateWallpaperVisibility(mWallpaperVisible, mDefaultDisplayId);
        listener.updateRotationWatcherState(mRotationWatcherRotation);

        mBgHandler.post(() -> {
            Boolean isRotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
            mMainExecutor.execute(
                    () -> listener.updateRotationWatcherState(
                            mRotationWatcherRotation, isRotationLocked));
        });
    }

    /**
@@ -526,9 +541,9 @@ public final class NavBarHelper implements
        }
    }

    private void dispatchRotationChanged(int rotation) {
    private void dispatchRotationChanged(int rotation, @Nullable Boolean isRotationLocked) {
        for (NavbarTaskbarStateUpdater listener : mStateListeners) {
            listener.updateRotationWatcherState(rotation);
            listener.updateRotationWatcherState(rotation, isRotationLocked);
        }
    }

@@ -544,7 +559,7 @@ public final class NavBarHelper implements
        void updateAccessibilityServicesState();
        void updateAssistantAvailable(boolean available, boolean longPressHomeEnabled);
        default void updateWallpaperVisibility(boolean visible, int displayId) {}
        default void updateRotationWatcherState(int rotation) {}
        default void updateRotationWatcherState(int rotation, @Nullable Boolean isRotationLocked) {}
    }

    /** Data class to help Taskbar/Navbar initiate state correctly when switching between the two.*/
+8 −4
Original line number Diff line number Diff line
@@ -136,6 +136,7 @@ import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.rotation.RotationPolicyUtil;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -366,9 +367,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
                }

                @Override
                public void updateRotationWatcherState(int rotation) {
                public void updateRotationWatcherState(
                        int rotation, @Nullable Boolean isRotationLocked) {
                    if (mIsOnDefaultDisplay && mView != null) {
                        mView.getRotationButtonController().onRotationWatcherChanged(rotation);
                        RotationButtonController controller = mView.getRotationButtonController();
                        controller.onRotationWatcherChanged(rotation, isRotationLocked);
                        if (mView.needsReorient(rotation)) {
                            repositionNavigationBar(rotation);
                        }
@@ -819,8 +822,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements

            // Reset user rotation pref to match that of the WindowManager if starting in locked
            // mode. This will automatically happen when switching from auto-rotate to locked mode.
            if (display != null && rotationButtonController.isRotationLocked()) {
                rotationButtonController.setRotationLockedAtAngle(
            @Nullable Boolean isRotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
            if (display != null && isRotationLocked) {
                rotationButtonController.setRotationLockedAtAngle(isRotationLocked,
                        display.getRotation(), /* caller= */ "NavigationBar#onViewAttached");
            }
        } else {
+11 −2
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.res.Configuration;
import android.os.Handler;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;

@@ -65,6 +66,8 @@ import dagger.Lazy;
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;

@@ -120,6 +123,10 @@ public class NavBarHelperTest extends SysuiTestCase {
    EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
    @Mock
    NotificationShadeWindowController mNotificationShadeWindowController;
    @Mock
    Handler mBgHandler;

    @Captor ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
    ConfigurationController mConfigurationController = new FakeConfigurationController();

    private AccessibilityManager.AccessibilityServicesStateChangeListener
@@ -149,7 +156,7 @@ public class NavBarHelperTest extends SysuiTestCase {
                () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
                mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
                mDisplayTracker, mNotificationShadeWindowController, mConfigurationController,
                mDumpManager, mCommandQueue, mSynchronousExecutor);
                mDumpManager, mCommandQueue, mSynchronousExecutor, mBgHandler);
    }

    @Test
@@ -203,8 +210,10 @@ public class NavBarHelperTest extends SysuiTestCase {
                .updateAccessibilityServicesState();
        verify(mNavbarTaskbarStateUpdater, times(1))
                .updateAssistantAvailable(anyBoolean(), anyBoolean());
        verify(mBgHandler).post(mRunnableArgumentCaptor.capture());
        mRunnableArgumentCaptor.getValue().run();
        verify(mNavbarTaskbarStateUpdater, times(1))
                .updateRotationWatcherState(anyInt());
                .updateRotationWatcherState(anyInt(), anyBoolean());
        verify(mNavbarTaskbarStateUpdater, times(1))
                .updateWallpaperVisibility(anyBoolean(), anyInt());
    }
Loading