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