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 87 mInjector.registerDisplayListener(this, mHandler, 88 DisplayManager.EVENT_FLAG_DISPLAY_ADDED | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED 89 | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED); 90 91 populateInitialDisplayInfo(); 92 } 93 setLoggingEnabled(boolean enabled)94 void setLoggingEnabled(boolean enabled) { 95 mLoggingEnabled = enabled; 96 } 97 98 @Override notifyThrottling(Temperature temp)99 public void notifyThrottling(Temperature temp) { 100 @Temperature.ThrottlingStatus int currentStatus = temp.getStatus(); 101 102 synchronized (mThermalObserverLock) { 103 if (mStatus == currentStatus) { 104 return; // status not changed, skip update 105 } 106 mStatus = currentStatus; 107 mHandler.post(this::updateVotes); 108 } 109 110 if (mLoggingEnabled) { 111 Slog.d(TAG, "New thermal throttling status " + ", current thermal status = " 112 + currentStatus); 113 } 114 } 115 116 //region DisplayManager.DisplayListener 117 @Override onDisplayAdded(int displayId)118 public void onDisplayAdded(int displayId) { 119 updateThermalRefreshRateThrottling(displayId); 120 if (mLoggingEnabled) { 121 Slog.d(TAG, "Display added:" + displayId); 122 } 123 } 124 125 @Override onDisplayRemoved(int displayId)126 public void onDisplayRemoved(int displayId) { 127 synchronized (mThermalObserverLock) { 128 mThermalThrottlingByDisplay.remove(displayId); 129 mHandler.post(() -> mVotesStorage.updateVote(displayId, 130 Vote.PRIORITY_SKIN_TEMPERATURE, null)); 131 } 132 if (mLoggingEnabled) { 133 Slog.d(TAG, "Display removed and voted: displayId=" + displayId); 134 } 135 } 136 137 @Override onDisplayChanged(int displayId)138 public void onDisplayChanged(int displayId) { 139 updateThermalRefreshRateThrottling(displayId); 140 if (mLoggingEnabled) { 141 Slog.d(TAG, "Display changed:" + displayId); 142 } 143 } 144 //endregion 145 populateInitialDisplayInfo()146 private void populateInitialDisplayInfo() { 147 DisplayInfo info = new DisplayInfo(); 148 Display[] displays = mInjector.getDisplays(); 149 int size = displays.length; 150 SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap = new SparseArray<>( 151 size); 152 for (Display d : displays) { 153 final int displayId = d.getDisplayId(); 154 d.getDisplayInfo(info); 155 localMap.put(displayId, info.thermalRefreshRateThrottling); 156 } 157 synchronized (mThermalObserverLock) { 158 for (int i = 0; i < size; i++) { 159 mThermalThrottlingByDisplay.put(localMap.keyAt(i), localMap.valueAt(i)); 160 } 161 } 162 if (mLoggingEnabled) { 163 Slog.d(TAG, "Display initial info:" + localMap); 164 } 165 } 166 updateThermalRefreshRateThrottling(int displayId)167 private void updateThermalRefreshRateThrottling(int displayId) { 168 DisplayInfo displayInfo = new DisplayInfo(); 169 mInjector.getDisplayInfo(displayId, displayInfo); 170 SparseArray<SurfaceControl.RefreshRateRange> throttlingMap = 171 displayInfo.thermalRefreshRateThrottling; 172 173 synchronized (mThermalObserverLock) { 174 mThermalThrottlingByDisplay.put(displayId, throttlingMap); 175 mHandler.post(() -> updateVoteForDisplay(displayId)); 176 } 177 if (mLoggingEnabled) { 178 Slog.d(TAG, 179 "Thermal throttling updated: display=" + displayId + ", map=" + throttlingMap); 180 } 181 } 182 183 //region in mHandler thread updateVotes()184 private void updateVotes() { 185 @Temperature.ThrottlingStatus int localStatus; 186 SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap; 187 188 synchronized (mThermalObserverLock) { 189 localStatus = mStatus; 190 localMap = mThermalThrottlingByDisplay.clone(); 191 } 192 if (mLoggingEnabled) { 193 Slog.d(TAG, "Updating votes for status=" + localStatus + ", map=" + localMap); 194 } 195 int size = localMap.size(); 196 for (int i = 0; i < size; i++) { 197 reportThrottlingIfNeeded(localMap.keyAt(i), localStatus, localMap.valueAt(i)); 198 } 199 } 200 updateVoteForDisplay(int displayId)201 private void updateVoteForDisplay(int displayId) { 202 @Temperature.ThrottlingStatus int localStatus; 203 SparseArray<SurfaceControl.RefreshRateRange> localMap; 204 205 synchronized (mThermalObserverLock) { 206 localStatus = mStatus; 207 localMap = mThermalThrottlingByDisplay.get(displayId); 208 } 209 if (localMap == null) { 210 Slog.d(TAG, "Updating votes, display already removed, display=" + displayId); 211 return; 212 } 213 if (mLoggingEnabled) { 214 Slog.d(TAG, "Updating votes for status=" + localStatus + ", display =" + displayId 215 + ", map=" + localMap); 216 } 217 reportThrottlingIfNeeded(displayId, localStatus, localMap); 218 } 219 reportThrottlingIfNeeded(int displayId, @Temperature.ThrottlingStatus int currentStatus, SparseArray<SurfaceControl.RefreshRateRange> throttlingMap)220 private void reportThrottlingIfNeeded(int displayId, 221 @Temperature.ThrottlingStatus int currentStatus, 222 SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) { 223 if (currentStatus == -1) { // no throttling status reported from thermal sensor yet 224 return; 225 } 226 227 if (throttlingMap.size() == 0) { // map is not configured, using default behaviour 228 fallbackReportThrottlingIfNeeded(displayId, currentStatus); 229 return; 230 } 231 232 SurfaceControl.RefreshRateRange foundRange = findBestMatchingRefreshRateRange(currentStatus, 233 throttlingMap); 234 // if status <= currentStatus not found in the map reset vote 235 Vote vote = null; 236 if (foundRange != null) { // otherwise vote with found range 237 vote = Vote.forRenderFrameRates(foundRange.min, foundRange.max); 238 } 239 mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote); 240 if (mLoggingEnabled) { 241 Slog.d(TAG, "Voted: vote=" + vote + ", display =" + displayId); 242 } 243 } 244 fallbackReportThrottlingIfNeeded(int displayId, @Temperature.ThrottlingStatus int currentStatus)245 private void fallbackReportThrottlingIfNeeded(int displayId, 246 @Temperature.ThrottlingStatus int currentStatus) { 247 Vote vote = null; 248 if (currentStatus >= Temperature.THROTTLING_CRITICAL) { 249 vote = Vote.forRenderFrameRates(0f, 60f); 250 } 251 mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote); 252 if (mLoggingEnabled) { 253 Slog.d(TAG, "Voted(fallback): vote=" + vote + ", display =" + displayId); 254 } 255 } 256 //endregion 257 dumpLocked(PrintWriter writer)258 void dumpLocked(PrintWriter writer) { 259 @Temperature.ThrottlingStatus int localStatus; 260 SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap; 261 262 synchronized (mThermalObserverLock) { 263 localStatus = mStatus; 264 localMap = mThermalThrottlingByDisplay.clone(); 265 } 266 267 writer.println(" SkinThermalStatusObserver:"); 268 writer.println(" mStatus: " + localStatus); 269 writer.println(" mThermalThrottlingByDisplay: " + localMap); 270 } 271 } 272