1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
20 import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
21 
22 import android.hardware.display.DisplayManager;
23 import android.view.Display;
24 import android.view.Display.Mode;
25 import android.view.DisplayInfo;
26 import android.view.Surface;
27 import android.view.SurfaceControl.RefreshRateRange;
28 
29 import java.util.HashMap;
30 import java.util.Objects;
31 
32 /**
33  * Policy to select a lower refresh rate for the display if applicable.
34  */
35 class RefreshRatePolicy {
36 
37     class PackageRefreshRate {
38         private final HashMap<String, RefreshRateRange> mPackages = new HashMap<>();
39 
add(String s, float minRefreshRate, float maxRefreshRate)40         public void add(String s, float minRefreshRate, float maxRefreshRate) {
41             float minSupportedRefreshRate =
42                     Math.max(RefreshRatePolicy.this.mMinSupportedRefreshRate, minRefreshRate);
43             float maxSupportedRefreshRate =
44                     Math.min(RefreshRatePolicy.this.mMaxSupportedRefreshRate, maxRefreshRate);
45 
46             mPackages.put(s,
47                     new RefreshRateRange(minSupportedRefreshRate, maxSupportedRefreshRate));
48         }
49 
get(String s)50         public RefreshRateRange get(String s) {
51             return mPackages.get(s);
52         }
53 
remove(String s)54         public void remove(String s) {
55             mPackages.remove(s);
56         }
57     }
58 
59     private final DisplayInfo mDisplayInfo;
60     private final Mode mDefaultMode;
61     private final Mode mLowRefreshRateMode;
62     private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate();
63     private final HighRefreshRateDenylist mHighRefreshRateDenylist;
64     private final WindowManagerService mWmService;
65     private float mMinSupportedRefreshRate;
66     private float mMaxSupportedRefreshRate;
67 
68     /**
69      * The following constants represent priority of the window. SF uses this information when
70      * deciding which window has a priority when deciding about the refresh rate of the screen.
71      * Priority 0 is considered the highest priority. -1 means that the priority is unset.
72      */
73     static final int LAYER_PRIORITY_UNSET = -1;
74     /** Windows that are in focus and voted for the preferred mode ID have the highest priority. */
75     static final int LAYER_PRIORITY_FOCUSED_WITH_MODE = 0;
76     /**
77      * This is a default priority for all windows that are in focus, but have not requested a
78      * specific mode ID.
79      */
80     static final int LAYER_PRIORITY_FOCUSED_WITHOUT_MODE = 1;
81     /**
82      * Windows that are not in focus, but voted for a specific mode ID should be
83      * acknowledged by SF. For example, there are two applications in a split screen.
84      * One voted for a given mode ID, and the second one doesn't care. Even though the
85      * second one might be in focus, we can honor the mode ID of the first one.
86      */
87     static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
88 
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, HighRefreshRateDenylist denylist)89     RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
90             HighRefreshRateDenylist denylist) {
91         mDisplayInfo = displayInfo;
92         mDefaultMode = displayInfo.getDefaultMode();
93         mLowRefreshRateMode = findLowRefreshRateMode(displayInfo, mDefaultMode);
94         mHighRefreshRateDenylist = denylist;
95         mWmService = wmService;
96     }
97 
98     /**
99      * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
100      * default mode.
101      */
findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode)102     private Mode findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode) {
103         float[] refreshRates = displayInfo.getDefaultRefreshRates();
104         float bestRefreshRate = defaultMode.getRefreshRate();
105         mMinSupportedRefreshRate = bestRefreshRate;
106         mMaxSupportedRefreshRate = bestRefreshRate;
107         for (int i = refreshRates.length - 1; i >= 0; i--) {
108             mMinSupportedRefreshRate = Math.min(mMinSupportedRefreshRate, refreshRates[i]);
109             mMaxSupportedRefreshRate = Math.max(mMaxSupportedRefreshRate, refreshRates[i]);
110 
111             if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) {
112                 bestRefreshRate = refreshRates[i];
113             }
114         }
115         return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate);
116     }
117 
addRefreshRateRangeForPackage(String packageName, float minRefreshRate, float maxRefreshRate)118     void addRefreshRateRangeForPackage(String packageName,
119             float minRefreshRate, float maxRefreshRate) {
120         mNonHighRefreshRatePackages.add(packageName, minRefreshRate, maxRefreshRate);
121         mWmService.requestTraversal();
122     }
123 
removeRefreshRateRangeForPackage(String packageName)124     void removeRefreshRateRangeForPackage(String packageName) {
125         mNonHighRefreshRatePackages.remove(packageName);
126         mWmService.requestTraversal();
127     }
128 
getPreferredModeId(WindowState w)129     int getPreferredModeId(WindowState w) {
130         final int preferredDisplayModeId = w.mAttrs.preferredDisplayModeId;
131         if (preferredDisplayModeId <= 0) {
132             // Unspecified, use default mode.
133             return 0;
134         }
135 
136         // If app is animating, it's not able to control refresh rate because we want the animation
137         // to run in default refresh rate. But if the display size of default mode is different
138         // from the using preferred mode, then still keep the preferred mode to avoid disturbing
139         // the animation.
140         if (w.isAnimationRunningSelfOrParent()) {
141             Display.Mode preferredMode = null;
142             for (Display.Mode mode : mDisplayInfo.supportedModes) {
143                 if (preferredDisplayModeId == mode.getModeId()) {
144                     preferredMode = mode;
145                     break;
146                 }
147             }
148             if (preferredMode != null) {
149                 final int pW = preferredMode.getPhysicalWidth();
150                 final int pH = preferredMode.getPhysicalHeight();
151                 if ((pW != mDefaultMode.getPhysicalWidth()
152                         || pH != mDefaultMode.getPhysicalHeight())
153                         && pW == mDisplayInfo.getNaturalWidth()
154                         && pH == mDisplayInfo.getNaturalHeight()) {
155                     // Prefer not to change display size when animating.
156                     return preferredDisplayModeId;
157                 }
158             }
159             return 0;
160         }
161 
162         return preferredDisplayModeId;
163     }
164 
165     /**
166      * Calculate the priority based on whether the window is in focus and whether the application
167      * voted for a specific refresh rate.
168      *
169      * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might
170      * be useful in edge cases when we are deciding which layer should get priority when deciding
171      * about the refresh rate.
172      */
calculatePriority(WindowState w)173     int calculatePriority(WindowState w) {
174         boolean isFocused = w.isFocused();
175         int preferredModeId = getPreferredModeId(w);
176 
177         if (!isFocused && preferredModeId > 0) {
178             return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE;
179         }
180         if (isFocused && preferredModeId == 0) {
181             return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE;
182         }
183         if (isFocused && preferredModeId > 0) {
184             return LAYER_PRIORITY_FOCUSED_WITH_MODE;
185         }
186         return LAYER_PRIORITY_UNSET;
187     }
188 
189     public static class FrameRateVote {
190         float mRefreshRate;
191         @Surface.FrameRateCompatibility int mCompatibility;
192 
FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility)193         FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility) {
194             update(refreshRate, compatibility);
195         }
196 
FrameRateVote()197         FrameRateVote() {
198             reset();
199         }
200 
update(float refreshRate, @Surface.FrameRateCompatibility int compatibility)201         boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility) {
202             if (!refreshRateEquals(refreshRate) || mCompatibility != compatibility) {
203                 mRefreshRate = refreshRate;
204                 mCompatibility = compatibility;
205                 return true;
206             }
207             return false;
208         }
209 
reset()210         boolean reset() {
211             return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
212         }
213 
214         @Override
equals(Object o)215         public boolean equals(Object o) {
216             if (!(o instanceof FrameRateVote)) {
217                 return false;
218             }
219 
220             FrameRateVote other = (FrameRateVote) o;
221             return refreshRateEquals(other.mRefreshRate)
222                     && mCompatibility == other.mCompatibility;
223         }
224 
225         @Override
hashCode()226         public int hashCode() {
227             return Objects.hash(mRefreshRate, mCompatibility);
228         }
229 
230         @Override
toString()231         public String toString() {
232             return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility;
233         }
234 
refreshRateEquals(float refreshRate)235         private boolean refreshRateEquals(float refreshRate) {
236             return mRefreshRate <= refreshRate + RefreshRateRange.FLOAT_TOLERANCE
237                     && mRefreshRate >= refreshRate - RefreshRateRange.FLOAT_TOLERANCE;
238         }
239     }
240 
updateFrameRateVote(WindowState w)241     boolean updateFrameRateVote(WindowState w) {
242         @DisplayManager.SwitchingType int refreshRateSwitchingType =
243                 mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType();
244 
245         // If refresh rate switching is disabled there is no point to set the frame rate on the
246         // surface as the refresh rate will be limited by display manager to a single value
247         // and SurfaceFlinger wouldn't be able to change it anyways.
248         if (refreshRateSwitchingType == SWITCHING_TYPE_NONE) {
249             return w.mFrameRateVote.reset();
250         }
251 
252         // If app is animating, it's not able to control refresh rate because we want the animation
253         // to run in default refresh rate.
254         if (w.isAnimationRunningSelfOrParent()) {
255             return w.mFrameRateVote.reset();
256         }
257 
258         // If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate
259         // of that mode id.
260         if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
261             final int preferredModeId = w.mAttrs.preferredDisplayModeId;
262             if (preferredModeId > 0) {
263                 for (Display.Mode mode : mDisplayInfo.supportedModes) {
264                     if (preferredModeId == mode.getModeId()) {
265                         return w.mFrameRateVote.update(mode.getRefreshRate(),
266                                 Surface.FRAME_RATE_COMPATIBILITY_EXACT);
267                     }
268                 }
269             }
270         }
271 
272         if (w.mAttrs.preferredRefreshRate > 0) {
273             return w.mFrameRateVote.update(w.mAttrs.preferredRefreshRate,
274                     Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
275         }
276 
277         // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny
278         // list, we return the low refresh rate as the preferred one.
279         if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
280             final String packageName = w.getOwningPackage();
281             if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
282                 return w.mFrameRateVote.update(mLowRefreshRateMode.getRefreshRate(),
283                         Surface.FRAME_RATE_COMPATIBILITY_EXACT);
284             }
285         }
286 
287         return w.mFrameRateVote.reset();
288     }
289 
getPreferredMinRefreshRate(WindowState w)290     float getPreferredMinRefreshRate(WindowState w) {
291         // If app is animating, it's not able to control refresh rate because we want the animation
292         // to run in default refresh rate.
293         if (w.isAnimationRunningSelfOrParent()) {
294             return 0;
295         }
296 
297         if (w.mAttrs.preferredMinDisplayRefreshRate > 0) {
298             return w.mAttrs.preferredMinDisplayRefreshRate;
299         }
300 
301         String packageName = w.getOwningPackage();
302         // If app is using Camera, we set both the min and max refresh rate to the camera's
303         // preferred refresh rate to make sure we don't end up with a refresh rate lower
304         // than the camera capture rate, which will lead to dropping camera frames.
305         RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName);
306         if (range != null) {
307             return range.min;
308         }
309 
310         return 0;
311     }
312 
getPreferredMaxRefreshRate(WindowState w)313     float getPreferredMaxRefreshRate(WindowState w) {
314         // If app is animating, it's not able to control refresh rate because we want the animation
315         // to run in default refresh rate.
316         if (w.isAnimationRunningSelfOrParent()) {
317             return 0;
318         }
319 
320         if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) {
321             return w.mAttrs.preferredMaxDisplayRefreshRate;
322         }
323 
324         final String packageName = w.getOwningPackage();
325         // If app is using Camera, force it to default (lower) refresh rate.
326         RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName);
327         if (range != null) {
328             return range.max;
329         }
330 
331         return 0;
332     }
333 }
334