blob: e25c1c6c7291d06336891cfa75885cf8768b48d6 [file] [log] [blame]
/*
* Copyright (C) 2019 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.whitebalance;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
import android.util.TypedValue;
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
import com.android.server.display.TestUtils;
import com.android.server.display.whitebalance.AmbientFilter;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.List;
@RunWith(JUnit4.class)
public final class AmbientLuxTest {
private static final int AMBIENT_COLOR_TYPE = 20705;
private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE = 3456.7f;
private Handler mHandler = new Handler(Looper.getMainLooper());
private Sensor mLightSensor;
private Sensor mAmbientColorSensor;
private ContextWrapper mContextSpy;
private Resources mResourcesSpy;
@Mock private SensorManager mSensorManagerMock;
@Mock private TypedArray mBrightnesses;
@Mock private TypedArray mBiases;
@Mock private TypedArray mHighLightBrightnesses;
@Mock private TypedArray mHighLightBiases;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null);
mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
mResourcesSpy = spy(mContextSpy.getResources());
when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
when(mSensorManagerMock.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mLightSensor);
final List<Sensor> sensorList = ImmutableList.of(mLightSensor, mAmbientColorSensor);
when(mSensorManagerMock.getSensorList(Sensor.TYPE_ALL)).thenReturn(sensorList);
when(mResourcesSpy.getString(
R.string.config_displayWhiteBalanceColorTemperatureSensorName))
.thenReturn(AMBIENT_COLOR_TYPE_STR);
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceDecreaseDebounce))
.thenReturn(0);
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceIncreaseDebounce))
.thenReturn(0);
mockResourcesFloat(R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature,
LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperature,
HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceAmbientColorTemperatures))
.thenReturn(createTypedArray());
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceDisplayColorTemperatures))
.thenReturn(createTypedArray());
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceLowLightAmbientBrightnesses))
.thenReturn(mBrightnesses);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceLowLightAmbientBiases))
.thenReturn(mBiases);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceHighLightAmbientBrightnesses))
.thenReturn(mHighLightBrightnesses);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceHighLightAmbientBiases))
.thenReturn(mHighLightBiases);
mockThrottler();
}
@Test
public void testNoSpline() throws Exception {
setBrightnesses();
setBiases();
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
assertEquals(controller.mPendingAmbientColorTemperature,
ambientColorTemperature, 0.001);
}
}
@Test
public void testSpline_OneSegment() throws Exception {
final float lowerBrightness = 10.0f;
final float upperBrightness = 50.0f;
setBrightnesses(lowerBrightness, upperBrightness);
setBiases(0.0f, 1.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
setEstimatedBrightnessAndUpdate(controller,
mix(lowerBrightness, upperBrightness, t));
assertEquals(controller.mPendingAmbientColorTemperature,
mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, t), 0.001);
}
setEstimatedBrightnessAndUpdate(controller, 0.0f);
assertEquals(controller.mPendingAmbientColorTemperature,
LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f);
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
}
@Test
public void testSpline_TwoSegments() throws Exception {
final float brightness0 = 10.0f;
final float brightness1 = 50.0f;
final float brightness2 = 60.0f;
setBrightnesses(brightness0, brightness1, brightness2);
final float bias0 = 0.0f;
final float bias1 = 0.25f;
final float bias2 = 1.0f;
setBiases(bias0, bias1, bias2);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
float luxOverride = mix(brightness0, brightness1, t);
setEstimatedBrightnessAndUpdate(controller, luxOverride);
float bias = mix(bias0, bias1, t);
assertEquals(controller.mPendingAmbientColorTemperature,
mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, bias), 0.001);
}
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
float luxOverride = mix(brightness1, brightness2, t);
setEstimatedBrightnessAndUpdate(controller, luxOverride);
float bias = mix(bias1, bias2, t);
assertEquals(controller.mPendingAmbientColorTemperature,
mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, bias), 0.001);
}
setEstimatedBrightnessAndUpdate(controller, 0.0f);
assertEquals(controller.mPendingAmbientColorTemperature,
LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
setEstimatedBrightnessAndUpdate(controller, brightness2 + 1.0f);
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
}
@Test
public void testSpline_VerticalSegment() throws Exception {
final float lowerBrightness = 10.0f;
final float upperBrightness = 10.0f;
setBrightnesses(lowerBrightness, upperBrightness);
setBiases(0.0f, 1.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
setEstimatedBrightnessAndUpdate(controller, 0.0f);
assertEquals(controller.mPendingAmbientColorTemperature,
LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f);
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
}
@Test
public void testSpline_InvalidEndBias() throws Exception {
setBrightnesses(10.0f, 1000.0f);
setBiases(0.0f, 2.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
assertEquals(controller.mPendingAmbientColorTemperature,
ambientColorTemperature, 0.001);
}
}
@Test
public void testSpline_InvalidBeginBias() throws Exception {
setBrightnesses(10.0f, 1000.0f);
setBiases(0.1f, 1.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
assertEquals(controller.mPendingAmbientColorTemperature,
ambientColorTemperature, 0.001);
}
}
@Test
public void testSpline_OneSegmentHighLight() throws Exception {
final float lowerBrightness = 10.0f;
final float upperBrightness = 50.0f;
setHighLightBrightnesses(lowerBrightness, upperBrightness);
setHighLightBiases(0.0f, 1.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
setEstimatedBrightnessAndUpdate(controller,
mix(lowerBrightness, upperBrightness, t));
assertEquals(controller.mPendingAmbientColorTemperature,
mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - t),
0.001);
}
setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f);
assertEquals(controller.mPendingAmbientColorTemperature,
HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
setEstimatedBrightnessAndUpdate(controller, 0.0f);
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
}
@Test
public void testSpline_TwoSegmentsHighLight() throws Exception {
final float brightness0 = 10.0f;
final float brightness1 = 50.0f;
final float brightness2 = 60.0f;
setHighLightBrightnesses(brightness0, brightness1, brightness2);
final float bias0 = 0.0f;
final float bias1 = 0.25f;
final float bias2 = 1.0f;
setHighLightBiases(bias0, bias1, bias2);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 6000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
float luxOverride = mix(brightness0, brightness1, t);
setEstimatedBrightnessAndUpdate(controller, luxOverride);
float bias = mix(bias0, bias1, t);
assertEquals(controller.mPendingAmbientColorTemperature,
mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
0.01);
}
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
float luxOverride = mix(brightness1, brightness2, t);
setEstimatedBrightnessAndUpdate(controller, luxOverride);
float bias = mix(bias1, bias2, t);
assertEquals(controller.mPendingAmbientColorTemperature,
mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
0.01);
}
setEstimatedBrightnessAndUpdate(controller, brightness2 + 1.0f);
assertEquals(controller.mPendingAmbientColorTemperature,
HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
setEstimatedBrightnessAndUpdate(controller, 0.0f);
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
}
@Test
public void testSpline_InvalidCombinations() throws Exception {
setBrightnesses(100.0f, 200.0f);
setBiases(0.0f, 1.0f);
setHighLightBrightnesses(150.0f, 250.0f);
setHighLightBiases(0.0f, 1.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
assertEquals(controller.mPendingAmbientColorTemperature,
ambientColorTemperature, 0.001);
}
}
@Test
public void testLowLight_DefaultAmbient() throws Exception {
final float lowerBrightness = 10.0f;
final float upperBrightness = 50.0f;
setBrightnesses(lowerBrightness, upperBrightness);
setBiases(0.0f, 1.0f);
DisplayWhiteBalanceController controller =
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = -1.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
setEstimatedBrightnessAndUpdate(controller,
mix(lowerBrightness, upperBrightness, t));
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature,
0.001);
}
setEstimatedBrightnessAndUpdate(controller, 0.0f);
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f);
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
}
void mockThrottler() {
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0);
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceIncreaseDebounce)).thenReturn(0);
TypedArray base = mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceBaseThresholds);
TypedArray inc = mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceIncreaseThresholds);
TypedArray dec = mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceDecreaseThresholds);
base = spy(base);
inc = spy(inc);
dec = spy(dec);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceBaseThresholds)).thenReturn(base);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceIncreaseThresholds)).thenReturn(inc);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceDecreaseThresholds)).thenReturn(dec);
setFloatArrayResource(base, new float[]{0.0f});
setFloatArrayResource(inc, new float[]{0.0f});
setFloatArrayResource(dec, new float[]{0.0f});
}
private void mockResourcesFloat(int id, float floatValue) {
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) {
TypedValue value = (TypedValue)invocation.getArgument(1);
value.type = TypedValue.TYPE_FLOAT;
value.data = Float.floatToIntBits(floatValue);
return null;
}
}).when(mResourcesSpy).getValue(
eq(id),
any(TypedValue.class), eq(true));
}
private void setEstimatedColorTemperature(DisplayWhiteBalanceController controller,
float ambientColorTemperature) {
AmbientFilter colorTemperatureFilter = spy(controller.mColorTemperatureFilter);
controller.mColorTemperatureFilter = colorTemperatureFilter;
when(colorTemperatureFilter.getEstimate(anyLong())).thenReturn(ambientColorTemperature);
}
private void setEstimatedBrightnessAndUpdate(DisplayWhiteBalanceController controller,
float brightness) {
when(controller.mBrightnessFilter.getEstimate(anyLong())).thenReturn(brightness);
controller.updateAmbientColorTemperature();
}
private void setBrightnesses(float... vals) {
setFloatArrayResource(mBrightnesses, vals);
}
private void setBiases(float... vals) {
setFloatArrayResource(mBiases, vals);
}
private void setHighLightBrightnesses(float... vals) {
setFloatArrayResource(mHighLightBrightnesses, vals);
}
private void setHighLightBiases(float... vals) {
setFloatArrayResource(mHighLightBiases, vals);
}
private void setFloatArrayResource(TypedArray array, float[] vals) {
when(array.length()).thenReturn(vals.length);
for (int i = 0; i < vals.length; i++) {
when(array.getFloat(i, Float.NaN)).thenReturn(vals[i]);
}
}
private TypedArray createTypedArray() throws Exception {
TypedArray mockArray = mock(TypedArray.class);
return mockArray;
}
private static float mix(float a, float b, float t) {
return (1.0f - t) * a + t * b;
}
}