blob: 58e15503be7d0db29c1d423ba6f6b26ec90e4267 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.display.mode;
import android.annotation.Nullable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.Temperature;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import java.io.PrintWriter;
final class SkinThermalStatusObserver extends IThermalEventListener.Stub implements
DisplayManager.DisplayListener {
private static final String TAG = "SkinThermalStatusObserver";
private final VotesStorage mVotesStorage;
private final DisplayModeDirector.Injector mInjector;
private boolean mLoggingEnabled;
private final Handler mHandler;
private final Object mThermalObserverLock = new Object();
@GuardedBy("mThermalObserverLock")
@Temperature.ThrottlingStatus
private int mStatus = -1;
@GuardedBy("mThermalObserverLock")
private final SparseArray<SparseArray<SurfaceControl.RefreshRateRange>>
mThermalThrottlingByDisplay = new SparseArray<>();
SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
VotesStorage votesStorage) {
this(injector, votesStorage, BackgroundThread.getHandler());
}
@VisibleForTesting
SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
VotesStorage votesStorage, Handler handler) {
mInjector = injector;
mVotesStorage = votesStorage;
mHandler = handler;
}
void observe() {
// if failed to register thermal service listener, don't register display listener
if (!mInjector.registerThermalServiceListener(this)) {
return;
}
mInjector.registerDisplayListener(this, mHandler,
DisplayManager.EVENT_FLAG_DISPLAY_ADDED | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
populateInitialDisplayInfo();
}
void setLoggingEnabled(boolean enabled) {
mLoggingEnabled = enabled;
}
@Override
public void notifyThrottling(Temperature temp) {
@Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
synchronized (mThermalObserverLock) {
if (mStatus == currentStatus) {
return; // status not changed, skip update
}
mStatus = currentStatus;
mHandler.post(this::updateVotes);
}
if (mLoggingEnabled) {
Slog.d(TAG, "New thermal throttling status " + ", current thermal status = "
+ currentStatus);
}
}
//region DisplayManager.DisplayListener
@Override
public void onDisplayAdded(int displayId) {
updateThermalRefreshRateThrottling(displayId);
if (mLoggingEnabled) {
Slog.d(TAG, "Display added:" + displayId);
}
}
@Override
public void onDisplayRemoved(int displayId) {
synchronized (mThermalObserverLock) {
mThermalThrottlingByDisplay.remove(displayId);
mHandler.post(() -> mVotesStorage.updateVote(displayId,
Vote.PRIORITY_SKIN_TEMPERATURE, null));
}
if (mLoggingEnabled) {
Slog.d(TAG, "Display removed and voted: displayId=" + displayId);
}
}
@Override
public void onDisplayChanged(int displayId) {
updateThermalRefreshRateThrottling(displayId);
if (mLoggingEnabled) {
Slog.d(TAG, "Display changed:" + displayId);
}
}
//endregion
private void populateInitialDisplayInfo() {
DisplayInfo info = new DisplayInfo();
Display[] displays = mInjector.getDisplays();
int size = displays.length;
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap = new SparseArray<>(
size);
for (Display d : displays) {
final int displayId = d.getDisplayId();
d.getDisplayInfo(info);
localMap.put(displayId, info.thermalRefreshRateThrottling);
}
synchronized (mThermalObserverLock) {
for (int i = 0; i < size; i++) {
mThermalThrottlingByDisplay.put(localMap.keyAt(i), localMap.valueAt(i));
}
}
if (mLoggingEnabled) {
Slog.d(TAG, "Display initial info:" + localMap);
}
}
private void updateThermalRefreshRateThrottling(int displayId) {
DisplayInfo displayInfo = new DisplayInfo();
mInjector.getDisplayInfo(displayId, displayInfo);
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap =
displayInfo.thermalRefreshRateThrottling;
synchronized (mThermalObserverLock) {
mThermalThrottlingByDisplay.put(displayId, throttlingMap);
mHandler.post(() -> updateVoteForDisplay(displayId));
}
if (mLoggingEnabled) {
Slog.d(TAG,
"Thermal throttling updated: display=" + displayId + ", map=" + throttlingMap);
}
}
//region in mHandler thread
private void updateVotes() {
@Temperature.ThrottlingStatus int localStatus;
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap;
synchronized (mThermalObserverLock) {
localStatus = mStatus;
localMap = mThermalThrottlingByDisplay.clone();
}
if (mLoggingEnabled) {
Slog.d(TAG, "Updating votes for status=" + localStatus + ", map=" + localMap);
}
int size = localMap.size();
for (int i = 0; i < size; i++) {
reportThrottlingIfNeeded(localMap.keyAt(i), localStatus, localMap.valueAt(i));
}
}
private void updateVoteForDisplay(int displayId) {
@Temperature.ThrottlingStatus int localStatus;
SparseArray<SurfaceControl.RefreshRateRange> localMap;
synchronized (mThermalObserverLock) {
localStatus = mStatus;
localMap = mThermalThrottlingByDisplay.get(displayId);
}
if (localMap == null) {
Slog.d(TAG, "Updating votes, display already removed, display=" + displayId);
return;
}
if (mLoggingEnabled) {
Slog.d(TAG, "Updating votes for status=" + localStatus + ", display =" + displayId
+ ", map=" + localMap);
}
reportThrottlingIfNeeded(displayId, localStatus, localMap);
}
private void reportThrottlingIfNeeded(int displayId,
@Temperature.ThrottlingStatus int currentStatus,
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
if (currentStatus == -1) { // no throttling status reported from thermal sensor yet
return;
}
if (throttlingMap.size() == 0) { // map is not configured, using default behaviour
fallbackReportThrottlingIfNeeded(displayId, currentStatus);
return;
}
SurfaceControl.RefreshRateRange foundRange = findBestMatchingRefreshRateRange(currentStatus,
throttlingMap);
// if status <= currentStatus not found in the map reset vote
Vote vote = null;
if (foundRange != null) { // otherwise vote with found range
vote = Vote.forRenderFrameRates(foundRange.min, foundRange.max);
}
mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
if (mLoggingEnabled) {
Slog.d(TAG, "Voted: vote=" + vote + ", display =" + displayId);
}
}
@Nullable
private SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
@Temperature.ThrottlingStatus int currentStatus,
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
SurfaceControl.RefreshRateRange foundRange = null;
for (int status = currentStatus; status >= 0; status--) {
foundRange = throttlingMap.get(status);
if (foundRange != null) {
break;
}
}
return foundRange;
}
private void fallbackReportThrottlingIfNeeded(int displayId,
@Temperature.ThrottlingStatus int currentStatus) {
Vote vote = null;
if (currentStatus >= Temperature.THROTTLING_CRITICAL) {
vote = Vote.forRenderFrameRates(0f, 60f);
}
mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
if (mLoggingEnabled) {
Slog.d(TAG, "Voted(fallback): vote=" + vote + ", display =" + displayId);
}
}
//endregion
void dumpLocked(PrintWriter writer) {
@Temperature.ThrottlingStatus int localStatus;
SparseArray<SparseArray<SurfaceControl.RefreshRateRange>> localMap;
synchronized (mThermalObserverLock) {
localStatus = mStatus;
localMap = mThermalThrottlingByDisplay.clone();
}
writer.println(" SkinThermalStatusObserver:");
writer.println(" mStatus: " + localStatus);
writer.println(" mThermalThrottlingByDisplay: " + localMap);
}
}