• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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