Limit refresh rates if skin temperature is high

Stay at low refresh rates (0 to 60) if the thermal status is or above
THROTTLING_CRITICAL.

Bug: 171277171
Test: atest DisplayModeDirectorTest
Test: heat the device to CRITICAL and check the votes in dumpsys
      cool down the device to SEVERE and check the votes in dumpsys
Change-Id: I37b5b05a363a3d4f2ed3626109206863b664ba66
Merged-In: I37b5b05a363a3d4f2ed3626109206863b664ba66
This commit is contained in:
Long Ling
2021-09-01 14:07:25 -07:00
committed by Daniel Solomon
parent d92e142ac4
commit 7e783bb071
2 changed files with 123 additions and 4 deletions

View File

@@ -36,9 +36,14 @@ import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.net.Uri;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Temperature;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -108,6 +113,7 @@ public class DisplayModeDirector {
private final UdfpsObserver mUdfpsObserver;
private final SensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
private final DeviceConfigInterface mDeviceConfig;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@@ -156,6 +162,7 @@ public class DisplayModeDirector {
};
mSensorObserver = new SensorObserver(context, ballotBox, injector);
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
@@ -174,6 +181,7 @@ public class DisplayModeDirector {
mBrightnessObserver.observe(sensorManager);
mSensorObserver.observe();
mHbmObserver.observe();
mSkinThermalStatusObserver.observe();
synchronized (mLock) {
// We may have a listener already registered before the call to start, so go ahead and
// notify them to pick up our newly initialized state.
@@ -606,6 +614,7 @@ public class DisplayModeDirector {
mUdfpsObserver.dumpLocked(pw);
mSensorObserver.dumpLocked(pw);
mHbmObserver.dumpLocked(pw);
mSkinThermalStatusObserver.dumpLocked(pw);
}
}
@@ -714,7 +723,6 @@ public class DisplayModeDirector {
return mUdfpsObserver;
}
@VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
@@ -950,16 +958,19 @@ public class DisplayModeDirector {
// user seeing the display flickering when the switches occur.
public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
public static final int PRIORITY_SKIN_TEMPERATURE = 9;
// High-brightness-mode may need a specific range of refresh-rates to function properly.
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 9;
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 10;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
public static final int PRIORITY_PROXIMITY = 10;
public static final int PRIORITY_PROXIMITY = 11;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
public static final int PRIORITY_UDFPS = 11;
public static final int PRIORITY_UDFPS = 12;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -1054,6 +1065,8 @@ public class DisplayModeDirector {
return "PRIORITY_PROXIMITY";
case PRIORITY_LOW_POWER_MODE:
return "PRIORITY_LOW_POWER_MODE";
case PRIORITY_SKIN_TEMPERATURE:
return "PRIORITY_SKIN_TEMPERATURE";
case PRIORITY_UDFPS:
return "PRIORITY_UDFPS";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
@@ -2309,6 +2322,52 @@ public class DisplayModeDirector {
}
}
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
private final BallotBox mBallotBox;
private final Injector mInjector;
private @Temperature.ThrottlingStatus int mStatus = -1;
SkinThermalStatusObserver(Injector injector, BallotBox ballotBox) {
mInjector = injector;
mBallotBox = ballotBox;
}
@Override
public void notifyThrottling(Temperature temp) {
mStatus = temp.getStatus();
if (mLoggingEnabled) {
Slog.d(TAG, "New thermal throttling status "
+ ", current thermal status = " + mStatus);
}
final Vote vote;
if (mStatus >= Temperature.THROTTLING_CRITICAL) {
vote = Vote.forRefreshRates(0f, 60f);
} else {
vote = null;
}
mBallotBox.vote(GLOBAL_ID, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
}
public void observe() {
IThermalService thermalService = mInjector.getThermalService();
if (thermalService == null) {
Slog.w(TAG, "Could not observe thermal status. Service not available");
return;
}
try {
thermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register thermal status listener", e);
}
}
void dumpLocked(PrintWriter writer) {
writer.println(" SkinThermalStatusObserver:");
writer.println(" mStatus: " + mStatus);
}
}
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public DeviceConfigDisplaySettings() {
}
@@ -2470,6 +2529,8 @@ public class DisplayModeDirector {
BrightnessInfo getBrightnessInfo(int displayId);
boolean isDozeState(Display d);
IThermalService getThermalService();
}
@VisibleForTesting
@@ -2530,6 +2591,12 @@ public class DisplayModeDirector {
return Display.isDozeState(d.getState());
}
@Override
public IThermalService getThermalService() {
return IThermalService.Stub.asInterface(
ServiceManager.getService(Context.THERMAL_SERVICE));
}
private DisplayManager getDisplayManager() {
if (mDisplayManager == null) {
mDisplayManager = mContext.getSystemService(DisplayManager.class);

View File

@@ -56,7 +56,11 @@ import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Looper;
import android.os.RemoteException;
import android.os.Temperature;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
@@ -116,6 +120,8 @@ public class DisplayModeDirectorTest {
public SensorManagerInternal mSensorManagerInternalMock;
@Mock
public DisplayManagerInternal mDisplayManagerInternalMock;
@Mock
public IThermalService mThermalServiceMock;
@Before
public void setUp() throws Exception {
@@ -124,6 +130,7 @@ public class DisplayModeDirectorTest {
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
when(mContext.getContentResolver()).thenReturn(resolver);
mInjector = spy(new FakesInjector());
when(mInjector.getThermalService()).thenReturn(mThermalServiceMock);
mHandler = new Handler(Looper.getMainLooper());
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -1547,12 +1554,52 @@ public class DisplayModeDirectorTest {
assertNull(vote);
}
@Test
public void testSkinTemperature() throws RemoteException {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
director.start(createMockSensorManager());
ArgumentCaptor<IThermalEventListener> thermalEventListener =
ArgumentCaptor.forClass(IThermalEventListener.class);
verify(mThermalServiceMock).registerThermalEventListenerWithType(
thermalEventListener.capture(), eq(Temperature.TYPE_SKIN));
final IThermalEventListener listener = thermalEventListener.getValue();
// Verify that there is no skin temperature vote initially.
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
assertNull(vote);
// Set the skin temperature to critical and verify that we added a vote.
listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
assertVoteForRefreshRateRange(vote, 0f, 60.f);
// Set the skin temperature to severe and verify that the vote is gone.
listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE));
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
assertNull(vote);
}
private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
private void assertVoteForRefreshRate(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
}
private void assertVoteForRefreshRateRange(
Vote vote, float refreshRateLow, float refreshRateHigh) {
assertThat(vote).isNotNull();
final RefreshRateRange expectedRange =
new RefreshRateRange(refreshRateLow, refreshRateHigh);
assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
}
public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
@Override
public String getProperty(String namespace, String name) {
@@ -1748,6 +1795,11 @@ public class DisplayModeDirectorTest {
return false;
}
@Override
public IThermalService getThermalService() {
return null;
}
void notifyPeakRefreshRateChanged() {
if (mPeakRefreshRateObserver != null) {
mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,