blob: 5105f4e2a9c9c4daf7d3cdb08e057675a9dd9e9d [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;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertNull;
22import static org.junit.Assert.assertTrue;
Michael Wright144aac92017-12-21 18:37:41 +000023import static org.mockito.ArgumentMatchers.anyFloat;
24import static org.mockito.ArgumentMatchers.anyInt;
25import static org.mockito.Mockito.mock;
26import static org.mockito.Mockito.when;
Michael Wrighteef0e132017-11-21 17:57:52 +000027
Michael Wright144aac92017-12-21 18:37:41 +000028import android.content.res.Resources;
29import android.content.res.TypedArray;
Michael Wrighteef0e132017-11-21 17:57:52 +000030import android.os.PowerManager;
31import android.support.test.filters.SmallTest;
32import android.support.test.runner.AndroidJUnit4;
33import android.util.Spline;
34
35import org.junit.Test;
36import org.junit.runner.RunWith;
37
38import java.util.Arrays;
39
40@SmallTest
41@RunWith(AndroidJUnit4.class)
42public class BrightnessMappingStrategyTest {
43
Michael Wright144aac92017-12-21 18:37:41 +000044 private static final int[] LUX_LEVELS = {
45 0,
46 5,
47 20,
48 40,
49 100,
50 325,
51 600,
52 1250,
53 2200,
54 4000,
55 5000
Michael Wrighteef0e132017-11-21 17:57:52 +000056 };
57
58 private static final float[] DISPLAY_LEVELS_NITS = {
59 13.25f,
60 54.0f,
61 78.85f,
62 105.02f,
63 132.7f,
64 170.12f,
65 212.1f,
66 265.2f,
67 335.8f,
68 415.2f,
69 478.5f,
70 };
71
72 private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
73 9,
74 30,
75 45,
76 62,
77 78,
78 96,
79 119,
80 146,
81 178,
82 221,
83 255
84 };
85
86 private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
87 private static final int[] BACKLIGHT_RANGE = { 1, 255 };
88
Michael Wright144aac92017-12-21 18:37:41 +000089 private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
90 private static final int[] EMPTY_INT_ARRAY = new int[0];
91
Michael Wrighteef0e132017-11-21 17:57:52 +000092 @Test
93 public void testSimpleStrategyMappingAtControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +000094 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
95 BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +000096 assertNotNull("BrightnessMappingStrategy should not be null", simple);
97 for (int i = 0; i < LUX_LEVELS.length; i++) {
98 final float expectedLevel =
99 (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON;
100 assertEquals(expectedLevel,
101 simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
102 }
103 }
104
105 @Test
106 public void testSimpleStrategyMappingBetweenControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +0000107 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
108 BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000109 assertNotNull("BrightnessMappingStrategy should not be null", simple);
110 for (int i = 1; i < LUX_LEVELS.length; i++) {
111 final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
112 final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
113 assertTrue("Desired brightness should be between adjacent control points.",
114 backlight > DISPLAY_LEVELS_BACKLIGHT[i-1]
115 && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
116 }
117 }
118
119 @Test
120 public void testPhysicalStrategyMappingAtControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +0000121 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
122 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
123 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000124 assertNotNull("BrightnessMappingStrategy should not be null", physical);
125 for (int i = 0; i < LUX_LEVELS.length; i++) {
126 final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1];
127 assertEquals(expectedLevel,
128 physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
129 }
130 }
131
132 @Test
133 public void testPhysicalStrategyMappingBetweenControlPoints() {
Michael Wright144aac92017-12-21 18:37:41 +0000134 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
135 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
136 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000137 assertNotNull("BrightnessMappingStrategy should not be null", physical);
138 Spline backlightToBrightness =
139 Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS);
140 for (int i = 1; i < LUX_LEVELS.length; i++) {
141 final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
142 final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
143 final float nits = backlightToBrightness.interpolate(backlight);
144 assertTrue("Desired brightness should be between adjacent control points.",
145 nits > DISPLAY_LEVELS_NITS[i-1] && nits < DISPLAY_LEVELS_NITS[i]);
146 }
147 }
148
149 @Test
150 public void testDefaultStrategyIsPhysical() {
Michael Wright144aac92017-12-21 18:37:41 +0000151 Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
Michael Wrighteef0e132017-11-21 17:57:52 +0000152 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
Michael Wright144aac92017-12-21 18:37:41 +0000153 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000154 assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
155 }
156
157 @Test
158 public void testNonStrictlyIncreasingLuxLevelsFails() {
Michael Wright144aac92017-12-21 18:37:41 +0000159 final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
Michael Wrighteef0e132017-11-21 17:57:52 +0000160 final int idx = lux.length / 2;
Michael Wright144aac92017-12-21 18:37:41 +0000161 int tmp = lux[idx];
Michael Wrighteef0e132017-11-21 17:57:52 +0000162 lux[idx] = lux[idx+1];
163 lux[idx+1] = tmp;
Michael Wright144aac92017-12-21 18:37:41 +0000164 Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
165 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
166 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000167 assertNull(strategy);
168
169 // And make sure we get the same result even if it's monotone but not increasing.
170 lux[idx] = lux[idx+1];
Michael Wright144aac92017-12-21 18:37:41 +0000171 res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
172 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000173 assertNull(strategy);
174 }
175
176 @Test
177 public void testDifferentNumberOfControlPointValuesFails() {
178 //Extra lux level
Michael Wright144aac92017-12-21 18:37:41 +0000179 final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1);
Michael Wrighteef0e132017-11-21 17:57:52 +0000180 // Make sure it's strictly increasing so that the only failure is the differing array
181 // lengths
182 lux[lux.length - 1] = lux[lux.length - 2] + 1;
Michael Wright144aac92017-12-21 18:37:41 +0000183 Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
184 DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
185 BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000186 assertNull(strategy);
187
Michael Wright144aac92017-12-21 18:37:41 +0000188 res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT);
189 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000190 assertNull(strategy);
191
192 // Extra backlight level
193 final int[] backlight = Arrays.copyOf(
194 DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
195 backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
Michael Wright144aac92017-12-21 18:37:41 +0000196 res = createResources(LUX_LEVELS, backlight);
197 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000198 assertNull(strategy);
199
200 // Extra nits level
201 final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
202 nits[nits.length - 1] = nits[nits.length - 2] + 1;
Michael Wright144aac92017-12-21 18:37:41 +0000203 res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
204 strategy = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000205 assertNull(strategy);
206 }
207
208 @Test
209 public void testPhysicalStrategyRequiresNitsMapping() {
Michael Wright144aac92017-12-21 18:37:41 +0000210 Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
211 DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE);
212 BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000213 assertNull(physical);
214
Michael Wright144aac92017-12-21 18:37:41 +0000215 res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
216 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/);
217 physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000218 assertNull(physical);
219
Michael Wright144aac92017-12-21 18:37:41 +0000220 res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
221 DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/,
222 EMPTY_INT_ARRAY /*backlightRange*/);
223 physical = BrightnessMappingStrategy.create(res);
Michael Wrighteef0e132017-11-21 17:57:52 +0000224 assertNull(physical);
225 }
226
227 private static float[] toFloatArray(int[] vals) {
228 float[] newVals = new float[vals.length];
229 for (int i = 0; i < vals.length; i++) {
230 newVals[i] = (float) vals[i];
231 }
232 return newVals;
233 }
Michael Wright144aac92017-12-21 18:37:41 +0000234
235 private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) {
236 return createResources(luxLevels, brightnessLevelsBacklight,
237 EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/,
238 EMPTY_INT_ARRAY /*backlightRange*/);
239 }
240
241 private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits,
242 float[] nitsRange, int[] backlightRange) {
243 return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
244 brightnessLevelsNits, nitsRange, backlightRange);
245 }
246
247 private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
248 float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) {
249 Resources mockResources = mock(Resources.class);
250 // For historical reasons, the lux levels resource implicitly defines the first point as 0,
251 // so we need to chop it off of the array the mock resource object returns.
252 int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length);
253 when(mockResources.getIntArray(com.android.internal.R.array.config_autoBrightnessLevels))
254 .thenReturn(luxLevelsResource);
255
256 when(mockResources.getIntArray(
257 com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
258 .thenReturn(brightnessLevelsBacklight);
259
260 TypedArray mockBrightnessLevelNits = createFloatTypedArray(brightnessLevelsNits);
261 when(mockResources.obtainTypedArray(
262 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
263 .thenReturn(mockBrightnessLevelNits);
264
265 TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
266 when(mockResources.obtainTypedArray(
267 com.android.internal.R.array.config_screenBrightnessNits))
268 .thenReturn(mockNitsRange);
269
270 when(mockResources.getIntArray(
271 com.android.internal.R.array.config_screenBrightnessBacklight))
272 .thenReturn(backlightRange);
273
274 when(mockResources.getInteger(
275 com.android.internal.R.integer.config_screenBrightnessSettingMinimum))
276 .thenReturn(1);
277 when(mockResources.getInteger(
278 com.android.internal.R.integer.config_screenBrightnessSettingMaximum))
279 .thenReturn(255);
280 return mockResources;
281 }
282
283 private TypedArray createFloatTypedArray(float[] vals) {
284 TypedArray mockArray = mock(TypedArray.class);
285 when(mockArray.length()).thenAnswer(invocation -> {
286 return vals.length;
287 });
288 when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> {
289 final float def = (float) invocation.getArguments()[1];
290 if (vals == null) {
291 return def;
292 }
293 int idx = (int) invocation.getArguments()[0];
294 if (idx >= 0 && idx < vals.length) {
295 return vals[idx];
296 } else {
297 return def;
298 }
299 });
300 return mockArray;
301 }
302
Michael Wrighteef0e132017-11-21 17:57:52 +0000303}