1 /* 2 * Copyright (C) 2023 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.display.mode; 18 19 import android.annotation.Nullable; 20 import android.hardware.display.DisplayManager; 21 import android.os.Handler; 22 import android.os.IThermalEventListener; 23 import android.os.Temperature; 24 import android.util.Slog; 25 import android.util.SparseArray; 26 import android.view.Display; 27 import android.view.DisplayInfo; 28 import android.view.SurfaceControl; 29 30 import com.android.internal.annotations.GuardedBy; 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.os.BackgroundThread; 33 34 import java.io.PrintWriter; 35 36 final class SkinThermalStatusObserver extends IThermalEventListener.Stub implements 37 DisplayManager.DisplayListener { 38 private static final String TAG = "SkinThermalStatusObserver"; 39 40 private final VotesStorage mVotesStorage; 41 private final DisplayModeDirector.Injector mInjector; 42 43 private boolean mLoggingEnabled; 44 45 private final Handler mHandler; 46 private final Object mThermalObserverLock = new Object(); 47 @GuardedBy("mThermalObserverLock") 48 @Temperature.ThrottlingStatus 49 private int mStatus = Temperature.THROTTLING_NONE; 50 @GuardedBy("mThermalObserverLock") 51 private final SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> 52 mThermalThrottlingByDisplay = new SparseArray<>(); 53 SkinThermalStatusObserver(DisplayModeDirector.Injector injector, VotesStorage votesStorage)54 SkinThermalStatusObserver(DisplayModeDirector.Injector injector, 55 VotesStorage votesStorage) { 56 this(injector, votesStorage, BackgroundThread.getHandler()); 57 } 58 59 @VisibleForTesting SkinThermalStatusObserver(DisplayModeDirector.Injector injector, VotesStorage votesStorage, Handler handler)60 SkinThermalStatusObserver(DisplayModeDirector.Injector injector, 61 VotesStorage votesStorage, Handler handler) { 62 mInjector = injector; 63 mVotesStorage = votesStorage; 64 mHandler = handler; 65 } 66 67 @Nullable findBestMatchingRefreshRateRange( @emperature.ThrottlingStatus int currentStatus, SparseArray<SurfaceControl.RefreshRateRange> throttlingMap)68 public static SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange( 69 @Temperature.ThrottlingStatus int currentStatus, 70 SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) { 71 SurfaceControl.RefreshRateRange foundRange = null; 72 for (int status = currentStatus; status >= 0; status--) { 73 foundRange = throttlingMap.get(status); 74 if (foundRange != null) { 75 break; 76 } 77 } 78 return foundRange; 79 } 80 observe()81 void observe() { 82 // if failed to register thermal service listener, don't register display listener 83 if (!mInjector.registerThermalServiceListener(this)) { 84 return; 85 } 86 registerDisplayListener(); 87 populateInitialDisplayInfo(); 88 } 89 registerDisplayListener()90 private void registerDisplayListener() { 91 mInjector.registerDisplayListener(this, mHandler, 92 DisplayManager.EVENT_TYPE_DISPLAY_ADDED 93 | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED 94 | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED); 95 } 96 setLoggingEnabled(boolean enabled)97 void setLoggingEnabled(boolean enabled) { 98 mLoggingEnabled = enabled; 99 } 100 101 @Override notifyThrottling(Temperature temp)102 public void notifyThrottling(Temperature temp) { 103 @Temperature.ThrottlingStatus int currentStatus = temp.getStatus(); 104 105 synchronized (mThermalObserverLock) { 106 if (mStatus == currentStatus) { 107 return; // status not changed, skip update 108 } 109 mStatus = currentStatus; 110 mHandler.post(this::updateVotes); 111 } 112 113 if (mLoggingEnabled) { 114 Slog.d(TAG, "New thermal throttling status " + ", current thermal status = " 115 + currentStatus); 116 } 117 } 118 119 //region DisplayManager.DisplayListener 120 @Override onDisplayAdded(int displayId)121 public void onDisplayAdded(int displayId) { 122 updateThermalRefreshRateThrottling(displayId); 123 if (mLoggingEnabled) { 124 Slog.d(TAG, "Display added:" + displayId); 125 } 126 } 127 128 @Override onDisplayRemoved(int displayId)129 public void onDisplayRemoved(int displayId) { 130 synchronized (mThermalObserverLock) { 131 mThermalThrottlingByDisplay.remove(displayId); 132 mHandler.post(() -> mVotesStorage.updateVote(displayId, 133 Vote.PRIORITY_SKIN_TEMPERATURE, null)); 134 } 135 if (mLoggingEnabled) { 136 Slog.d(TAG, "Display removed and voted: displayId=" + displayId); 137 } 138 } 139 140 @Override onDisplayChanged(int displayId)141 public void onDisplayChanged(int displayId) { 142 updateThermalRefreshRateThrottling(displayId); 143 if (mLoggingEnabled) { 144 Slog.d(TAG, "Display changed:" + displayId); 145 } 146 } 147 //endregion 148 populateInitialDisplayInfo()149 private void populateInitialDisplayInfo() { 150 DisplayInfo info = new DisplayInfo(); 151 Display[] displays = mInjector.getDisplays(); 152 int size = displays.length; 153 SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap = new SparseArray<>( 154 size); 155 for (Display d : displays) { 156 final int displayId = d.getDisplayId(); 157 d.getDisplayInfo(info); 158 localMap.put(displayId, info.thermalRefreshRateThrottling); 159 } 160 synchronized (mThermalObserverLock) { 161 for (int i = 0; i < size; i++) { 162 mThermalThrottlingByDisplay.put(localMap.keyAt(i), localMap.valueAt(i)); 163 } 164 } 165 if (mLoggingEnabled) { 166 Slog.d(TAG, "Display initial info:" + localMap); 167 } 168 } 169 updateThermalRefreshRateThrottling(int displayId)170 private void updateThermalRefreshRateThrottling(int displayId) { 171 DisplayInfo displayInfo = new DisplayInfo(); 172 mInjector.getDisplayInfo(displayId, displayInfo); 173 SparseArray<SurfaceControl.RefreshRateRange> throttlingMap = 174 displayInfo.thermalRefreshRateThrottling; 175 176 synchronized (mThermalObserverLock) { 177 mThermalThrottlingByDisplay.put(displayId, throttlingMap); 178 mHandler.post(() -> updateVoteForDisplay(displayId)); 179 } 180 if (mLoggingEnabled) { 181 Slog.d(TAG, 182 "Thermal throttling updated: display=" + displayId + ", map=" + throttlingMap); 183 } 184 } 185 186 //region in mHandler thread updateVotes()187 private void updateVotes() { 188 @Temperature.ThrottlingStatus int localStatus; 189 SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap; 190 191 synchronized (mThermalObserverLock) { 192 localStatus = mStatus; 193 localMap = mThermalThrottlingByDisplay.clone(); 194 } 195 if (mLoggingEnabled) { 196 Slog.d(TAG, "Updating votes for status=" + localStatus + ", map=" + localMap); 197 } 198 int size = localMap.size(); 199 for (int i = 0; i < size; i++) { 200 reportThrottlingIfNeeded(localMap.keyAt(i), localStatus, localMap.valueAt(i)); 201 } 202 } 203 updateVoteForDisplay(int displayId)204 private void updateVoteForDisplay(int displayId) { 205 @Temperature.ThrottlingStatus int localStatus; 206 SparseArray<SurfaceControl.RefreshRateRange> localMap; 207 208 synchronized (mThermalObserverLock) { 209 localStatus = mStatus; 210 localMap = mThermalThrottlingByDisplay.get(displayId); 211 } 212 if (localMap == null) { 213 Slog.d(TAG, "Updating votes, display already removed, display=" + displayId); 214 return; 215 } 216 if (mLoggingEnabled) { 217 Slog.d(TAG, "Updating votes for status=" + localStatus + ", display =" + displayId 218 + ", map=" + localMap); 219 } 220 reportThrottlingIfNeeded(displayId, localStatus, localMap); 221 } 222 reportThrottlingIfNeeded(int displayId, @Temperature.ThrottlingStatus int currentStatus, SparseArray<SurfaceControl.RefreshRateRange> throttlingMap)223 private void reportThrottlingIfNeeded(int displayId, 224 @Temperature.ThrottlingStatus int currentStatus, 225 SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) { 226 if (currentStatus == -1) { // no throttling status reported from thermal sensor yet 227 return; 228 } 229 230 if (throttlingMap.size() == 0) { // map is not configured, using default behaviour 231 fallbackReportThrottlingIfNeeded(displayId, currentStatus); 232 return; 233 } 234 235 SurfaceControl.RefreshRateRange foundRange = findBestMatchingRefreshRateRange(currentStatus, 236 throttlingMap); 237 // if status <= currentStatus not found in the map reset vote 238 Vote vote = null; 239 if (foundRange != null) { // otherwise vote with found range 240 vote = Vote.forRenderFrameRates(foundRange.min, foundRange.max); 241 } 242 mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote); 243 if (mLoggingEnabled) { 244 Slog.d(TAG, "Voted: vote=" + vote + ", display =" + displayId); 245 } 246 } 247 fallbackReportThrottlingIfNeeded(int displayId, @Temperature.ThrottlingStatus int currentStatus)248 private void fallbackReportThrottlingIfNeeded(int displayId, 249 @Temperature.ThrottlingStatus int currentStatus) { 250 Vote vote = null; 251 if (currentStatus >= Temperature.THROTTLING_CRITICAL) { 252 vote = Vote.forRenderFrameRates(0f, 60f); 253 } 254 mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote); 255 if (mLoggingEnabled) { 256 Slog.d(TAG, "Voted(fallback): vote=" + vote + ", display =" + displayId); 257 } 258 } 259 //endregion 260 dumpLocked(PrintWriter writer)261 void dumpLocked(PrintWriter writer) { 262 @Temperature.ThrottlingStatus int localStatus; 263 SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap; 264 265 synchronized (mThermalObserverLock) { 266 localStatus = mStatus; 267 localMap = mThermalThrottlingByDisplay.clone(); 268 } 269 270 writer.println(" SkinThermalStatusObserver:"); 271 writer.println(" mStatus: " + localStatus); 272 writer.println(" mThermalThrottlingByDisplay: " + localMap); 273 } 274 } 275