blob: 9e7ef6539c2bb60caae050ce54607abaa938edca [file] [log] [blame]
Michael Wrighteef0e132017-11-21 17:57:52 +00001/*
2 * Copyright 2017 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
17package com.android.server.display;
18
19import static org.junit.Assert.assertEquals;
Michael Wrightd5df3612018-01-02 12:44:52 +000020import static org.junit.Assert.assertNotEquals;
Michael Wrighteef0e132017-11-21 17:57:52 +000021import static org.junit.Assert.assertNotNull;
22import static org.junit.Assert.assertNull;
23import static org.junit.Assert.assertTrue;
Michael Wright144aac92017-12-21 18:37:41 +000024import static org.mockito.ArgumentMatchers.anyFloat;
25import static org.mockito.ArgumentMatchers.anyInt;
26import static org.mockito.Mockito.mock;
27import static org.mockito.Mockito.when;
Michael Wrighteef0e132017-11-21 17:57:52 +000028
Michael Wright144aac92017-12-21 18:37:41 +000029import android.content.res.Resources;
30import android.content.res.TypedArray;
Michael Wrightd5df3612018-01-02 12:44:52 +000031import android.hardware.display.BrightnessConfiguration;
Michael Wrighteef0e132017-11-21 17:57:52 +000032import android.os.PowerManager;
33import android.support.test.filters.SmallTest;
34import android.support.test.runner.AndroidJUnit4;
35import android.util.Spline;
36
37import org.junit.Test;
38import org.junit.runner.RunWith;
39
40import java.util.Arrays;
41
42@SmallTest
43@RunWith(AndroidJUnit4.class)
44public class BrightnessMappingStrategyTest {
45
Michael Wright144aac92017-12-21 18:37:41 +000046 private static final int[] LUX_LEVELS = {
47 0,
48 5,
49 20,
50 40,
51 100,
52 325,
53 600,
54 1250,
55 2200,
56 4000,
57 5000
Michael Wrighteef0e132017-11-21 17:57:52 +000058 };
59
60 private static final float[] DISPLAY_LEVELS_NITS = {
61 13.25f,
62 54.0f,
63 78.85f,
64 105.02f,
65 132.7f,
66 170.12f,
67 212.1f,
68 265.2f,
69 335.8f,
70 415.2f,
71 478.5f,
72 };
73
74 private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
75 9,
76 30,
77 45,
78 62,
79 78,
80 96,
81 119,
82 146,
83 178,
84 221,
85 255
86 };
87
88 private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
89 private static final int[] BACKLIGHT_RANGE = { 1, 255 };
90
Michael Wright144aac92017-12-21 18:37:41 +000091 private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
92 private static final int[] EMPTY_INT_ARRAY = new int[0];
93
Michael Wrighteef0e132017-11-21 17:57:52 +000094 @Test
95 public void testSimpleStrategyMappingAtControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +000096 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
97 BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +000098 assertNotNull("BrightnessMappingStrategy should not be null", simple);
99 for (int i = 0; i < LUX_LEVELS.length; i++) {
100 final float expectedLevel =
101 (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON;
102 assertEquals(expectedLevel,
103 simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
104 }
105 }
106
107 @Test
108 public void testSimpleStrategyMappingBetweenControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +0000109 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
110 BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000111 assertNotNull("BrightnessMappingStrategy should not be null", simple);
112 for (int i = 1; i < LUX_LEVELS.length; i++) {
113 final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
114 final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
115 assertTrue("Desired brightness should be between adjacent control points.",
Michael Wrightd5df3612018-01-02 12:44:52 +0000116 backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1]
Michael Wrighteef0e132017-11-21 17:57:52 +0000117 && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
118 }
119 }
120
121 @Test
Michael Wrightd5df3612018-01-02 12:44:52 +0000122 public void testSimpleStrategyIgnoresNewConfiguration() {
123 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
124 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
125
126 final int N = LUX_LEVELS.length;
127 final float[] lux = { 0f, 1f };
128 final float[] nits = { 0, PowerManager.BRIGHTNESS_ON };
129
130 BrightnessConfiguration config = new BrightnessConfiguration.Builder()
131 .setCurve(lux, nits)
132 .build();
133 strategy.setBrightnessConfiguration(config);
134 assertNotEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/);
135 }
136
137 @Test
138 public void testSimpleStrategyIgnoresNullConfiguration() {
139 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
140 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
141
142 strategy.setBrightnessConfiguration(null);
143 final int N = DISPLAY_LEVELS_BACKLIGHT.length;
144 final float expectedBrightness =
145 (float) DISPLAY_LEVELS_BACKLIGHT[N - 1] / PowerManager.BRIGHTNESS_ON;
146 assertEquals(expectedBrightness,
147 strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01 /*tolerance*/);
148 }
149
150 @Test
Michael Wrighteef0e132017-11-21 17:57:52 +0000151 public void testPhysicalStrategyMappingAtControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +0000152 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
153 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
154 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000155 assertNotNull("BrightnessMappingStrategy should not be null", physical);
156 for (int i = 0; i < LUX_LEVELS.length; i++) {
157 final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1];
158 assertEquals(expectedLevel,
159 physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
160 }
161 }
162
163 @Test
164 public void testPhysicalStrategyMappingBetweenControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +0000165 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
166 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
167 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000168 assertNotNull("BrightnessMappingStrategy should not be null", physical);
169 Spline backlightToBrightness =
170 Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS);
171 for (int i = 1; i < LUX_LEVELS.length; i++) {
172 final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
173 final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
174 final float nits = backlightToBrightness.interpolate(backlight);
175 assertTrue("Desired brightness should be between adjacent control points.",
Michael Wrightd5df3612018-01-02 12:44:52 +0000176 nits > DISPLAY_LEVELS_NITS[i - 1] && nits < DISPLAY_LEVELS_NITS[i]);
Michael Wrighteef0e132017-11-21 17:57:52 +0000177 }
178 }
179
180 @Test
Michael Wrightd5df3612018-01-02 12:44:52 +0000181 public void testPhysicalStrategyUsesNewConfigurations() {
182 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
183 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
184 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
185
186 final float[] lux = { 0f, 1f };
187 final float[] nits = {
188 DISPLAY_RANGE_NITS[0],
189 DISPLAY_RANGE_NITS[DISPLAY_RANGE_NITS.length - 1]
190 };
191
192 BrightnessConfiguration config = new BrightnessConfiguration.Builder()
193 .setCurve(lux, nits)
194 .build();
195 strategy.setBrightnessConfiguration(config);
196 assertEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/);
197
198 // Check that null returns us to the default configuration.
199 strategy.setBrightnessConfiguration(null);
200 final int N = DISPLAY_LEVELS_NITS.length;
201 final float expectedBrightness = DISPLAY_LEVELS_NITS[N - 1] / DISPLAY_RANGE_NITS[1];
202 assertEquals(expectedBrightness,
203 strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01f /*tolerance*/);
204 }
205
206 @Test
Michael Wrighteef0e132017-11-21 17:57:52 +0000207 public void testDefaultStrategyIsPhysical() {
Michael Wright144aac92017-12-21 18:37:41 +0000208 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
Michael Wrighteef0e132017-11-21 17:57:52 +0000209 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
Michael Wright144aac92017-12-21 18:37:41 +0000210 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000211 assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
212 }
213
214 @Test
215 public void testNonStrictlyIncreasingLuxLevelsFails() {
Michael Wright144aac92017-12-21 18:37:41 +0000216 final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
Michael Wrighteef0e132017-11-21 17:57:52 +0000217 final int idx = lux.length / 2;
Michael Wright144aac92017-12-21 18:37:41 +0000218 int tmp = lux[idx];
Michael Wrighteef0e132017-11-21 17:57:52 +0000219 lux[idx] = lux[idx+1];
220 lux[idx+1] = tmp;
Michael Wright144aac92017-12-21 18:37:41 +0000221 Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
222 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
223 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000224 assertNull(strategy);
225
226 // And make sure we get the same result even if it's monotone but not increasing.
227 lux[idx] = lux[idx+1];
Michael Wright144aac92017-12-21 18:37:41 +0000228 res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
229 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000230 assertNull(strategy);
231 }
232
233 @Test
234 public void testDifferentNumberOfControlPointValuesFails() {
235 //Extra lux level
Michael Wright144aac92017-12-21 18:37:41 +0000236 final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1);
Michael Wrighteef0e132017-11-21 17:57:52 +0000237 // Make sure it's strictly increasing so that the only failure is the differing array
238 // lengths
239 lux[lux.length - 1] = lux[lux.length - 2] + 1;
Michael Wright144aac92017-12-21 18:37:41 +0000240 Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
241 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
242 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000243 assertNull(strategy);
244
Michael Wright144aac92017-12-21 18:37:41 +0000245 res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT);
246 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000247 assertNull(strategy);
248
249 // Extra backlight level
250 final int[] backlight = Arrays.copyOf(
251 DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
252 backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
Michael Wright144aac92017-12-21 18:37:41 +0000253 res = createResources(LUX_LEVELS, backlight);
254 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000255 assertNull(strategy);
256
257 // Extra nits level
258 final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
259 nits[nits.length - 1] = nits[nits.length - 2] + 1;
Michael Wright144aac92017-12-21 18:37:41 +0000260 res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
261 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000262 assertNull(strategy);
263 }
264
265 @Test
266 public void testPhysicalStrategyRequiresNitsMapping() {
Michael Wright144aac92017-12-21 18:37:41 +0000267 Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
268 DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE);
269 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000270 assertNull(physical);
271
Michael Wright144aac92017-12-21 18:37:41 +0000272 res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
273 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/);
274 physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000275 assertNull(physical);
276
Michael Wright144aac92017-12-21 18:37:41 +0000277 res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
278 DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/,
279 EMPTY_INT_ARRAY /*backlightRange*/);
280 physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000281 assertNull(physical);
282 }
283
284 private static float[] toFloatArray(int[] vals) {
285 float[] newVals = new float[vals.length];
286 for (int i = 0; i < vals.length; i++) {
287 newVals[i] = (float) vals[i];
288 }
289 return newVals;
290 }
Michael Wright144aac92017-12-21 18:37:41 +0000291
292 private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) {
293 return createResources(luxLevels, brightnessLevelsBacklight,
294 EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/,
295 EMPTY_INT_ARRAY /*backlightRange*/);
296 }
297
298 private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits,
299 float[] nitsRange, int[] backlightRange) {
300 return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
301 brightnessLevelsNits, nitsRange, backlightRange);
302 }
303
304 private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
305 float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) {
306 Resources mockResources = mock(Resources.class);
307 // For historical reasons, the lux levels resource implicitly defines the first point as 0,
308 // so we need to chop it off of the array the mock resource object returns.
309 int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length);
310 when(mockResources.getIntArray(com.android.internal.R.array.config_autoBrightnessLevels))
311 .thenReturn(luxLevelsResource);
312
313 when(mockResources.getIntArray(
314 com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
315 .thenReturn(brightnessLevelsBacklight);
316
317 TypedArray mockBrightnessLevelNits = createFloatTypedArray(brightnessLevelsNits);
318 when(mockResources.obtainTypedArray(
319 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
320 .thenReturn(mockBrightnessLevelNits);
321
322 TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
323 when(mockResources.obtainTypedArray(
324 com.android.internal.R.array.config_screenBrightnessNits))
325 .thenReturn(mockNitsRange);
326
327 when(mockResources.getIntArray(
328 com.android.internal.R.array.config_screenBrightnessBacklight))
329 .thenReturn(backlightRange);
330
331 when(mockResources.getInteger(
332 com.android.internal.R.integer.config_screenBrightnessSettingMinimum))
333 .thenReturn(1);
334 when(mockResources.getInteger(
335 com.android.internal.R.integer.config_screenBrightnessSettingMaximum))
336 .thenReturn(255);
337 return mockResources;
338 }
339
340 private TypedArray createFloatTypedArray(float[] vals) {
341 TypedArray mockArray = mock(TypedArray.class);
342 when(mockArray.length()).thenAnswer(invocation -> {
343 return vals.length;
344 });
345 when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> {
346 final float def = (float) invocation.getArguments()[1];
347 if (vals == null) {
348 return def;
349 }
350 int idx = (int) invocation.getArguments()[0];
351 if (idx >= 0 && idx < vals.length) {
352 return vals[idx];
353 } else {
354 return def;
355 }
356 });
357 return mockArray;
358 }
359
Michael Wrighteef0e132017-11-21 17:57:52 +0000360}