1 /*
2  * Copyright (C) 2015 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 package android.app.cts;
17 
18 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
19 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
20 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
21 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
22 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
23 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
24 import static android.app.UiModeManager.MODE_NIGHT_NO;
25 import static android.app.UiModeManager.MODE_NIGHT_YES;
26 
27 import static androidx.test.InstrumentationRegistry.getInstrumentation;
28 
29 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
30 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
31 
32 import static com.google.common.truth.Truth.assertThat;
33 
34 import static org.testng.Assert.assertThrows;
35 
36 import android.Manifest;
37 import android.app.UiAutomation;
38 import android.app.UiModeManager;
39 import android.app.UiModeManager.ContrastChangeListener;
40 import android.content.Context;
41 import android.content.pm.PackageManager;
42 import android.content.res.Configuration;
43 import android.os.Handler;
44 import android.os.HandlerThread;
45 import android.os.ParcelFileDescriptor;
46 import android.os.UserHandle;
47 import android.provider.Settings;
48 import android.test.AndroidTestCase;
49 import android.text.TextUtils;
50 import android.util.ArraySet;
51 import android.util.Log;
52 
53 import com.android.compatibility.common.util.BatteryUtils;
54 import com.android.compatibility.common.util.CommonTestUtils;
55 import com.android.compatibility.common.util.TestUtils;
56 import com.android.compatibility.common.util.UserSettings;
57 
58 import com.google.common.util.concurrent.MoreExecutors;
59 
60 import junit.framework.Assert;
61 
62 import java.io.FileInputStream;
63 import java.io.IOException;
64 import java.io.InputStream;
65 import java.time.LocalTime;
66 import java.util.ArrayList;
67 import java.util.List;
68 import java.util.Set;
69 import java.util.concurrent.Executor;
70 import java.util.concurrent.atomic.AtomicBoolean;
71 import java.util.concurrent.atomic.AtomicInteger;
72 import java.util.concurrent.atomic.AtomicReference;
73 
74 
75 public class UiModeManagerTest extends AndroidTestCase {
76     private static final String TAG = "UiModeManagerTest";
77     private static final long MAX_WAIT_TIME_SECS = 2;
78     private static final long MAX_WAIT_TIME_MS = MAX_WAIT_TIME_SECS * 1000;
79     private static final long WAIT_TIME_INCR_MS = 100;
80 
81     private static final String CONTRAST_LEVEL = "contrast_level";
82 
83     private final UserSettings mSystemUserSettings = new UserSettings(UserHandle.USER_SYSTEM);
84     private UiModeManager mUiModeManager;
85     private boolean mHasModifiedNightModePermissionAcquired = false;
86 
87     @Override
setUp()88     protected void setUp() throws Exception {
89         super.setUp();
90         mUiModeManager = (UiModeManager) getContext().getSystemService(Context.UI_MODE_SERVICE);
91         assertNotNull(mUiModeManager);
92         resetNightMode();
93         // Make sure automotive projection is not set by this package at the beginning of the test.
94         releaseAutomotiveProjection();
95     }
96 
97     @Override
tearDown()98     protected void tearDown() throws Exception {
99         resetNightMode();
100         BatteryUtils.runDumpsysBatteryReset();
101         BatteryUtils.resetBatterySaver();
102     }
103 
resetNightMode()104     private void resetNightMode() {
105         try {
106             if (!mHasModifiedNightModePermissionAcquired) {
107                 acquireModifyNightModePermission();
108             }
109             mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
110             mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
111                     false /* active */);
112         } finally {
113             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
114             mHasModifiedNightModePermissionAcquired = false;
115         }
116     }
117 
testUiMode()118     public void testUiMode() throws Exception {
119         if (isAutomotive()) {
120             Log.i(TAG, "testUiMode automotive");
121             doTestUiModeForAutomotive();
122         } else if (isTelevision()) {
123             assertEquals(Configuration.UI_MODE_TYPE_TELEVISION,
124                     mUiModeManager.getCurrentModeType());
125             doTestLockedUiMode();
126         } else if (isWatch()) {
127             assertEquals(Configuration.UI_MODE_TYPE_WATCH,
128                     mUiModeManager.getCurrentModeType());
129             doTestLockedUiMode();
130         } else {
131             Log.i(TAG, "testUiMode generic");
132             doTestUiModeGeneric();
133         }
134     }
135 
testNightMode()136     public void testNightMode() throws Exception {
137         if (isAutomotive()) {
138             assertTrue(mUiModeManager.isNightModeLocked());
139             doTestLockedNightMode();
140         } else {
141             if (mUiModeManager.isNightModeLocked()) {
142                 doTestLockedNightMode();
143             } else {
144                 doTestUnlockedNightMode();
145             }
146         }
147     }
148 
testSetAndGetCustomTimeStart()149     public void testSetAndGetCustomTimeStart() {
150         LocalTime time = mUiModeManager.getCustomNightModeStart();
151         // decrease time
152         LocalTime timeNew = LocalTime.of(
153                 (time.getHour() + 1) % 12,
154                 (time.getMinute() + 30) % 60);
155         setStartTime(timeNew);
156         assertNotSame(time, timeNew);
157         assertEquals(timeNew, mUiModeManager.getCustomNightModeStart());
158     }
159 
testSetAndGetCustomTimeEnd()160     public void testSetAndGetCustomTimeEnd() {
161         LocalTime time = mUiModeManager.getCustomNightModeEnd();
162         // decrease time
163         LocalTime timeNew = LocalTime.of(
164                 (time.getHour() + 1) % 12,
165                 (time.getMinute() + 30) % 60);
166         setEndTime(timeNew);
167         assertNotSame(time, timeNew);
168         assertEquals(timeNew, mUiModeManager.getCustomNightModeEnd());
169     }
170 
testNightModeYesPersisted()171     public void testNightModeYesPersisted() throws InterruptedException {
172         if (mUiModeManager.isNightModeLocked()) {
173             Log.i(TAG, "testNightModeYesPersisted skipped: night mode is locked");
174             return;
175         }
176 
177         // Reset the mode to no if it is set to another value
178         setNightMode(UiModeManager.MODE_NIGHT_NO);
179 
180         setNightMode(UiModeManager.MODE_NIGHT_YES);
181         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_YES);
182     }
183 
testNightModeAutoPersisted()184     public void testNightModeAutoPersisted() throws InterruptedException {
185         if (mUiModeManager.isNightModeLocked()) {
186             Log.i(TAG, "testNightModeAutoPersisted skipped: night mode is locked");
187             return;
188         }
189 
190         // Reset the mode to no if it is set to another value
191         setNightMode(UiModeManager.MODE_NIGHT_NO);
192 
193         setNightMode(UiModeManager.MODE_NIGHT_AUTO);
194         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_AUTO);
195     }
196 
testSetNightModeCustomType_noPermission_bedtime_shouldThrow()197     public void testSetNightModeCustomType_noPermission_bedtime_shouldThrow() {
198         assertThrows(SecurityException.class,
199                 () -> mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME));
200     }
201 
testSetNightModeCustomType_customTypeUnknown_bedtime_shouldThrow()202     public void testSetNightModeCustomType_customTypeUnknown_bedtime_shouldThrow() {
203         acquireModifyNightModePermission();
204 
205         assertThrows(IllegalArgumentException.class,
206                 () -> mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN));
207     }
208 
testSetNightModeCustomType_bedtime_shouldPersist()209     public void testSetNightModeCustomType_bedtime_shouldPersist() {
210         acquireModifyNightModePermission();
211         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
212 
213         assertThat(mUiModeManager.getNightMode()).isEqualTo(MODE_NIGHT_CUSTOM);
214         assertThat(mUiModeManager.getNightModeCustomType())
215                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
216     }
217 
testSetNightModeCustomType_schedule_shouldPersist()218     public void testSetNightModeCustomType_schedule_shouldPersist() {
219         acquireModifyNightModePermission();
220         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
221 
222         assertThat(mUiModeManager.getNightMode()).isEqualTo(MODE_NIGHT_CUSTOM);
223         assertThat(mUiModeManager.getNightModeCustomType())
224                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
225     }
226 
testGetNightModeCustomType_nightModeNo_shouldReturnUnknown()227     public void testGetNightModeCustomType_nightModeNo_shouldReturnUnknown() {
228         acquireModifyNightModePermission();
229         mUiModeManager.setNightMode(MODE_NIGHT_NO);
230 
231         assertThat(mUiModeManager.getNightModeCustomType())
232                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
233     }
234 
testGetNightModeCustomType_nightModeYes_shouldReturnUnknown()235     public void testGetNightModeCustomType_nightModeYes_shouldReturnUnknown() {
236         acquireModifyNightModePermission();
237         mUiModeManager.setNightMode(MODE_NIGHT_YES);
238 
239         assertThat(mUiModeManager.getNightModeCustomType())
240                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
241     }
242 
testGetNightModeCustomType_nightModeAuto_shouldReturnUnknown()243     public void testGetNightModeCustomType_nightModeAuto_shouldReturnUnknown() {
244         acquireModifyNightModePermission();
245         mUiModeManager.setNightMode(MODE_NIGHT_AUTO);
246 
247         assertThat(mUiModeManager.getNightModeCustomType())
248                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
249     }
250 
testGetNightModeCustomType_nightModeCustom_shouldReturnScheduleAsDefault()251     public void testGetNightModeCustomType_nightModeCustom_shouldReturnScheduleAsDefault() {
252         acquireModifyNightModePermission();
253         mUiModeManager.setNightMode(MODE_NIGHT_CUSTOM);
254 
255         assertThat(mUiModeManager.getNightModeCustomType())
256                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
257     }
258 
testSetNightModeCustomType_hasPermission_bedtime_shouldNotActivateNightMode()259     public void testSetNightModeCustomType_hasPermission_bedtime_shouldNotActivateNightMode() {
260         acquireModifyNightModePermission();
261         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
262 
263         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
264     }
265 
testSetNightModeActivatedForCustomMode_noPermission_customTypeBedtime_activateAtBedtime_shouldNotActivateNightMode()266     public void testSetNightModeActivatedForCustomMode_noPermission_customTypeBedtime_activateAtBedtime_shouldNotActivateNightMode() {
267         acquireModifyNightModePermission();
268         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
269         getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
270         mHasModifiedNightModePermissionAcquired = false;
271 
272         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
273                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
274 
275         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
276     }
277 
testSetNightModeActivatedForCustomMode_customTypeBedtime_activateAtBedtime_shouldActivateNightMode()278     public void testSetNightModeActivatedForCustomMode_customTypeBedtime_activateAtBedtime_shouldActivateNightMode() {
279         acquireModifyNightModePermission();
280         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
281 
282         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
283                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isTrue();
284 
285         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
286     }
287 
testSetNightModeActivatedForCustomMode_customTypeBedtime_deactivateAtBedtime_shouldDeactivateNightMode()288     public void testSetNightModeActivatedForCustomMode_customTypeBedtime_deactivateAtBedtime_shouldDeactivateNightMode() {
289         acquireModifyNightModePermission();
290         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
291 
292         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
293                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */)).isTrue();
294 
295         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
296     }
297 
testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_shouldNotActivateNightMode()298     public void testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_shouldNotActivateNightMode() {
299         acquireModifyNightModePermission();
300         mUiModeManager.setNightMode(MODE_NIGHT_NO);
301 
302         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
303                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
304 
305         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
306     }
307 
testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_shouldKeepNightModeActivated()308     public void testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_shouldKeepNightModeActivated() {
309         acquireModifyNightModePermission();
310         mUiModeManager.setNightMode(MODE_NIGHT_YES);
311 
312         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
313                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
314 
315         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
316     }
317 
testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_shouldNotActivateNightMode()318     public void testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_shouldNotActivateNightMode() {
319         acquireModifyNightModePermission();
320         mUiModeManager.setNightMode(MODE_NIGHT_AUTO);
321         int uiMode = getContext().getResources().getConfiguration().uiMode &
322                 Configuration.UI_MODE_NIGHT_MASK;
323 
324         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
325                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
326 
327         assertVisibleNightModeInConfiguration(uiMode);
328     }
329 
testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_shouldNotActivateNightMode()330     public void testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_shouldNotActivateNightMode() {
331         acquireModifyNightModePermission();
332         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
333         int uiMode = getContext().getResources().getConfiguration().uiMode &
334                 Configuration.UI_MODE_NIGHT_MASK;
335 
336         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
337                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
338 
339         assertVisibleNightModeInConfiguration(uiMode);
340     }
341 
testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode()342     public void testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode() {
343         acquireModifyNightModePermission();
344         mUiModeManager.setNightMode(MODE_NIGHT_NO);
345         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
346                 true /* active */);
347 
348         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
349 
350         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
351     }
352 
testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_thenCustomTypeBedtime_shouldActiveNightMode()353     public void testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_thenCustomTypeBedtime_shouldActiveNightMode() {
354         acquireModifyNightModePermission();
355         mUiModeManager.setNightMode(MODE_NIGHT_YES);
356         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
357                 true /* active */);
358 
359         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
360 
361         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
362     }
363 
testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode()364     public void testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode() {
365         acquireModifyNightModePermission();
366         mUiModeManager.setNightMode(MODE_NIGHT_AUTO);
367         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
368                 true /* active */);
369 
370         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
371 
372         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
373     }
374 
testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode()375     public void testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode() {
376         acquireModifyNightModePermission();
377         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
378         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
379                 true /* active */);
380 
381         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
382 
383         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
384     }
385 
testNightModeAutoNotPersistedCarMode()386     public void testNightModeAutoNotPersistedCarMode() {
387         if (mUiModeManager.isNightModeLocked()) {
388             Log.i(TAG, "testNightModeAutoNotPersistedCarMode skipped: night mode is locked");
389             return;
390         }
391 
392         // Reset the mode to no if it is set to another value
393         setNightMode(UiModeManager.MODE_NIGHT_NO);
394         mUiModeManager.enableCarMode(0);
395 
396         setNightMode(UiModeManager.MODE_NIGHT_AUTO);
397         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
398         mUiModeManager.disableCarMode(0);
399     }
400 
testNightModeCustomTypeBedtimeNotPersistedInCarMode()401     public void testNightModeCustomTypeBedtimeNotPersistedInCarMode() throws InterruptedException {
402         if (mUiModeManager.isNightModeLocked()) {
403             Log.i(TAG, "testNightModeCustomTypeBedtimeNotPersistedInCarMode skipped: night mode is "
404                     + "locked");
405             return;
406         }
407 
408         acquireModifyNightModePermission();
409         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
410         mUiModeManager.enableCarMode(0);
411 
412         // We don't expect users modifing bedtime features when driving.
413         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
414 
415         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
416         mUiModeManager.disableCarMode(0);
417     }
418 
testNightModeInCarModeIsTransient()419     public void testNightModeInCarModeIsTransient() {
420         if (mUiModeManager.isNightModeLocked()) {
421             Log.i(TAG, "testNightModeInCarModeIsTransient skipped: night mode is locked");
422             return;
423         }
424 
425         assertNightModeChange(UiModeManager.MODE_NIGHT_NO);
426 
427         mUiModeManager.enableCarMode(0);
428         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
429 
430         assertNightModeChange(UiModeManager.MODE_NIGHT_YES);
431 
432         mUiModeManager.disableCarMode(0);
433         assertNotSame(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
434         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
435     }
436 
testNightModeToggleInCarModeDoesNotChangeSetting()437     public void testNightModeToggleInCarModeDoesNotChangeSetting() {
438         if (mUiModeManager.isNightModeLocked()) {
439             Log.i(TAG, "testNightModeToggleInCarModeDoesNotChangeSetting skipped: "
440                     + "night mode is locked");
441             return;
442         }
443 
444         assertNightModeChange(UiModeManager.MODE_NIGHT_NO);
445         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
446 
447         mUiModeManager.enableCarMode(0);
448         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
449 
450         assertNightModeChange(UiModeManager.MODE_NIGHT_YES);
451         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
452 
453         mUiModeManager.disableCarMode(0);
454         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
455     }
456 
testNightModeInCarModeOnPowerSaveIsTransient()457     public void testNightModeInCarModeOnPowerSaveIsTransient() throws Throwable {
458         if (mUiModeManager.isNightModeLocked() || !BatteryUtils.isBatterySaverSupported()) {
459             Log.i(TAG, "testNightModeInCarModeOnPowerSaveIsTransient skipped: "
460                     + "night mode is locked or battery saver is not supported");
461             return;
462         }
463 
464         BatteryUtils.runDumpsysBatteryUnplug();
465 
466         // Turn off battery saver, disable night mode
467         BatteryUtils.enableBatterySaver(false);
468         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
469         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
470         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
471 
472         // Then enable battery saver to check night mode is made visible
473         BatteryUtils.enableBatterySaver(true);
474         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
475         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
476 
477         // Then disable it, enable car mode, and check night mode is not visible
478         BatteryUtils.enableBatterySaver(false);
479         mUiModeManager.enableCarMode(0);
480         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
481         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
482 
483         // Enable battery saver, check that night mode is still not visible, overridden by car mode
484         BatteryUtils.enableBatterySaver(true);
485         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
486         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
487 
488         // Disable car mode
489         mUiModeManager.disableCarMode(0);
490 
491         // Toggle night mode to force propagation of uiMode update, since disabling car mode
492         // is deferred to a broadcast.
493         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_YES);
494         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
495 
496         // Check battery saver mode now shows night mode
497         assertNotSame(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
498         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
499 
500         // Disable battery saver and check night mode back to not visible
501         BatteryUtils.enableBatterySaver(false);
502         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
503         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
504     }
505 
506     /**
507      * Verifies that an app holding the ENTER_CAR_MODE_PRIORITIZED permission can enter car mode
508      * while specifying a priority.
509      */
testEnterCarModePrioritized()510     public void testEnterCarModePrioritized() {
511         if (mUiModeManager.isUiModeLocked()) {
512             return;
513         }
514 
515         runWithShellPermissionIdentity(() -> mUiModeManager.enableCarMode(100, 0),
516                 Manifest.permission.ENTER_CAR_MODE_PRIORITIZED);
517         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
518 
519         runWithShellPermissionIdentity(() -> mUiModeManager.disableCarMode(0),
520                 Manifest.permission.ENTER_CAR_MODE_PRIORITIZED);
521         assertEquals(Configuration.UI_MODE_TYPE_NORMAL, mUiModeManager.getCurrentModeType());
522     }
523 
524     /**
525      * Attempts to use the prioritized car mode API when the caller does not hold the correct
526      * permission to use that API.
527      */
testEnterCarModePrioritizedDenied()528     public void testEnterCarModePrioritizedDenied() {
529         if (mUiModeManager.isUiModeLocked()) {
530             return;
531         }
532         try {
533             mUiModeManager.enableCarMode(100, 0);
534         } catch (SecurityException se) {
535             // Expect exception.
536             return;
537         }
538         fail("Expected SecurityException");
539     }
540 
541     /**
542      * Verifies that an app holding the TOGGLE_AUTOMOTIVE_PROJECTION permission can request/release
543      * automotive projection.
544      */
testToggleAutomotiveProjection()545     public void testToggleAutomotiveProjection() throws Exception {
546         // If we didn't hold it in the first place, we didn't release it, so expect false.
547         assertFalse(releaseAutomotiveProjection());
548         assertTrue(requestAutomotiveProjection());
549         // Multiple calls are OK.
550         assertTrue(requestAutomotiveProjection());
551         assertTrue(releaseAutomotiveProjection());
552         // Once it's released, further calls return false since it was already released.
553         assertFalse(releaseAutomotiveProjection());
554     }
555 
556     /**
557      * Verifies that the system can correctly read the projection state.
558      */
testReadProjectionState()559     public void testReadProjectionState() throws Exception {
560         assertEquals(UiModeManager.PROJECTION_TYPE_NONE, getActiveProjectionTypes());
561         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE).isEmpty());
562         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL).isEmpty());
563         requestAutomotiveProjection();
564         assertEquals(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, getActiveProjectionTypes());
565         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE)
566                 .contains(getContext().getPackageName()));
567         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL)
568                 .contains(getContext().getPackageName()));
569         releaseAutomotiveProjection();
570         assertEquals(UiModeManager.PROJECTION_TYPE_NONE, getActiveProjectionTypes());
571         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE).isEmpty());
572         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL).isEmpty());
573     }
574 
575     /** Verifies that the system receives callbacks about the projection state at expected times. */
testReadProjectionState_listener()576     public void testReadProjectionState_listener() throws Exception {
577         // Use AtomicInteger so it can be effectively final.
578         AtomicInteger activeProjectionTypes = new AtomicInteger();
579         Set<String> projectingPackages = new ArraySet<>();
580         AtomicInteger callbackInvocations = new AtomicInteger();
581         UiModeManager.OnProjectionStateChangedListener listener = (t, pkgs) -> {
582             Log.i(TAG, "onProjectionStateChanged(" + t + "," + pkgs + ")");
583             activeProjectionTypes.set(t);
584             projectingPackages.clear();
585             projectingPackages.addAll(pkgs);
586             callbackInvocations.incrementAndGet();
587         };
588 
589         requestAutomotiveProjection();
590         runWithShellPermissionIdentity(() -> mUiModeManager.addOnProjectionStateChangedListener(
591                 UiModeManager.PROJECTION_TYPE_ALL, MoreExecutors.directExecutor(), listener),
592                 Manifest.permission.READ_PROJECTION_STATE);
593 
594         // Should have called back immediately, but the call might not have gotten here yet.
595         CommonTestUtils.waitUntil("Callback wasn't invoked on listener addition!",
596                 MAX_WAIT_TIME_SECS, () -> callbackInvocations.get() == 1);
597         assertEquals(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, activeProjectionTypes.get());
598         assertEquals(1, projectingPackages.size());
599         assertTrue(projectingPackages.contains(getContext().getPackageName()));
600 
601         // Callback should not be invoked again.
602         requestAutomotiveProjection();
603         Thread.sleep(MAX_WAIT_TIME_MS);
604         assertEquals(1, callbackInvocations.get());
605 
606         releaseAutomotiveProjection();
607         CommonTestUtils.waitUntil("Callback wasn't invoked on projection release!",
608                 MAX_WAIT_TIME_SECS, () -> callbackInvocations.get() == 2);
609         assertEquals(UiModeManager.PROJECTION_TYPE_NONE, activeProjectionTypes.get());
610         assertEquals(0, projectingPackages.size());
611 
612         // Again, no callback for noop call.
613         releaseAutomotiveProjection();
614         Thread.sleep(MAX_WAIT_TIME_MS);
615         assertEquals(2, callbackInvocations.get());
616 
617         // Test the case that isn't at time of registration.
618         requestAutomotiveProjection();
619         CommonTestUtils.waitUntil("Callback wasn't invoked on projection set!",
620                 MAX_WAIT_TIME_SECS, () -> callbackInvocations.get() == 3);
621         assertEquals(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, activeProjectionTypes.get());
622         assertEquals(1, projectingPackages.size());
623         assertTrue(projectingPackages.contains(getContext().getPackageName()));
624 
625         // Unregister and shouldn't receive further callbacks.
626         runWithShellPermissionIdentity(() -> mUiModeManager.removeOnProjectionStateChangedListener(
627                 listener), Manifest.permission.READ_PROJECTION_STATE);
628 
629         releaseAutomotiveProjection();
630         requestAutomotiveProjection();
631         releaseAutomotiveProjection(); // Just to clean up.
632         Thread.sleep(MAX_WAIT_TIME_MS);
633         assertEquals(3, callbackInvocations.get());
634     }
635 
requestAutomotiveProjection()636     private boolean requestAutomotiveProjection() throws Exception {
637         return callWithShellPermissionIdentity(
638                 () -> mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE),
639                 Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION,
640                 Manifest.permission.INTERACT_ACROSS_USERS);
641     }
642 
releaseAutomotiveProjection()643     private boolean releaseAutomotiveProjection() throws Exception {
644         return callWithShellPermissionIdentity(
645                 () -> mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE),
646                 Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION,
647                 Manifest.permission.INTERACT_ACROSS_USERS);
648     }
649 
getActiveProjectionTypes()650     private int getActiveProjectionTypes() throws Exception {
651         return callWithShellPermissionIdentity(mUiModeManager::getActiveProjectionTypes,
652                 Manifest.permission.READ_PROJECTION_STATE);
653     }
654 
getProjectingPackages(int projectionType)655     private Set<String> getProjectingPackages(int projectionType) throws Exception {
656         return callWithShellPermissionIdentity(
657                 () -> mUiModeManager.getProjectingPackages(projectionType),
658                 Manifest.permission.READ_PROJECTION_STATE);
659     }
660 
661     /**
662      * Attempts to request automotive projection without TOGGLE_AUTOMOTIVE_PROJECTION permission.
663      */
testRequestAutomotiveProjectionDenied()664     public void testRequestAutomotiveProjectionDenied() {
665         try {
666             mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
667         } catch (SecurityException se) {
668             // Expect exception.
669             return;
670         }
671         fail("Expected SecurityException");
672     }
673 
674     /**
675      * Attempts to request automotive projection without TOGGLE_AUTOMOTIVE_PROJECTION permission.
676      */
testReleaseAutomotiveProjectionDenied()677     public void testReleaseAutomotiveProjectionDenied() {
678         try {
679             mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
680         } catch (SecurityException se) {
681             // Expect exception.
682             return;
683         }
684         fail("Expected SecurityException");
685     }
686 
687     /**
688      * Attempts to request more than one projection type at once.
689      */
testRequestAllProjectionTypes()690     public void testRequestAllProjectionTypes() {
691         try {
692             mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_ALL);
693         } catch (IllegalArgumentException iae) {
694             // Expect exception.
695             return;
696         }
697         fail("Expected IllegalArgumentException");
698     }
699 
700     /**
701      * Attempts to release more than one projection type.
702      */
testReleaseAllProjectionTypes()703     public void testReleaseAllProjectionTypes() {
704         try {
705             mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_ALL);
706         } catch (IllegalArgumentException iae) {
707             // Expect exception.
708             return;
709         }
710         fail("Expected IllegalArgumentException");
711     }
712 
713     /** Attempts to request no projection types. */
testRequestNoProjectionTypes()714     public void testRequestNoProjectionTypes() {
715         try {
716             mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_NONE);
717         } catch (IllegalArgumentException iae) {
718             // Expect exception.
719             return;
720         }
721         fail("Expected IllegalArgumentException");
722     }
723 
724     /** Attempts to release no projection types. */
testReleaseNoProjectionTypes()725     public void testReleaseNoProjectionTypes() {
726         try {
727             mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_NONE);
728         } catch (IllegalArgumentException iae) {
729             // Expect exception.
730             return;
731         }
732         fail("Expected IllegalArgumentException");
733     }
734 
735     /** Attempts to call getActiveProjectionTypes without READ_PROJECTION_STATE permission. */
testReadProjectionState_getActiveProjectionTypesDenied()736     public void testReadProjectionState_getActiveProjectionTypesDenied() {
737         try {
738             mUiModeManager.getActiveProjectionTypes();
739         } catch (SecurityException se) {
740             // Expect exception.
741             return;
742         }
743         fail("Expected SecurityException");
744     }
745 
746     /** Attempts to call getProjectingPackages without READ_PROJECTION_STATE permission. */
testReadProjectionState_getProjectingPackagesDenied()747     public void testReadProjectionState_getProjectingPackagesDenied() {
748         try {
749             mUiModeManager.getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL);
750         } catch (SecurityException se) {
751             // Expect exception.
752             return;
753         }
754         fail("Expected SecurityException");
755     }
756 
757     /**
758      * Attempts to call addOnProjectionStateChangedListener without
759      * READ_PROJECTION_STATE permission.
760      */
testReadProjectionState_addOnProjectionStateChangedListenerDenied()761     public void testReadProjectionState_addOnProjectionStateChangedListenerDenied() {
762         try {
763             mUiModeManager.addOnProjectionStateChangedListener(UiModeManager.PROJECTION_TYPE_ALL,
764                     getContext().getMainExecutor(), (t, pkgs) -> { });
765         } catch (SecurityException se) {
766             // Expect exception.
767             return;
768         }
769         fail("Expected SecurityException");
770     }
771 
772     /**
773      * Attempts to call removeOnProjectionStateChangedListener without
774      * READ_PROJECTION_STATE permission.
775      */
testReadProjectionState_removeOnProjectionStateChangedListenerDenied()776     public void testReadProjectionState_removeOnProjectionStateChangedListenerDenied() {
777         UiModeManager.OnProjectionStateChangedListener listener = (t, pkgs) -> { };
778         runWithShellPermissionIdentity(() -> mUiModeManager.addOnProjectionStateChangedListener(
779                 UiModeManager.PROJECTION_TYPE_ALL, getContext().getMainExecutor(), listener),
780                 Manifest.permission.READ_PROJECTION_STATE);
781         try {
782             mUiModeManager.removeOnProjectionStateChangedListener(listener);
783         } catch (SecurityException se) {
784             // Expect exception.
785             return;
786         }
787         fail("Expected SecurityException");
788     }
789 
testGetContrast()790     public void testGetContrast() throws Exception {
791         float initialContrast = mUiModeManager.getContrast();
792         putContrastInSettings(0f);
793         try {
794             for (float testContrast : List.of(-1f, 0.5f)) {
795                 putContrastInSettings(testContrast);
796                 TestUtils.waitUntil("getContrast() should return the new contrast value",
797                         (int) MAX_WAIT_TIME_SECS,
798                         () -> Math.abs(mUiModeManager.getContrast() - testContrast) < 1e-10);
799             }
800         } finally {
801             putContrastInSettings(initialContrast);
802         }
803     }
804 
testAddContrastChangeListener()805     public void testAddContrastChangeListener() {
806         float initialContrast = mUiModeManager.getContrast();
807         putContrastInSettings(0f);
808         final Object waitObject = new Object();
809         final List<ContrastChangeListener> listeners = new ArrayList<>();
810 
811         // store callback errors here to avoid crashing the whole test suite
812         final AtomicReference<AssertionError> error = new AtomicReference<>(null);
813         final AtomicBoolean atomicBoolean = new AtomicBoolean(false);
814 
815         String handlerThreadName = "UiModeManagerTestBackgroundThread";
816         final HandlerThread backgroundThread = new HandlerThread(handlerThreadName);
817         backgroundThread.start();
818         Handler backgroundHandler = new Handler(backgroundThread.getLooper());
819         Executor backgroundExecutor = backgroundHandler::post;
820 
821         try {
822             for (float testContrast : List.of(-1f, 0f)) {
823                 ContrastChangeListener listener = (float contrast) -> {
824                     synchronized (waitObject) {
825                         try {
826                             assertEquals("Wrong value received by the color contrast listener",
827                                     testContrast, contrast, 1e-10);
828                             assertEquals("The executor should be used to invoke the callback",
829                                     backgroundThread, Thread.currentThread());
830                         } catch (AssertionError e) {
831                             error.set(e);
832                         }
833                         atomicBoolean.set(true);
834                         waitObject.notifyAll();
835                     }
836                 };
837                 listeners.add(listener);
838                 mUiModeManager.addContrastChangeListener(backgroundExecutor, listener);
839                 putContrastInSettings(testContrast);
840                 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject,
841                         "The color contrast listener should be called when the setting changes");
842                 if (error.get() != null) throw error.get();
843                 mUiModeManager.removeContrastChangeListener(listener);
844             }
845         } finally {
846             backgroundThread.quitSafely();
847             putContrastInSettings(initialContrast);
848             listeners.forEach(mUiModeManager::removeContrastChangeListener);
849         }
850     }
851 
testRemoveContrastChangeListener()852     public void testRemoveContrastChangeListener() {
853         float initialContrast = mUiModeManager.getContrast();
854         putContrastInSettings(0f);
855         final Object waitObject = new Object();
856 
857         // store callback errors here to avoid crashing the whole test suite
858         final AtomicReference<AssertionError> error = new AtomicReference<>(null);
859         final AtomicBoolean atomicBoolean = new AtomicBoolean(false);
860         Executor mainExecutor = mContext.getMainExecutor();
861         ContrastChangeListener listener = (float value) -> {
862             synchronized (waitObject) {
863                 atomicBoolean.set(true);
864                 waitObject.notifyAll();
865             }
866         };
867 
868         ContrastChangeListener removedListener = (float contrast) -> error.set(new AssertionError(
869                 "The listener should not be invoked after being removed"));
870 
871         final List<ContrastChangeListener> listeners = List.of(listener, removedListener);
872         try {
873             for (float testContrast: List.of(0.5f, 1f)) {
874                 mUiModeManager.addContrastChangeListener(mainExecutor, removedListener);
875                 mUiModeManager.addContrastChangeListener(mainExecutor, listener);
876                 mUiModeManager.removeContrastChangeListener(removedListener);
877 
878                 putContrastInSettings(testContrast);
879                 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject,
880                         "The color contrast listener should be called when the setting changes");
881                 if (error.get() != null) throw error.get();
882                 mUiModeManager.removeContrastChangeListener(listener);
883             }
884         } finally {
885             putContrastInSettings(initialContrast);
886             listeners.forEach(mUiModeManager::removeContrastChangeListener);
887         }
888     }
889 
isAutomotive()890     private boolean isAutomotive() {
891         return getContext().getPackageManager().hasSystemFeature(
892                 PackageManager.FEATURE_AUTOMOTIVE);
893     }
894 
isTelevision()895     private boolean isTelevision() {
896         PackageManager pm = getContext().getPackageManager();
897         return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
898                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
899     }
900 
isWatch()901     private boolean isWatch() {
902         return getContext().getPackageManager().hasSystemFeature(
903                 PackageManager.FEATURE_WATCH);
904     }
905 
doTestUiModeForAutomotive()906     private void doTestUiModeForAutomotive() throws Exception {
907         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
908         assertTrue(mUiModeManager.isUiModeLocked());
909         doTestLockedUiMode();
910     }
911 
doTestUiModeGeneric()912     private void doTestUiModeGeneric() throws Exception {
913         if (mUiModeManager.isUiModeLocked()) {
914             doTestLockedUiMode();
915         } else {
916             doTestUnlockedUiMode();
917         }
918     }
919 
doTestLockedUiMode()920     private void doTestLockedUiMode() throws Exception {
921         int originalMode = mUiModeManager.getCurrentModeType();
922         mUiModeManager.enableCarMode(0);
923         assertEquals(originalMode, mUiModeManager.getCurrentModeType());
924         mUiModeManager.disableCarMode(0);
925         assertEquals(originalMode, mUiModeManager.getCurrentModeType());
926     }
927 
doTestUnlockedUiMode()928     private void doTestUnlockedUiMode() throws Exception {
929         mUiModeManager.enableCarMode(0);
930         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
931         mUiModeManager.disableCarMode(0);
932         assertNotSame(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
933     }
934 
doTestLockedNightMode()935     private void doTestLockedNightMode() throws Exception {
936         int currentMode = mUiModeManager.getNightMode();
937         if (currentMode == UiModeManager.MODE_NIGHT_YES) {
938             mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
939             assertEquals(currentMode, mUiModeManager.getNightMode());
940         } else {
941             mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_YES);
942             assertEquals(currentMode, mUiModeManager.getNightMode());
943         }
944     }
945 
doTestUnlockedNightMode()946     private void doTestUnlockedNightMode() throws Exception {
947         // day night mode should be settable regardless of car mode.
948         mUiModeManager.enableCarMode(0);
949         doTestAllNightModes();
950         mUiModeManager.disableCarMode(0);
951         doTestAllNightModes();
952     }
953 
doTestAllNightModes()954     private void doTestAllNightModes() {
955         assertNightModeChange(UiModeManager.MODE_NIGHT_AUTO);
956         assertNightModeChange(UiModeManager.MODE_NIGHT_YES);
957         assertNightModeChange(UiModeManager.MODE_NIGHT_NO);
958     }
959 
assertNightModeChange(int mode)960     private void assertNightModeChange(int mode) {
961         mUiModeManager.setNightMode(mode);
962         assertEquals(mode, mUiModeManager.getNightMode());
963     }
964 
assertVisibleNightModeInConfiguration(int mode)965     private void assertVisibleNightModeInConfiguration(int mode) {
966         int uiMode = getContext().getResources().getConfiguration().uiMode;
967         int flags = uiMode & Configuration.UI_MODE_NIGHT_MASK;
968         assertEquals(mode, flags);
969     }
970 
assertStoredNightModeSetting(int mode)971     private void assertStoredNightModeSetting(int mode) {
972         int storedModeInt = -1;
973         // Settings.Secure.UI_NIGHT_MODE
974         for (int i = 0; i < MAX_WAIT_TIME_MS; i += WAIT_TIME_INCR_MS) {
975             String storedMode = getUiNightModeFromSetting();
976             if (!TextUtils.isEmpty(storedMode)) {
977                 storedModeInt = Integer.parseInt(storedMode);
978             }
979             if (mode == storedModeInt) break;
980             try {
981                 Thread.sleep(WAIT_TIME_INCR_MS);
982             } catch (InterruptedException e) {
983                 e.printStackTrace();
984             }
985         }
986         assertEquals(mode, storedModeInt);
987     }
988 
setNightMode(int mode)989     private void setNightMode(int mode) {
990         String modeString = "unknown";
991         switch (mode) {
992             case UiModeManager.MODE_NIGHT_AUTO:
993                 modeString = "auto";
994                 break;
995             case UiModeManager.MODE_NIGHT_NO:
996                 modeString = "no";
997                 break;
998             case UiModeManager.MODE_NIGHT_YES:
999                 modeString = "yes";
1000                 break;
1001         }
1002         final String command = " cmd uimode night " + modeString;
1003         applyCommand(command);
1004     }
1005 
setStartTime(LocalTime t)1006     private void setStartTime(LocalTime t) {
1007         final String command = " cmd uimode time start " + t.toString();
1008         applyCommand(command);
1009     }
1010 
setEndTime(LocalTime t)1011     private void setEndTime(LocalTime t) {
1012         final String command = " cmd uimode time end " + t.toString();
1013         applyCommand(command);
1014     }
1015 
applyCommand(String command)1016     private void applyCommand(String command) {
1017         final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
1018         uiAutomation.adoptShellPermissionIdentity(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1019         try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
1020             Assert.assertNotNull("Failed to execute shell command: " + command, fd);
1021             // Wait for the command to finish by reading until EOF
1022             try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
1023                 byte[] buffer = new byte[4096];
1024                 while (in.read(buffer) > 0) continue;
1025             } catch (IOException e) {
1026                 throw new IOException("Could not read stdout of command: " + command, e);
1027             }
1028         } catch (IOException e) {
1029             fail();
1030         } finally {
1031             uiAutomation.destroy();
1032         }
1033     }
1034 
getUiNightModeFromSetting()1035     private String getUiNightModeFromSetting() {
1036         return mSystemUserSettings.get("ui_night_mode");
1037     }
1038 
acquireModifyNightModePermission()1039     private void acquireModifyNightModePermission() {
1040         getInstrumentation().getUiAutomation()
1041                 .adoptShellPermissionIdentity(Manifest.permission.MODIFY_DAY_NIGHT_MODE);
1042         mHasModifiedNightModePermissionAcquired = true;
1043     }
1044 
waitForAtomicBooleanBecomes(AtomicBoolean atomicBoolean, boolean expectedValue, Object waitObject, String condition)1045     private void waitForAtomicBooleanBecomes(AtomicBoolean atomicBoolean,
1046             boolean expectedValue, Object waitObject, String condition) {
1047         TestUtils.waitOn(waitObject, () -> atomicBoolean.get() == expectedValue,
1048                 MAX_WAIT_TIME_MS, condition);
1049     }
1050 
putContrastInSettings(float contrast)1051     private void putContrastInSettings(float contrast) {
1052         runWithShellPermissionIdentity(() -> Settings.Secure.putFloat(
1053                 mContext.getContentResolver(), CONTRAST_LEVEL, contrast), WRITE_SECURE_SETTINGS);
1054     }
1055 }
1056