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.hardware.display.DisplayManagerInternal.RefreshRateRange; 23 import android.view.Display; 24 import android.view.Display.Mode; 25 import android.view.DisplayInfo; 26 27 import java.util.HashMap; 28 29 /** 30 * Policy to select a lower refresh rate for the display if applicable. 31 */ 32 class RefreshRatePolicy { 33 34 class PackageRefreshRate { 35 private final HashMap<String, RefreshRateRange> mPackages = new HashMap<>(); 36 add(String s, float minRefreshRate, float maxRefreshRate)37 public void add(String s, float minRefreshRate, float maxRefreshRate) { 38 float minSupportedRefreshRate = 39 Math.max(RefreshRatePolicy.this.mMinSupportedRefreshRate, minRefreshRate); 40 float maxSupportedRefreshRate = 41 Math.min(RefreshRatePolicy.this.mMaxSupportedRefreshRate, maxRefreshRate); 42 43 mPackages.put(s, 44 new RefreshRateRange(minSupportedRefreshRate, maxSupportedRefreshRate)); 45 } 46 get(String s)47 public RefreshRateRange get(String s) { 48 return mPackages.get(s); 49 } 50 remove(String s)51 public void remove(String s) { 52 mPackages.remove(s); 53 } 54 } 55 56 private final DisplayInfo mDisplayInfo; 57 private final Mode mDefaultMode; 58 private final Mode mLowRefreshRateMode; 59 private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate(); 60 private final HighRefreshRateDenylist mHighRefreshRateDenylist; 61 private final WindowManagerService mWmService; 62 private float mMinSupportedRefreshRate; 63 private float mMaxSupportedRefreshRate; 64 65 /** 66 * The following constants represent priority of the window. SF uses this information when 67 * deciding which window has a priority when deciding about the refresh rate of the screen. 68 * Priority 0 is considered the highest priority. -1 means that the priority is unset. 69 */ 70 static final int LAYER_PRIORITY_UNSET = -1; 71 /** Windows that are in focus and voted for the preferred mode ID have the highest priority. */ 72 static final int LAYER_PRIORITY_FOCUSED_WITH_MODE = 0; 73 /** 74 * This is a default priority for all windows that are in focus, but have not requested a 75 * specific mode ID. 76 */ 77 static final int LAYER_PRIORITY_FOCUSED_WITHOUT_MODE = 1; 78 /** 79 * Windows that are not in focus, but voted for a specific mode ID should be 80 * acknowledged by SF. For example, there are two applications in a split screen. 81 * One voted for a given mode ID, and the second one doesn't care. Even though the 82 * second one might be in focus, we can honor the mode ID of the first one. 83 */ 84 static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2; 85 RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, HighRefreshRateDenylist denylist)86 RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, 87 HighRefreshRateDenylist denylist) { 88 mDisplayInfo = displayInfo; 89 mDefaultMode = displayInfo.getDefaultMode(); 90 mLowRefreshRateMode = findLowRefreshRateMode(displayInfo, mDefaultMode); 91 mHighRefreshRateDenylist = denylist; 92 mWmService = wmService; 93 } 94 95 /** 96 * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the 97 * default mode. 98 */ findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode)99 private Mode findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode) { 100 float[] refreshRates = displayInfo.getDefaultRefreshRates(); 101 float bestRefreshRate = defaultMode.getRefreshRate(); 102 mMinSupportedRefreshRate = bestRefreshRate; 103 mMaxSupportedRefreshRate = bestRefreshRate; 104 for (int i = refreshRates.length - 1; i >= 0; i--) { 105 mMinSupportedRefreshRate = Math.min(mMinSupportedRefreshRate, refreshRates[i]); 106 mMaxSupportedRefreshRate = Math.max(mMaxSupportedRefreshRate, refreshRates[i]); 107 108 if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) { 109 bestRefreshRate = refreshRates[i]; 110 } 111 } 112 return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate); 113 } 114 addRefreshRateRangeForPackage(String packageName, float minRefreshRate, float maxRefreshRate)115 void addRefreshRateRangeForPackage(String packageName, 116 float minRefreshRate, float maxRefreshRate) { 117 mNonHighRefreshRatePackages.add(packageName, minRefreshRate, maxRefreshRate); 118 mWmService.requestTraversal(); 119 } 120 removeRefreshRateRangeForPackage(String packageName)121 void removeRefreshRateRangeForPackage(String packageName) { 122 mNonHighRefreshRatePackages.remove(packageName); 123 mWmService.requestTraversal(); 124 } 125 getPreferredModeId(WindowState w)126 int getPreferredModeId(WindowState w) { 127 final int preferredDisplayModeId = w.mAttrs.preferredDisplayModeId; 128 if (preferredDisplayModeId <= 0) { 129 // Unspecified, use default mode. 130 return 0; 131 } 132 133 // If app is animating, it's not able to control refresh rate because we want the animation 134 // to run in default refresh rate. But if the display size of default mode is different 135 // from the using preferred mode, then still keep the preferred mode to avoid disturbing 136 // the animation. 137 if (w.isAnimating(TRANSITION | PARENTS)) { 138 Display.Mode preferredMode = null; 139 for (Display.Mode mode : mDisplayInfo.supportedModes) { 140 if (preferredDisplayModeId == mode.getModeId()) { 141 preferredMode = mode; 142 break; 143 } 144 } 145 if (preferredMode != null) { 146 final int pW = preferredMode.getPhysicalWidth(); 147 final int pH = preferredMode.getPhysicalHeight(); 148 if ((pW != mDefaultMode.getPhysicalWidth() 149 || pH != mDefaultMode.getPhysicalHeight()) 150 && pW == mDisplayInfo.getNaturalWidth() 151 && pH == mDisplayInfo.getNaturalHeight()) { 152 // Prefer not to change display size when animating. 153 return preferredDisplayModeId; 154 } 155 } 156 return 0; 157 } 158 159 return preferredDisplayModeId; 160 } 161 162 /** 163 * Calculate the priority based on whether the window is in focus and whether the application 164 * voted for a specific refresh rate. 165 * 166 * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might 167 * be useful in edge cases when we are deciding which layer should get priority when deciding 168 * about the refresh rate. 169 */ calculatePriority(WindowState w)170 int calculatePriority(WindowState w) { 171 boolean isFocused = w.isFocused(); 172 int preferredModeId = getPreferredModeId(w); 173 174 if (!isFocused && preferredModeId > 0) { 175 return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE; 176 } 177 if (isFocused && preferredModeId == 0) { 178 return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE; 179 } 180 if (isFocused && preferredModeId > 0) { 181 return LAYER_PRIORITY_FOCUSED_WITH_MODE; 182 } 183 return LAYER_PRIORITY_UNSET; 184 } 185 getPreferredRefreshRate(WindowState w)186 float getPreferredRefreshRate(WindowState w) { 187 // If app is animating, it's not able to control refresh rate because we want the animation 188 // to run in default refresh rate. 189 if (w.isAnimating(TRANSITION | PARENTS)) { 190 return 0; 191 } 192 193 // If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate 194 // of that mode id. 195 final int preferredModeId = w.mAttrs.preferredDisplayModeId; 196 if (preferredModeId > 0) { 197 for (Display.Mode mode : mDisplayInfo.supportedModes) { 198 if (preferredModeId == mode.getModeId()) { 199 return mode.getRefreshRate(); 200 } 201 } 202 } 203 204 if (w.mAttrs.preferredRefreshRate > 0) { 205 return w.mAttrs.preferredRefreshRate; 206 } 207 208 // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny 209 // list, we return the low refresh rate as the preferred one. 210 final String packageName = w.getOwningPackage(); 211 if (mHighRefreshRateDenylist.isDenylisted(packageName)) { 212 return mLowRefreshRateMode.getRefreshRate(); 213 } 214 215 return 0; 216 } 217 getPreferredMinRefreshRate(WindowState w)218 float getPreferredMinRefreshRate(WindowState w) { 219 // If app is animating, it's not able to control refresh rate because we want the animation 220 // to run in default refresh rate. 221 if (w.isAnimating(TRANSITION | PARENTS)) { 222 return 0; 223 } 224 225 if (w.mAttrs.preferredMinDisplayRefreshRate > 0) { 226 return w.mAttrs.preferredMinDisplayRefreshRate; 227 } 228 229 String packageName = w.getOwningPackage(); 230 // If app is using Camera, we set both the min and max refresh rate to the camera's 231 // preferred refresh rate to make sure we don't end up with a refresh rate lower 232 // than the camera capture rate, which will lead to dropping camera frames. 233 RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); 234 if (range != null) { 235 return range.min; 236 } 237 238 return 0; 239 } 240 getPreferredMaxRefreshRate(WindowState w)241 float getPreferredMaxRefreshRate(WindowState w) { 242 // If app is animating, it's not able to control refresh rate because we want the animation 243 // to run in default refresh rate. 244 if (w.isAnimating(TRANSITION | PARENTS)) { 245 return 0; 246 } 247 248 if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) { 249 return w.mAttrs.preferredMaxDisplayRefreshRate; 250 } 251 252 final String packageName = w.getOwningPackage(); 253 // If app is using Camera, force it to default (lower) refresh rate. 254 RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); 255 if (range != null) { 256 return range.max; 257 } 258 259 return 0; 260 } 261 } 262