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