• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
28 import android.view.SurfaceControl.RefreshRateRange;
29 
30 import java.util.HashMap;
31 import java.util.Objects;
32 
33 /**
34  * Policy to select a lower refresh rate for the display if applicable.
35  */
36 class RefreshRatePolicy {
37 
38     class PackageRefreshRate {
39         private final HashMap<String, RefreshRateRange> mPackages = new HashMap<>();
40 
add(String s, float minRefreshRate, float maxRefreshRate)41         public void add(String s, float minRefreshRate, float maxRefreshRate) {
42             float minSupportedRefreshRate =
43                     Math.max(RefreshRatePolicy.this.mMinSupportedRefreshRate, minRefreshRate);
44             float maxSupportedRefreshRate =
45                     Math.min(RefreshRatePolicy.this.mMaxSupportedRefreshRate, maxRefreshRate);
46 
47             mPackages.put(s,
48                     new RefreshRateRange(minSupportedRefreshRate, maxSupportedRefreshRate));
49         }
50 
get(String s)51         public RefreshRateRange get(String s) {
52             return mPackages.get(s);
53         }
54 
remove(String s)55         public void remove(String s) {
56             mPackages.remove(s);
57         }
58     }
59 
60     private final DisplayInfo mDisplayInfo;
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         mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
93         mHighRefreshRateDenylist = denylist;
94         mWmService = wmService;
95     }
96 
97     /**
98      * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
99      * default mode.
100      */
findLowRefreshRateMode(DisplayInfo displayInfo)101     private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
102         final Mode defaultMode = displayInfo.getDefaultMode();
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         return preferredDisplayModeId;
136     }
137 
138     /**
139      * Calculate the priority based on whether the window is in focus and whether the application
140      * voted for a specific refresh rate.
141      *
142      * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might
143      * be useful in edge cases when we are deciding which layer should get priority when deciding
144      * about the refresh rate.
145      */
calculatePriority(WindowState w)146     int calculatePriority(WindowState w) {
147         boolean isFocused = w.isFocused();
148         int preferredModeId = getPreferredModeId(w);
149 
150         if (!isFocused && preferredModeId > 0) {
151             return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE;
152         }
153         if (isFocused && preferredModeId == 0) {
154             return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE;
155         }
156         if (isFocused && preferredModeId > 0) {
157             return LAYER_PRIORITY_FOCUSED_WITH_MODE;
158         }
159         return LAYER_PRIORITY_UNSET;
160     }
161 
162     public static class FrameRateVote {
163         float mRefreshRate;
164         @Surface.FrameRateCompatibility int mCompatibility;
165         @SurfaceControl.FrameRateSelectionStrategy int mSelectionStrategy;
166 
167 
168 
FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility, @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy)169         FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility,
170                       @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) {
171             update(refreshRate, compatibility, selectionStrategy);
172         }
173 
FrameRateVote()174         FrameRateVote() {
175             reset();
176         }
177 
update(float refreshRate, @Surface.FrameRateCompatibility int compatibility, @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy)178         boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility,
179                        @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) {
180             if (!refreshRateEquals(refreshRate)
181                     || mCompatibility != compatibility
182                     || mSelectionStrategy != selectionStrategy) {
183                 mRefreshRate = refreshRate;
184                 mCompatibility = compatibility;
185                 mSelectionStrategy = selectionStrategy;
186                 return true;
187             }
188             return false;
189         }
190 
reset()191         boolean reset() {
192             return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
193                     SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE);
194         }
195 
196         @Override
equals(Object o)197         public boolean equals(Object o) {
198             if (!(o instanceof FrameRateVote)) {
199                 return false;
200             }
201 
202             FrameRateVote other = (FrameRateVote) o;
203             return refreshRateEquals(other.mRefreshRate)
204                     && mCompatibility == other.mCompatibility
205                     && mSelectionStrategy == other.mSelectionStrategy;
206         }
207 
208         @Override
hashCode()209         public int hashCode() {
210             return Objects.hash(mRefreshRate, mCompatibility, mSelectionStrategy);
211 
212         }
213 
214         @Override
toString()215         public String toString() {
216             return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility
217                     + ", mSelectionStrategy=" + mSelectionStrategy;
218         }
219 
refreshRateEquals(float refreshRate)220         private boolean refreshRateEquals(float refreshRate) {
221             return mRefreshRate <= refreshRate + RefreshRateRange.FLOAT_TOLERANCE
222                     && mRefreshRate >= refreshRate - RefreshRateRange.FLOAT_TOLERANCE;
223         }
224     }
225 
updateFrameRateVote(WindowState w)226     boolean updateFrameRateVote(WindowState w) {
227         @DisplayManager.SwitchingType int refreshRateSwitchingType =
228                 mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType();
229 
230         // If refresh rate switching is disabled there is no point to set the frame rate on the
231         // surface as the refresh rate will be limited by display manager to a single value
232         // and SurfaceFlinger wouldn't be able to change it anyways.
233         if (refreshRateSwitchingType == SWITCHING_TYPE_NONE) {
234             return w.mFrameRateVote.reset();
235         }
236 
237         // If insets animation is running, do not convey the preferred app refresh rate to let VRI
238         // to control the refresh rate.
239         if (w.isInsetsAnimationRunning()) {
240             return w.mFrameRateVote.reset();
241         }
242 
243         // If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate
244         // of that mode id.
245         if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
246             final int preferredModeId = w.mAttrs.preferredDisplayModeId;
247             if (preferredModeId > 0) {
248                 for (Display.Mode mode : mDisplayInfo.appsSupportedModes) {
249                     if (preferredModeId == mode.getModeId()) {
250                         return w.mFrameRateVote.update(mode.getRefreshRate(),
251                                 Surface.FRAME_RATE_COMPATIBILITY_EXACT,
252                                 SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
253                     }
254                 }
255             }
256         }
257 
258         if (w.mAttrs.preferredRefreshRate > 0) {
259             return w.mFrameRateVote.update(w.mAttrs.preferredRefreshRate,
260                     Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
261                     SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
262         }
263 
264         // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny
265         // list, we return the low refresh rate as the preferred one.
266         if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
267             final String packageName = w.getOwningPackage();
268             if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
269                 return w.mFrameRateVote.update(mLowRefreshRateMode.getRefreshRate(),
270                         Surface.FRAME_RATE_COMPATIBILITY_EXACT,
271                         SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
272             }
273         }
274 
275         return w.mFrameRateVote.reset();
276     }
277 
getPreferredMinRefreshRate(WindowState w)278     float getPreferredMinRefreshRate(WindowState w) {
279         // If app is animating, it's not able to control refresh rate because we want the animation
280         // to run in default refresh rate.
281         if (w.isAnimationRunningSelfOrParent() || w.isInsetsAnimationRunning()) {
282             return 0;
283         }
284 
285         if (w.mAttrs.preferredMinDisplayRefreshRate > 0) {
286             return w.mAttrs.preferredMinDisplayRefreshRate;
287         }
288 
289         String packageName = w.getOwningPackage();
290         // If app is using Camera, we set both the min and max refresh rate to the camera's
291         // preferred refresh rate to make sure we don't end up with a refresh rate lower
292         // than the camera capture rate, which will lead to dropping camera frames.
293         RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName);
294         if (range != null) {
295             return range.min;
296         }
297 
298         return 0;
299     }
300 
getPreferredMaxRefreshRate(WindowState w)301     float getPreferredMaxRefreshRate(WindowState w) {
302         // If app is animating, it's not able to control refresh rate because we want the animation
303         // to run in default refresh rate.
304         if (w.isAnimationRunningSelfOrParent() || w.isInsetsAnimationRunning()) {
305             return 0;
306         }
307 
308         if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) {
309             return w.mAttrs.preferredMaxDisplayRefreshRate;
310         }
311 
312         final String packageName = w.getOwningPackage();
313         // If app is using Camera, force it to default (lower) refresh rate.
314         RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName);
315         if (range != null) {
316             return range.max;
317         }
318 
319         return 0;
320     }
321 }
322