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

Commit f3e0531f authored by Ady Abraham's avatar Ady Abraham
Browse files

Implement high refresh rate policy in window manager

If an app is blacklisted or using the camera, we should switch
back to the refresh rate that is >= 60hz.

However, if the app is animating, we want it to be running at
90hz so we switch immediately when a window is animating or
about to animate.

Test: HighRefreshRateBlacklistTest
Test: RefreshRatePolicyTest
Test: manually added blacklist entry and checked refresh rate
Test: Verify that animations still run at 90hz

Fixes: 132338514
Fixes: 130744843
Change-Id: If44268b01df8b81ffef183e4942105a9fe077d3d
parent b380516f
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -804,10 +804,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
                    mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
                            = w.mAttrs.preferredRefreshRate;
                }
                final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
                        .getPreferredModeId(w);
                if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
                        && w.mAttrs.preferredDisplayModeId != 0) {
                    mTmpApplySurfaceChangesTransactionState.preferredModeId
                            = w.mAttrs.preferredDisplayModeId;
                        && preferredModeId != 0) {
                    mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;
                }
            }
        }
+10 −0
Original line number Diff line number Diff line
@@ -381,6 +381,8 @@ public class DisplayPolicy {
     */
    @NonNull private Insets mForwardedInsets = Insets.NONE;

    private RefreshRatePolicy mRefreshRatePolicy;

    // -------- PolicyHandler --------
    private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1;
    private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
@@ -591,6 +593,10 @@ public class DisplayPolicy {
            mHasStatusBar = false;
            mHasNavigationBar = mDisplayContent.supportsSystemDecorations();
        }

        mRefreshRatePolicy = new RefreshRatePolicy(mService,
                mDisplayContent.getDisplayInfo(),
                mService.mHighRefreshRateBlacklist);
    }

    void systemReady() {
@@ -3596,6 +3602,10 @@ public class DisplayPolicy {
        }
    }

    RefreshRatePolicy getRefreshRatePolicy() {
        return mRefreshRatePolicy;
    }

    void dump(String prefix, PrintWriter pw) {
        pw.print(prefix); pw.print("DisplayPolicy");
        prefix += "  ";
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.annotation.NonNull;
import android.os.SystemProperties;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;

/**
 * A Blacklist for packages that should force the display out of high refresh rate.
 */
class HighRefreshRateBlacklist {

    private static final String SYSPROP_KEY = "ro.window_manager.high_refresh_rate_blacklist";
    private static final String SYSPROP_KEY_LENGTH_SUFFIX = "_length";
    private static final String SYSPROP_KEY_ENTRY_SUFFIX = "_entry";
    private static final int MAX_ENTRIES = 50;

    private ArraySet<String> mBlacklistedPackages = new ArraySet<>();

    static HighRefreshRateBlacklist create() {
        return new HighRefreshRateBlacklist(new SystemPropertyGetter() {
            @Override
            public int getInt(String key, int def) {
                return SystemProperties.getInt(key, def);
            }

            @Override
            public String get(String key) {
                return SystemProperties.get(key);
            }
        });
    }

    @VisibleForTesting
    HighRefreshRateBlacklist(SystemPropertyGetter propertyGetter) {

        // Read and populate the blacklist
        final int length = Math.min(
                propertyGetter.getInt(SYSPROP_KEY + SYSPROP_KEY_LENGTH_SUFFIX, 0),
                MAX_ENTRIES);
        for (int i = 1; i <= length; i++) {
            final String packageName = propertyGetter.get(
                    SYSPROP_KEY + SYSPROP_KEY_ENTRY_SUFFIX + i);
            if (!packageName.isEmpty()) {
                mBlacklistedPackages.add(packageName);
            }
        }
    }

    boolean isBlacklisted(String packageName) {
        return mBlacklistedPackages.contains(packageName);
    }

    interface SystemPropertyGetter {
        int getInt(String key, int def);
        @NonNull String get(String key);
    }
}
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.util.ArraySet;
import android.view.Display.Mode;
import android.view.DisplayInfo;

/**
 * Policy to select a lower refresh rate for the display if applicable.
 */
class RefreshRatePolicy {

    private final int mLowRefreshRateId;
    private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
    private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
    private final WindowManagerService mWmService;

    RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
            HighRefreshRateBlacklist blacklist) {
        mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
        mHighRefreshRateBlacklist = blacklist;
        mWmService = wmService;
    }

    /**
     * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
     * default mode.
     */
    private int findLowRefreshRateModeId(DisplayInfo displayInfo) {
        Mode mode = displayInfo.getDefaultMode();
        float[] refreshRates = displayInfo.getDefaultRefreshRates();
        float bestRefreshRate = mode.getRefreshRate();
        for (int i = refreshRates.length - 1; i >= 0; i--) {
            if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) {
                bestRefreshRate = refreshRates[i];
            }
        }
        return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate);
    }

    void addNonHighRefreshRatePackage(String packageName) {
        mNonHighRefreshRatePackages.add(packageName);
        mWmService.requestTraversal();
    }

    void removeNonHighRefreshRatePackage(String packageName) {
        mNonHighRefreshRatePackages.remove(packageName);
        mWmService.requestTraversal();
    }

    int getPreferredModeId(WindowState w) {

        // If app is animating, it's not able to control refresh rate because we want the animation
        // to run in default refresh rate.
        if (w.isAnimating()) {
            return 0;
        }

        // If app requests a certain refresh rate or mode, don't override it.
        if (w.mAttrs.preferredRefreshRate != 0 || w.mAttrs.preferredDisplayModeId != 0) {
            return w.mAttrs.preferredDisplayModeId;
        }

        final String packageName = w.getOwningPackage();

        // If app is using Camera, force it to default (lower) refresh rate.
        if (mNonHighRefreshRatePackages.contains(packageName)) {
            return mLowRefreshRateId;
        }

        // If app is blacklisted using higher refresh rate, return default (lower) refresh rate
        if (mHighRefreshRateBlacklist.isBlacklisted(packageName)) {
            return mLowRefreshRateId;
        }
        return 0;
    }
}
+10 −5
Original line number Diff line number Diff line
@@ -845,6 +845,8 @@ public class WindowManagerService extends IWindowManager.Stub

    final Configuration mTempConfiguration = new Configuration();

    final HighRefreshRateBlacklist mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create();

    // If true, only the core apps and services are being launched because the device
    // is in a special boot mode, such as being encrypted or waiting for a decryption password.
    // For example, when this flag is true, there will be no wallpaper service.
@@ -7449,16 +7451,19 @@ public class WindowManagerService extends IWindowManager.Stub

        @Override
        public void addNonHighRefreshRatePackage(@NonNull String packageName) {
            // TODO
            Slog.i(TAG, "addNonHighRefreshRatePackage: " + packageName);
            synchronized (mGlobalLock) {
                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
                        .addNonHighRefreshRatePackage(packageName));
            }
        }

        @Override
        public void removeNonHighRefreshRatePackage(@NonNull String packageName) {
            // TODO
            Slog.i(TAG, "removeNonHighRefreshRatePackage: " + packageName);
            synchronized (mGlobalLock) {
                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
                        .removeNonHighRefreshRatePackage(packageName));
            }
        }

    }

    void registerAppFreezeListener(AppFreezeListener listener) {
Loading