• 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 com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
20 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
21 
22 import android.util.ArraySet;
23 import android.view.Display;
24 import android.view.Display.Mode;
25 import android.view.DisplayInfo;
26 
27 /**
28  * Policy to select a lower refresh rate for the display if applicable.
29  */
30 class RefreshRatePolicy {
31 
32     private final Mode mLowRefreshRateMode;
33     private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
34     private final HighRefreshRateDenylist mHighRefreshRateDenylist;
35     private final WindowManagerService mWmService;
36 
37     /**
38      * The following constants represent priority of the window. SF uses this information when
39      * deciding which window has a priority when deciding about the refresh rate of the screen.
40      * Priority 0 is considered the highest priority. -1 means that the priority is unset.
41      */
42     static final int LAYER_PRIORITY_UNSET = -1;
43     /** Windows that are in focus and voted for the preferred mode ID have the highest priority. */
44     static final int LAYER_PRIORITY_FOCUSED_WITH_MODE = 0;
45     /**
46      * This is a default priority for all windows that are in focus, but have not requested a
47      * specific mode ID.
48      */
49     static final int LAYER_PRIORITY_FOCUSED_WITHOUT_MODE = 1;
50     /**
51      * Windows that are not in focus, but voted for a specific mode ID should be
52      * acknowledged by SF. For example, there are two applications in a split screen.
53      * One voted for a given mode ID, and the second one doesn't care. Even though the
54      * second one might be in focus, we can honor the mode ID of the first one.
55      */
56     static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
57 
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, HighRefreshRateDenylist denylist)58     RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
59             HighRefreshRateDenylist denylist) {
60         mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
61         mHighRefreshRateDenylist = denylist;
62         mWmService = wmService;
63     }
64 
65     /**
66      * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
67      * default mode.
68      */
findLowRefreshRateMode(DisplayInfo displayInfo)69     private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
70         Mode mode = displayInfo.getDefaultMode();
71         float[] refreshRates = displayInfo.getDefaultRefreshRates();
72         float bestRefreshRate = mode.getRefreshRate();
73         for (int i = refreshRates.length - 1; i >= 0; i--) {
74             if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) {
75                 bestRefreshRate = refreshRates[i];
76             }
77         }
78         return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate);
79     }
80 
addNonHighRefreshRatePackage(String packageName)81     void addNonHighRefreshRatePackage(String packageName) {
82         mNonHighRefreshRatePackages.add(packageName);
83         mWmService.requestTraversal();
84     }
85 
removeNonHighRefreshRatePackage(String packageName)86     void removeNonHighRefreshRatePackage(String packageName) {
87         mNonHighRefreshRatePackages.remove(packageName);
88         mWmService.requestTraversal();
89     }
90 
getPreferredModeId(WindowState w)91     int getPreferredModeId(WindowState w) {
92         // If app is animating, it's not able to control refresh rate because we want the animation
93         // to run in default refresh rate.
94         if (w.isAnimating(TRANSITION | PARENTS)) {
95             return 0;
96         }
97 
98         return w.mAttrs.preferredDisplayModeId;
99     }
100 
101     /**
102      * Calculate the priority based on whether the window is in focus and whether the application
103      * voted for a specific refresh rate.
104      *
105      * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might
106      * be useful in edge cases when we are deciding which layer should get priority when deciding
107      * about the refresh rate.
108      */
calculatePriority(WindowState w)109     int calculatePriority(WindowState w) {
110         boolean isFocused = w.isFocused();
111         int preferredModeId = getPreferredModeId(w);
112 
113         if (!isFocused && preferredModeId > 0) {
114             return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE;
115         }
116         if (isFocused && preferredModeId == 0) {
117             return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE;
118         }
119         if (isFocused && preferredModeId > 0) {
120             return LAYER_PRIORITY_FOCUSED_WITH_MODE;
121         }
122         return LAYER_PRIORITY_UNSET;
123     }
124 
getPreferredRefreshRate(WindowState w)125     float getPreferredRefreshRate(WindowState w) {
126         // If app is animating, it's not able to control refresh rate because we want the animation
127         // to run in default refresh rate.
128         if (w.isAnimating(TRANSITION | PARENTS)) {
129             return 0;
130         }
131 
132         // If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate
133         // of that mode id.
134         final int preferredModeId = w.mAttrs.preferredDisplayModeId;
135         if (preferredModeId > 0) {
136             DisplayInfo info = w.getDisplayInfo();
137             if (info != null) {
138                 for (Display.Mode mode : info.supportedModes) {
139                     if (preferredModeId == mode.getModeId()) {
140                         return mode.getRefreshRate();
141                     }
142                 }
143             }
144         }
145 
146         if (w.mAttrs.preferredRefreshRate > 0) {
147             return w.mAttrs.preferredRefreshRate;
148         }
149 
150         // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny
151         // list, we return the low refresh rate as the preferred one.
152         final String packageName = w.getOwningPackage();
153         if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
154             return mLowRefreshRateMode.getRefreshRate();
155         }
156 
157         return 0;
158     }
159 
getPreferredMinRefreshRate(WindowState w)160     float getPreferredMinRefreshRate(WindowState w) {
161         // If app is animating, it's not able to control refresh rate because we want the animation
162         // to run in default refresh rate.
163         if (w.isAnimating(TRANSITION | PARENTS)) {
164             return 0;
165         }
166 
167         return w.mAttrs.preferredMinDisplayRefreshRate;
168     }
169 
getPreferredMaxRefreshRate(WindowState w)170     float getPreferredMaxRefreshRate(WindowState w) {
171         // If app is animating, it's not able to control refresh rate because we want the animation
172         // to run in default refresh rate.
173         if (w.isAnimating(TRANSITION | PARENTS)) {
174             return 0;
175         }
176 
177         if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) {
178             return w.mAttrs.preferredMaxDisplayRefreshRate;
179         }
180 
181         final String packageName = w.getOwningPackage();
182         // If app is using Camera, force it to default (lower) refresh rate.
183         if (mNonHighRefreshRatePackages.contains(packageName)) {
184             return mLowRefreshRateMode.getRefreshRate();
185         }
186 
187         return 0;
188     }
189 }
190