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

Commit fea97178 authored by Ana Krulec's avatar Ana Krulec
Browse files

WindowManager: Send layer priority hint to SurfaceFlinger

Basic algorithm:
if (!isFocused && preferredModeId > 0) -> priority is 2
if (isFocused && preferredModeId == 0) -> priority is 1
if (isFocused && preferredModeId > 0) -> priority is 0
else priority is INT_MAX

See go/sf-generalizing-refresh-rate for master doc, and
https://docs.google.com/document/d/1ozR7UT3AZclsJ6f4NJ4WXYO2eb-GRdv-Q55AYTltZSo
for WM specific document.

Test: Device starts. Observe logs for errors.
Test: Observe printouts on SF side to make sure the correct number gets passed along.
Test: Unit tests for setting priority on Transaction.
Test: Swappy and Messaging app running in split screen. If swappy in focus,
      it gets priority 0, otherwise 2. If Messsging app in focus it gets priority 1,
      otherwise INT_MAX.
Test: Chrome playing video. Chrome Activity gets priority 0.
Test: Expand status bar. Status bar has priority 1.
Test: Application running. Status bar gets an update. Nothing is passed along.

Bug: 142507166
Change-Id: Iea75f63882b173fad089cd4a32a4831b92206797
parent 18901e2f
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -122,6 +122,8 @@ public final class SurfaceControl implements Parcelable {
    private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
    private static native void nativeSetFlags(long transactionObj, long nativeObject,
            int flags, int mask);
    private static native void nativeSetFrameRateSelectionPriority(long transactionObj,
            long nativeObject, int priority);
    private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
            int l, int t, int r, int b);
    private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
@@ -2244,6 +2246,19 @@ public final class SurfaceControl implements Parcelable {
            }
        }

        /**
         * This information is passed to SurfaceFlinger to decide which window should have a
         * priority when deciding about the refresh rate of the display. All windows have the
         * lowest priority by default.
         * @hide
         */
        @NonNull
        public Transaction setFrameRateSelectionPriority(@NonNull SurfaceControl sc, int priority) {
            sc.checkNotReleased();
            nativeSetFrameRateSelectionPriority(mNativeObject, sc.mNativeObject, priority);
            return this;
        }

        /**
         * Request that a given surface and it's sub-tree be shown.
         *
+10 −0
Original line number Diff line number Diff line
@@ -423,6 +423,14 @@ static void nativeSetFlags(JNIEnv* env, jclass clazz, jlong transactionObj,
    transaction->setFlags(ctrl, flags, mask);
}

static void nativeSetFrameRateSelectionPriority(JNIEnv* env, jclass clazz, jlong transactionObj,
        jlong nativeObject, jint priority) {
    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);

    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
    transaction->setFrameRateSelectionPriority(ctrl, priority);
}

static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
        jlong nativeObject, jobject regionObj) {
    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -1362,6 +1370,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
            (void*)nativeSetColorSpaceAgnostic },
    {"nativeSetFlags", "(JJII)V",
            (void*)nativeSetFlags },
    {"nativeSetFrameRateSelectionPriority", "(JJI)V",
            (void*)nativeSetFrameRateSelectionPriority },
    {"nativeSetWindowCrop", "(JJIIII)V",
            (void*)nativeSetWindowCrop },
    {"nativeSetCornerRadius", "(JJF)V",
+45 −0
Original line number Diff line number Diff line
@@ -33,6 +33,27 @@ class RefreshRatePolicy {
    private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
    private final WindowManagerService mWmService;

    /**
     * The following constants represent priority of the window. SF uses this information when
     * deciding which window has a priority when deciding about the refresh rate of the screen.
     * Priority 0 is considered the highest priority. -1 means that the priority is unset.
     */
    static final int LAYER_PRIORITY_UNSET = -1;
    /** Windows that are in focus and voted for the preferred mode ID have the highest priority. */
    static final int LAYER_PRIORITY_FOCUSED_WITH_MODE = 0;
    /**
     * This is a default priority for all windows that are in focus, but have not requested a
     * specific mode ID.
     */
    static final int LAYER_PRIORITY_FOCUSED_WITHOUT_MODE = 1;
    /**
     * Windows that are not in focus, but voted for a specific mode ID should be
     * acknowledged by SF. For example, there are two applications in a split screen.
     * One voted for a given mode ID, and the second one doesn't care. Even though the
     * second one might be in focus, we can honor the mode ID of the first one.
     */
    static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2;

    RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
            HighRefreshRateBlacklist blacklist) {
        mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
@@ -92,4 +113,28 @@ class RefreshRatePolicy {
        }
        return 0;
    }

    /**
     * Calculate the priority based on whether the window is in focus and whether the application
     * voted for a specific refresh rate.
     *
     * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might
     * be useful in edge cases when we are deciding which layer should get priority when deciding
     * about the refresh rate.
     */
    int calculatePriority(WindowState w) {
        boolean isFocused = w.isFocused();
        int preferredModeId = getPreferredModeId(w);

        if (!isFocused && preferredModeId > 0) {
            return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE;
        }
        if (isFocused && preferredModeId == 0) {
            return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE;
        }
        if (isFocused && preferredModeId > 0) {
            return LAYER_PRIORITY_FOCUSED_WITH_MODE;
        }
        return LAYER_PRIORITY_UNSET;
    }
}
+27 −0
Original line number Diff line number Diff line
@@ -656,6 +656,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
    private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
    private KeyInterceptionInfo mKeyInterceptionInfo;

    /**
     * This information is passed to SurfaceFlinger to decide which window should have a priority
     * when deciding about the refresh rate of the display. All windows have the lowest priority by
     * default. The variable is cached, so we do not send too many updates to SF.
     */
    int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;

    /**
     * @return The insets state as requested by the client, i.e. the dispatched insets state
     *         for which the visibilities are overridden with what the client requested.
@@ -5169,6 +5176,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        }
    }


    /**
     * Notifies SF about the priority of the window, if it changed. SF then uses this information
     * to decide which window's desired rendering rate should have a priority when deciding about
     * the refresh rate of the screen. Priority
     * {@link RefreshRatePolicy#LAYER_PRIORITY_FOCUSED_WITH_MODE} is considered the highest.
     */
    @VisibleForTesting
    void updateFrameRateSelectionPriorityIfNeeded() {
        final int priority = getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
                .calculatePriority(this);
        if (mFrameRateSelectionPriority != priority) {
            mFrameRateSelectionPriority = priority;
            getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl,
                    mFrameRateSelectionPriority);
        }
    }

    @Override
    void prepareSurfaces() {
        final Dimmer dimmer = getDimmer();
@@ -5177,6 +5202,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            applyDims(dimmer);
        }
        updateSurfacePosition();
        // Send information to SufaceFlinger about the priority of the current window.
        updateFrameRateSelectionPriorityIfNeeded();

        mWinAnimator.prepareSurfaceLocked(true);
        super.prepareSurfaces();
+161 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 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.server.wm;

import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import android.platform.test.annotations.Presubmit;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * This file tests WM setting the priority on windows that is used in SF to determine at what
 * frame rate the Display should run. Any changes to the algorithm should be reflected in these
 * tests.
 *
 * Build/Install/Run: atest FrameRateSelectionPriority
 */
@Presubmit
@RunWith(WindowTestRunner.class)
public class FrameRateSelectionPriorityTests extends WindowTestsBase {

    @Test
    public void basicTest() {
        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
        assertNotNull("Window state is created", appWindow);
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority doesn't change.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        // Call the function a few times.
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        appWindow.updateFrameRateSelectionPriorityIfNeeded();

        // Since nothing changed in the priority state, the transaction should not be updating.
        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
    }

    @Test
    public void testApplicationInFocusWithoutModeId() {
        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
                .getPreferredModeId(appWindow), 0);

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority stays MAX_VALUE.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        // Application is in focus.
        appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes to 1.
        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), 1);
    }

    @Test
    public void testApplicationInFocusWithModeId() {
        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        // Application is in focus.
        appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
        // Update the mode ID to a requested number.
        appWindow.mAttrs.preferredDisplayModeId = 1;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 0);

        // Remove the mode ID request.
        appWindow.mAttrs.preferredDisplayModeId = 0;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 1);

        // Verify we called actions on Transactions correctly.
        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), 0);
        verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), 1);
    }

    @Test
    public void testApplicationNotInFocusWithModeId() {
        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
        appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // The window is not in focus.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        // Update the mode ID to a requested number.
        appWindow.mAttrs.preferredDisplayModeId = 1;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 2);

        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), 2);
    }

    @Test
    public void testApplicationNotInFocusWithoutModeId() {
        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
        appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // The window is not in focus.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        // Make sure that the mode ID is not set.
        appWindow.mAttrs.preferredDisplayModeId = 0;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority doesn't change.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);

        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
    }
}
Loading