• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package android.display.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import android.Manifest;
27 import android.app.UiAutomation;
28 import android.content.Context;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageInfo;
31 import android.content.pm.PackageManager;
32 import android.hardware.display.BrightnessChangeEvent;
33 import android.hardware.display.BrightnessConfiguration;
34 import android.hardware.display.BrightnessCorrection;
35 import android.hardware.display.DisplayManager;
36 import android.os.ParcelFileDescriptor;
37 import android.os.PowerManager;
38 import android.provider.Settings;
39 import android.util.Pair;
40 
41 import androidx.test.InstrumentationRegistry;
42 import androidx.test.filters.MediumTest;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import org.junit.After;
46 import org.junit.Before;
47 import org.junit.Test;
48 import org.junit.runner.RunWith;
49 
50 import java.io.FileDescriptor;
51 import java.io.FileInputStream;
52 import java.util.ArrayList;
53 import java.util.HashMap;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.Scanner;
57 
58 @MediumTest
59 @RunWith(AndroidJUnit4.class)
60 public class BrightnessTest {
61 
62     private Map<Long, BrightnessChangeEvent> mLastReadEvents = new HashMap<>();
63     private DisplayManager mDisplayManager;
64     private PowerManager.WakeLock mWakeLock;
65     private Context mContext;
66 
67     @Before
setUp()68     public void setUp() {
69         mContext = InstrumentationRegistry.getContext();
70         mDisplayManager = mContext.getSystemService(DisplayManager.class);
71         PowerManager pm = mContext.getSystemService(PowerManager.class);
72 
73         mWakeLock = pm.newWakeLock(
74                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
75                 "BrightnessTest");
76         mWakeLock.acquire();
77     }
78 
79     @After
tearDown()80     public void tearDown() {
81         if (mWakeLock != null) {
82             mWakeLock.release();
83         }
84 
85         revokePermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
86         revokePermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE);
87     }
88 
89     @Test
testBrightnessSliderTracking()90     public void testBrightnessSliderTracking() throws InterruptedException {
91         if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
92             // Don't run as there is no app that has permission to access slider usage.
93             return;
94         }
95 
96         int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
97         int previousBrightnessMode =
98                 getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
99         try {
100             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
101                     Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
102             int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
103             assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode);
104 
105             grantPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE);
106 
107             // Setup and remember some initial state.
108             recordSliderEvents();
109             waitForFirstSliderEvent();
110             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
111             getNewEvents(1);
112 
113             // Update brightness
114             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 60);
115 
116             // Check we got a slider event for the change.
117             List<BrightnessChangeEvent> newEvents = getNewEvents(1);
118             assertEquals(1, newEvents.size());
119             BrightnessChangeEvent firstEvent = newEvents.get(0);
120             assertValidLuxData(firstEvent);
121 
122             // Update brightness again
123             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 200);
124 
125             // Check we get a second slider event.
126             newEvents = getNewEvents(1);
127             assertEquals(1, newEvents.size());
128             BrightnessChangeEvent secondEvent = newEvents.get(0);
129             assertValidLuxData(secondEvent);
130             assertEquals(secondEvent.lastBrightness, firstEvent.brightness, 1.0f);
131             assertTrue(secondEvent.isUserSetBrightness);
132             assertTrue("failed " + secondEvent.brightness + " not greater than " +
133                     firstEvent.brightness, secondEvent.brightness > firstEvent.brightness);
134         } finally {
135             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
136             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
137         }
138     }
139 
140     @Test
testNoTrackingForManualBrightness()141     public void testNoTrackingForManualBrightness() throws InterruptedException {
142         if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
143             // Don't run as there is no app that has permission to access slider usage.
144             return;
145         }
146         int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
147         int previousBrightnessMode =
148                 getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
149         try {
150             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
151                     Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
152             int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
153             assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, mode);
154 
155             grantPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE);
156 
157             // Setup and remember some initial state.
158             recordSliderEvents();
159             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
160             assertTrue(getNewEvents().isEmpty());
161 
162             // Then change the brightness
163             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 80);
164             Thread.sleep(200);
165             // There shouldn't be any events.
166             assertTrue(getNewEvents().isEmpty());
167         } finally {
168             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
169             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
170         }
171     }
172 
173     @Test
testSliderUsagePermission()174     public void testSliderUsagePermission() {
175         revokePermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE);
176 
177         try {
178             mDisplayManager.getBrightnessEvents();
179         } catch (SecurityException e) {
180             // Expected
181             return;
182         }
183         fail();
184     }
185 
186     @Test
testConfigureBrightnessPermission()187     public void testConfigureBrightnessPermission() {
188         revokePermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
189 
190         BrightnessConfiguration config =
191             new BrightnessConfiguration.Builder(
192                     new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
193                 .setDescription("some test").build();
194 
195         try {
196             mDisplayManager.setBrightnessConfiguration(config);
197         } catch (SecurityException e) {
198             // Expected
199             return;
200         }
201         fail();
202     }
203 
204     @Test
testSetGetSimpleCurve()205     public void testSetGetSimpleCurve() {
206         if (numberOfSystemAppsWithPermission(
207                     Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) == 0) {
208             // Don't run as there is no app that has permission to push curves.
209             return;
210         }
211 
212         grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
213 
214         BrightnessConfiguration config =
215                 new BrightnessConfiguration.Builder(
216                         new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
217                         .addCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE,
218                                 BrightnessCorrection.createScaleAndTranslateLog(0.80f, 0.2f))
219                         .addCorrectionByPackageName("some.package.name",
220                                 BrightnessCorrection.createScaleAndTranslateLog(0.70f, 0.1f))
221                         .setDescription("some test").build();
222         mDisplayManager.setBrightnessConfiguration(config);
223         BrightnessConfiguration returnedConfig = mDisplayManager.getBrightnessConfiguration();
224         assertEquals(config, returnedConfig);
225         assertEquals(config.getCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE),
226                 BrightnessCorrection.createScaleAndTranslateLog(0.80f, 0.2f));
227         assertEquals(config.getCorrectionByPackageName("some.package.name"),
228                 BrightnessCorrection.createScaleAndTranslateLog(0.70f, 0.1f));
229         assertNull(config.getCorrectionByCategory(ApplicationInfo.CATEGORY_GAME));
230         assertNull(config.getCorrectionByPackageName("someother.package.name"));
231 
232         // After clearing the curve we should get back the default curve.
233         mDisplayManager.setBrightnessConfiguration(null);
234         returnedConfig = mDisplayManager.getBrightnessConfiguration();
235         assertEquals(mDisplayManager.getDefaultBrightnessConfiguration(), returnedConfig);
236     }
237 
238     @Test
testGetDefaultCurve()239     public void testGetDefaultCurve()  {
240         if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
241             // Don't run as there is no app that has permission to push curves.
242             return;
243         }
244 
245         grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
246 
247         BrightnessConfiguration defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration();
248         // Must provide a default config if an app with CONFIGURE_DISPLAY_BRIGHTNESS exists.
249         assertNotNull(defaultConfig);
250 
251         Pair<float[], float[]> curve = defaultConfig.getCurve();
252         assertTrue(curve.first.length > 0);
253         assertEquals(curve.first.length, curve.second.length);
254         assertInRange(curve.first, 0, Float.MAX_VALUE);
255         assertInRange(curve.second, 0, Float.MAX_VALUE);
256         assertEquals(0.0, curve.first[0], 0.1);
257         assertMonotonic(curve.first, true /*strictly increasing*/, "lux");
258         assertMonotonic(curve.second, false /*strictly increasing*/, "nits");
259     }
260 
261 
262     @Test
testSliderEventsReflectCurves()263     public void testSliderEventsReflectCurves() throws InterruptedException {
264         if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
265             // Don't run as there is no app that has permission to access slider usage.
266             return;
267         }
268         if (numberOfSystemAppsWithPermission(
269                     Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) == 0) {
270             // Don't run as there is no app that has permission to push curves.
271             return;
272         }
273 
274         BrightnessConfiguration config =
275                 new BrightnessConfiguration.Builder(
276                         new float[]{0.0f, 10000.0f},new float[]{15.0f, 400.0f})
277                         .setDescription("model:8").build();
278 
279         int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
280         int previousBrightnessMode =
281                 getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
282         try {
283             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
284                     Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
285             int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
286             assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode);
287 
288             grantPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE);
289             grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
290 
291             // Setup and remember some initial state.
292             recordSliderEvents();
293             waitForFirstSliderEvent();
294             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
295             getNewEvents(1);
296 
297             // Update brightness while we have a custom curve.
298             mDisplayManager.setBrightnessConfiguration(config);
299             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 60);
300 
301             // Check we got a slider event for the change.
302             List<BrightnessChangeEvent> newEvents = getNewEvents(1);
303             assertEquals(1, newEvents.size());
304             BrightnessChangeEvent firstEvent = newEvents.get(0);
305             assertValidLuxData(firstEvent);
306             assertFalse(firstEvent.isDefaultBrightnessConfig);
307 
308             // Update brightness again now with default curve.
309             mDisplayManager.setBrightnessConfiguration(null);
310             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 200);
311 
312             // Check we get a second slider event.
313             newEvents = getNewEvents(1);
314             assertEquals(1, newEvents.size());
315             BrightnessChangeEvent secondEvent = newEvents.get(0);
316             assertValidLuxData(secondEvent);
317             assertTrue(secondEvent.isDefaultBrightnessConfig);
318         } finally {
319             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
320             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
321         }
322     }
323 
324     @Test
testAtMostOneAppHoldsBrightnessConfigurationPermission()325     public void testAtMostOneAppHoldsBrightnessConfigurationPermission() {
326         assertTrue(numberOfSystemAppsWithPermission(
327                     Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) < 2);
328     }
329 
330     private void assertValidLuxData(BrightnessChangeEvent event) {
331         assertNotNull(event.luxTimestamps);
332         assertNotNull(event.luxValues);
333         assertTrue(event.luxTimestamps.length > 0);
334         assertEquals(event.luxValues.length, event.luxTimestamps.length);
335         for (int i = 1; i < event.luxTimestamps.length; ++i) {
336             assertTrue(event.luxTimestamps[i - 1] <= event.luxTimestamps[i]);
337         }
338         for (int i = 0; i < event.luxValues.length; ++i) {
339             assertTrue(event.luxValues[i] >= 0.0f);
340             assertTrue(event.luxValues[i] <= Float.MAX_VALUE);
341             assertFalse(Float.isNaN(event.luxValues[i]));
342         }
343     }
344 
345     /**
346      * Returns the number of system apps with the given permission.
347      */
numberOfSystemAppsWithPermission(String permission)348     private int numberOfSystemAppsWithPermission(String permission) {
349         List<PackageInfo> packages = mContext.getPackageManager().getPackagesHoldingPermissions(
350                 new String[] {permission}, PackageManager.MATCH_SYSTEM_ONLY);
351         return packages.size();
352     }
353 
getNewEvents(int expected)354     private List<BrightnessChangeEvent> getNewEvents(int expected)
355             throws InterruptedException {
356         List<BrightnessChangeEvent> newEvents = new ArrayList<>();
357         for (int i = 0; newEvents.size() < expected && i < 20; ++i) {
358             if (i != 0) {
359                 Thread.sleep(100);
360             }
361             newEvents.addAll(getNewEvents());
362         }
363         return newEvents;
364     }
365 
getNewEvents()366     private List<BrightnessChangeEvent> getNewEvents() {
367         List<BrightnessChangeEvent> newEvents = new ArrayList<>();
368         List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents();
369         for (BrightnessChangeEvent event : events) {
370             if (!mLastReadEvents.containsKey(event.timeStamp)) {
371                 newEvents.add(event);
372             }
373         }
374         mLastReadEvents = new HashMap<>();
375         for (BrightnessChangeEvent event : events) {
376             mLastReadEvents.put(event.timeStamp, event);
377         }
378         return newEvents;
379     }
380 
recordSliderEvents()381     private void recordSliderEvents() {
382         mLastReadEvents = new HashMap<>();
383         List<BrightnessChangeEvent> eventsBefore = mDisplayManager.getBrightnessEvents();
384         for (BrightnessChangeEvent event : eventsBefore) {
385             mLastReadEvents.put(event.timeStamp, event);
386         }
387     }
388 
waitForFirstSliderEvent()389     private void waitForFirstSliderEvent() throws  InterruptedException {
390         // Keep changing brightness until we get an event to handle devices with sensors
391         // that take a while to warm up.
392         int brightness = 25;
393         for (int i = 0; i < 20; ++i) {
394             setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, brightness);
395             brightness = brightness == 25 ? 80 : 25;
396             Thread.sleep(100);
397             if (!getNewEvents().isEmpty()) {
398                 return;
399             }
400         }
401         fail("Failed to fetch first slider event. Is the ambient brightness sensor working?");
402     }
403 
getSystemSetting(String setting)404     private int getSystemSetting(String setting) {
405         return Integer.parseInt(runShellCommand("settings get system " + setting));
406     }
407 
setSystemSetting(String setting, int value)408     private void setSystemSetting(String setting, int value) {
409         runShellCommand("settings put system " + setting + " " + Integer.toString(value));
410     }
411 
grantPermission(String permission)412     private void grantPermission(String permission) {
413         InstrumentationRegistry.getInstrumentation().getUiAutomation()
414                 .grantRuntimePermission(mContext.getPackageName(), permission);
415     }
416 
revokePermission(String permission)417     private void revokePermission(String permission) {
418         InstrumentationRegistry.getInstrumentation().getUiAutomation()
419                 .revokeRuntimePermission(mContext.getPackageName(), permission);
420     }
421 
runShellCommand(String cmd)422     private String runShellCommand(String cmd) {
423         UiAutomation automation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
424         ParcelFileDescriptor output = automation.executeShellCommand(cmd);
425         String result = convertFileDescriptorToString(output.getFileDescriptor());
426         return result.trim();
427     }
428 
convertFileDescriptorToString(FileDescriptor desc)429     private String convertFileDescriptorToString(FileDescriptor desc) {
430         try (Scanner s = new Scanner(new FileInputStream(desc)).useDelimiter("\\Z")) {
431             return s.hasNext() ? s.next() : "";
432         }
433     }
434 
assertInRange(float[] values, float min, float max)435     private static void assertInRange(float[] values, float min, float max) {
436         for (int i = 0; i < values.length; i++) {
437             assertFalse(Float.isNaN(values[i]));
438             assertTrue(values[i] >= min);
439             assertTrue(values[i] <= max);
440         }
441     }
442 
assertMonotonic(float[] values, boolean strictlyIncreasing, String name)443     private static void assertMonotonic(float[] values, boolean strictlyIncreasing, String name) {
444         if (values.length <= 1) {
445             return;
446         }
447         float prev = values[0];
448         for (int i = 1; i < values.length; i++) {
449             if (prev > values[i] || (prev == values[i] && strictlyIncreasing)) {
450                 String condition = strictlyIncreasing ? "strictly increasing" : "monotonic";
451                 fail(name + " values must be " + condition);
452             }
453             prev = values[i];
454         }
455     }
456 }
457