blob: 0ed90d27db996a4ad8d7b6a42a7dc24b3940a37e [file] [log] [blame]
/*
* Copyright (C) 2022 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;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.display.BrightnessInfo;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Message;
import android.os.PowerManager;
import android.os.Temperature.ThrottlingStatus;
import android.os.Temperature;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.display.BrightnessThrottler.Injector;
import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class BrightnessThrottlerTest {
private static final float EPSILON = 0.000001f;
private Handler mHandler;
private TestLooper mTestLooper;
@Mock IThermalService mThermalServiceMock;
@Mock Injector mInjectorMock;
@Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock);
mTestLooper = new TestLooper();
mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return true;
}
});
}
/////////////////
// Test Methods
/////////////////
@Test
public void testBrightnessThrottlingData() {
List<ThrottlingLevel> singleLevel = new ArrayList<>();
singleLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
List<ThrottlingLevel> validLevels = new ArrayList<>();
validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
List<ThrottlingLevel> unsortedThermalLevels = new ArrayList<>();
unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
List<ThrottlingLevel> unsortedBrightnessLevels = new ArrayList<>();
unsortedBrightnessLevels.add(
new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
unsortedBrightnessLevels.add(
new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
List<ThrottlingLevel> unsortedLevels = new ArrayList<>();
unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
List<ThrottlingLevel> invalidLevel = new ArrayList<>();
invalidLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
PowerManager.BRIGHTNESS_MAX + EPSILON));
// Test invalid data
BrightnessThrottlingData data;
data = BrightnessThrottlingData.create((List<ThrottlingLevel>)null);
assertEquals(data, null);
data = BrightnessThrottlingData.create((BrightnessThrottlingData)null);
assertEquals(data, null);
data = BrightnessThrottlingData.create(new ArrayList<ThrottlingLevel>());
assertEquals(data, null);
data = BrightnessThrottlingData.create(unsortedThermalLevels);
assertEquals(data, null);
data = BrightnessThrottlingData.create(unsortedBrightnessLevels);
assertEquals(data, null);
data = BrightnessThrottlingData.create(unsortedLevels);
assertEquals(data, null);
data = BrightnessThrottlingData.create(invalidLevel);
assertEquals(data, null);
// Test valid data
data = BrightnessThrottlingData.create(singleLevel);
assertNotEquals(data, null);
assertThrottlingLevelsEquals(singleLevel, data.throttlingLevels);
data = BrightnessThrottlingData.create(validLevels);
assertNotEquals(data, null);
assertThrottlingLevelsEquals(validLevels, data.throttlingLevels);
}
@Test
public void testThrottlingUnsupported() throws Exception {
final BrightnessThrottler throttler = createThrottlerUnsupported();
assertFalse(throttler.deviceSupportsThrottling());
// Thermal listener shouldn't be registered if throttling is unsupported
verify(mInjectorMock, never()).getThermalService();
// Ensure that brightness is uncapped when the device doesn't support throttling
assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
}
@Test
public void testThrottlingSingleLevel() throws Exception {
final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
0.25f);
List<ThrottlingLevel> levels = new ArrayList<>();
levels.add(level);
final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
final BrightnessThrottler throttler = createThrottlerSupported(data);
assertTrue(throttler.deviceSupportsThrottling());
verify(mThermalServiceMock).registerThermalEventListenerWithType(
mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
// Set status too low to trigger throttling
listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
mTestLooper.dispatchAll();
assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
assertFalse(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
// Set status just high enough to trigger throttling
listener.notifyThrottling(getSkinTemp(level.thermalStatus));
mTestLooper.dispatchAll();
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Set status more than high enough to trigger throttling
listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
mTestLooper.dispatchAll();
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Return to the lower throttling level
listener.notifyThrottling(getSkinTemp(level.thermalStatus));
mTestLooper.dispatchAll();
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Cool down
listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
mTestLooper.dispatchAll();
assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
assertFalse(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
throttler.getBrightnessMaxReason());
}
@Test
public void testThrottlingMultiLevel() throws Exception {
final ThrottlingLevel levelLo = new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE,
0.62f);
final ThrottlingLevel levelHi = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
0.25f);
List<ThrottlingLevel> levels = new ArrayList<>();
levels.add(levelLo);
levels.add(levelHi);
final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
final BrightnessThrottler throttler = createThrottlerSupported(data);
assertTrue(throttler.deviceSupportsThrottling());
verify(mThermalServiceMock).registerThermalEventListenerWithType(
mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
// Set status too low to trigger throttling
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
mTestLooper.dispatchAll();
assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
assertFalse(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
// Set status just high enough to trigger throttling
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
mTestLooper.dispatchAll();
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Set status to an intermediate throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
mTestLooper.dispatchAll();
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Set status to the highest configured throttling level
listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus));
mTestLooper.dispatchAll();
assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Set status to exceed the highest configured throttling level
listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus + 1));
mTestLooper.dispatchAll();
assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Return to an intermediate throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
mTestLooper.dispatchAll();
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Return to the lowest configured throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
mTestLooper.dispatchAll();
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
throttler.getBrightnessMaxReason());
// Cool down
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
mTestLooper.dispatchAll();
assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
assertFalse(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
}
private void assertThrottlingLevelsEquals(
List<ThrottlingLevel> expected,
List<ThrottlingLevel> actual) {
assertEquals(expected.size(), actual.size());
for (int i = 0; i < expected.size(); i++) {
ThrottlingLevel expectedLevel = expected.get(i);
ThrottlingLevel actualLevel = actual.get(i);
assertEquals(expectedLevel.thermalStatus, actualLevel.thermalStatus);
assertEquals(expectedLevel.brightness, actualLevel.brightness, 0.0f);
}
}
private BrightnessThrottler createThrottlerUnsupported() {
return new BrightnessThrottler(mInjectorMock, mHandler, null, () -> {});
}
private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) {
assertNotNull(data);
return new BrightnessThrottler(mInjectorMock, mHandler, data, () -> {});
}
private Temperature getSkinTemp(@ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
}