xref: /aosp_15_r20/cts/tests/app/src/android/app/cts/UiModeManagerTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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.junit.Assert.assertEquals;
35 import static org.junit.Assert.assertFalse;
36 import static org.junit.Assert.assertNotNull;
37 import static org.junit.Assert.assertNotSame;
38 import static org.junit.Assert.assertTrue;
39 import static org.junit.Assert.fail;
40 import static org.testng.Assert.assertThrows;
41 
42 import android.Manifest;
43 import android.app.UiAutomation;
44 import android.app.UiModeManager;
45 import android.app.UiModeManager.ContrastChangeListener;
46 import android.content.Context;
47 import android.content.pm.PackageManager;
48 import android.content.res.Configuration;
49 import android.os.Handler;
50 import android.os.HandlerThread;
51 import android.os.ParcelFileDescriptor;
52 import android.os.UserHandle;
53 import android.provider.Settings;
54 import android.text.TextUtils;
55 import android.util.ArraySet;
56 import android.util.Log;
57 
58 import androidx.test.runner.AndroidJUnit4;
59 
60 import com.android.bedstead.harrier.DeviceState;
61 import com.android.bedstead.multiuser.annotations.RequireRunNotOnVisibleBackgroundNonProfileUser;
62 import com.android.bedstead.multiuser.annotations.RequireRunOnVisibleBackgroundNonProfileUser;
63 import com.android.compatibility.common.util.BatteryUtils;
64 import com.android.compatibility.common.util.CommonTestUtils;
65 import com.android.compatibility.common.util.TestUtils;
66 import com.android.compatibility.common.util.UserHelper;
67 import com.android.compatibility.common.util.UserSettings;
68 
69 import com.google.common.util.concurrent.MoreExecutors;
70 
71 import junit.framework.Assert;
72 
73 import org.junit.After;
74 import org.junit.Before;
75 import org.junit.ClassRule;
76 import org.junit.Rule;
77 import org.junit.Test;
78 import org.junit.runner.RunWith;
79 
80 import java.io.FileInputStream;
81 import java.io.IOException;
82 import java.io.InputStream;
83 import java.time.LocalTime;
84 import java.util.ArrayList;
85 import java.util.List;
86 import java.util.Set;
87 import java.util.concurrent.Executor;
88 import java.util.concurrent.atomic.AtomicBoolean;
89 import java.util.concurrent.atomic.AtomicInteger;
90 import java.util.concurrent.atomic.AtomicReference;
91 
92 @RunWith(AndroidJUnit4.class)
93 public class UiModeManagerTest {
94     @ClassRule @Rule
95     public static final DeviceState sDeviceState = new DeviceState();
96 
97     private static final String TAG = "UiModeManagerTest";
98     private static final long MAX_WAIT_TIME_SECS = 2;
99     private static final long MAX_WAIT_TIME_MS = MAX_WAIT_TIME_SECS * 1000;
100     private static final long WAIT_TIME_INCR_MS = 100;
101 
102     private static final String CONTRAST_LEVEL = "contrast_level";
103 
104     private final UserHelper mUserHelper = new UserHelper();
105 
106     private Context mContext;
107     private Context mTargetContext;
108 
109     private final UserSettings mSystemUserSettings = new UserSettings(UserHandle.USER_SYSTEM);
110     private UiModeManager mUiModeManager;
111     private boolean mHasModifiedNightModePermissionAcquired = false;
112 
113     @Before
setUp()114     public void setUp() throws Exception {
115         mContext = getInstrumentation().getContext();
116         mTargetContext = getInstrumentation().getTargetContext();
117         mUiModeManager = mContext.getSystemService(UiModeManager.class);
118         assertNotNull(mUiModeManager);
119         resetNightMode();
120         // Make sure automotive projection is not set by this package at the beginning of the test.
121         releaseAutomotiveProjection();
122     }
123 
124     @After
tearDown()125     public void tearDown() throws Exception {
126         resetNightMode();
127         BatteryUtils.runDumpsysBatteryReset();
128         BatteryUtils.resetBatterySaver();
129     }
130 
resetNightMode()131     private void resetNightMode() {
132         try {
133             if (!mHasModifiedNightModePermissionAcquired) {
134                 acquireModifyNightModePermission();
135             }
136             if (mUserHelper.isVisibleBackgroundUser()) {
137                 // TODO(b/340960913): remove it once uimode supports visible background users.
138                 return;
139             }
140             mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
141             mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
142                     false /* active */);
143         } finally {
144             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
145             mHasModifiedNightModePermissionAcquired = false;
146         }
147     }
148 
149     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
150     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
151             + " visible background users at the moment, so skipping these tests for"
152             + " secondary_user_on_secondary_display.")
153     @Test
testUiMode()154     public void testUiMode() throws Exception {
155         if (isAutomotive()) {
156             Log.i(TAG, "testUiMode automotive");
157             doTestUiModeForAutomotive();
158         } else if (isTelevision()) {
159             assertEquals(Configuration.UI_MODE_TYPE_TELEVISION,
160                     mUiModeManager.getCurrentModeType());
161             doTestLockedUiMode();
162         } else if (isWatch()) {
163             assertEquals(Configuration.UI_MODE_TYPE_WATCH,
164                     mUiModeManager.getCurrentModeType());
165             doTestLockedUiMode();
166         } else {
167             Log.i(TAG, "testUiMode generic");
168             doTestUiModeGeneric();
169         }
170     }
171 
172     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
173     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
174             + " visible background users at the moment, so skipping these tests for"
175             + " secondary_user_on_secondary_display.")
176     @Test
testNightMode()177     public void testNightMode() throws Exception {
178         if (isAutomotive()) {
179             assertTrue(mUiModeManager.isNightModeLocked());
180             doTestLockedNightMode();
181         } else {
182             if (mUiModeManager.isNightModeLocked()) {
183                 doTestLockedNightMode();
184             } else {
185                 doTestUnlockedNightMode();
186             }
187         }
188     }
189 
190     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
191     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
192             + " visible background users at the moment, so skipping these tests for"
193             + " secondary_user_on_secondary_display.")
194     @Test
testSetAndGetCustomTimeStart()195     public void testSetAndGetCustomTimeStart() {
196         LocalTime time = mUiModeManager.getCustomNightModeStart();
197         // decrease time
198         LocalTime timeNew = LocalTime.of(
199                 (time.getHour() + 1) % 12,
200                 (time.getMinute() + 30) % 60);
201         setStartTime(timeNew);
202         assertNotSame(time, timeNew);
203         assertEquals(timeNew, mUiModeManager.getCustomNightModeStart());
204     }
205 
206     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
207     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
208             + " visible background users at the moment, so skipping these tests for"
209             + " secondary_user_on_secondary_display.")
210     @Test
testSetAndGetCustomTimeEnd()211     public void testSetAndGetCustomTimeEnd() {
212         LocalTime time = mUiModeManager.getCustomNightModeEnd();
213         // decrease time
214         LocalTime timeNew = LocalTime.of(
215                 (time.getHour() + 1) % 12,
216                 (time.getMinute() + 30) % 60);
217         setEndTime(timeNew);
218         assertNotSame(time, timeNew);
219         assertEquals(timeNew, mUiModeManager.getCustomNightModeEnd());
220     }
221 
222     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
223     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
224             + " visible background users at the moment, so skipping these tests for"
225             + " secondary_user_on_secondary_display.")
226     @Test
testNightModeYesPersisted()227     public void testNightModeYesPersisted() throws InterruptedException {
228         if (mUiModeManager.isNightModeLocked()) {
229             Log.i(TAG, "testNightModeYesPersisted skipped: night mode is locked");
230             return;
231         }
232 
233         // Reset the mode to no if it is set to another value
234         setNightMode(UiModeManager.MODE_NIGHT_NO);
235 
236         setNightMode(UiModeManager.MODE_NIGHT_YES);
237         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_YES);
238     }
239 
240     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
241     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
242             + " visible background users at the moment, so skipping these tests for"
243             + " secondary_user_on_secondary_display.")
244     @Test
testNightModeAutoPersisted()245     public void testNightModeAutoPersisted() throws InterruptedException {
246         if (mUiModeManager.isNightModeLocked()) {
247             Log.i(TAG, "testNightModeAutoPersisted skipped: night mode is locked");
248             return;
249         }
250 
251         // Reset the mode to no if it is set to another value
252         setNightMode(UiModeManager.MODE_NIGHT_NO);
253 
254         setNightMode(UiModeManager.MODE_NIGHT_AUTO);
255         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_AUTO);
256     }
257 
258     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
259     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
260             + " visible background users at the moment, so skipping these tests for"
261             + " secondary_user_on_secondary_display.")
262     @Test
testSetNightModeCustomType_noPermission_bedtime_shouldThrow()263     public void testSetNightModeCustomType_noPermission_bedtime_shouldThrow() {
264         assertThrows(SecurityException.class,
265                 () -> mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME));
266     }
267 
268     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
269     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
270             + " visible background users at the moment, so skipping these tests for"
271             + " secondary_user_on_secondary_display.")
272     @Test
testSetNightModeCustomType_customTypeUnknown_bedtime_shouldThrow()273     public void testSetNightModeCustomType_customTypeUnknown_bedtime_shouldThrow() {
274         acquireModifyNightModePermission();
275 
276         assertThrows(IllegalArgumentException.class,
277                 () -> mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN));
278     }
279 
280     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
281     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
282             + " visible background users at the moment, so skipping these tests for"
283             + " secondary_user_on_secondary_display.")
284     @Test
testSetNightModeCustomType_bedtime_shouldPersist()285     public void testSetNightModeCustomType_bedtime_shouldPersist() {
286         acquireModifyNightModePermission();
287         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
288 
289         assertThat(mUiModeManager.getNightMode()).isEqualTo(MODE_NIGHT_CUSTOM);
290         assertThat(mUiModeManager.getNightModeCustomType())
291                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
292     }
293 
294     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
295     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
296             + " visible background users at the moment, so skipping these tests for"
297             + " secondary_user_on_secondary_display.")
298     @Test
testSetNightModeCustomType_schedule_shouldPersist()299     public void testSetNightModeCustomType_schedule_shouldPersist() {
300         acquireModifyNightModePermission();
301         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
302 
303         assertThat(mUiModeManager.getNightMode()).isEqualTo(MODE_NIGHT_CUSTOM);
304         assertThat(mUiModeManager.getNightModeCustomType())
305                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
306     }
307 
308     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
309     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
310             + " visible background users at the moment, so skipping these tests for"
311             + " secondary_user_on_secondary_display.")
312     @Test
testGetNightModeCustomType_nightModeNo_shouldReturnUnknown()313     public void testGetNightModeCustomType_nightModeNo_shouldReturnUnknown() {
314         acquireModifyNightModePermission();
315         mUiModeManager.setNightMode(MODE_NIGHT_NO);
316 
317         assertThat(mUiModeManager.getNightModeCustomType())
318                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
319     }
320 
321     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
322     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
323             + " visible background users at the moment, so skipping these tests for"
324             + " secondary_user_on_secondary_display.")
325     @Test
testGetNightModeCustomType_nightModeYes_shouldReturnUnknown()326     public void testGetNightModeCustomType_nightModeYes_shouldReturnUnknown() {
327         acquireModifyNightModePermission();
328         mUiModeManager.setNightMode(MODE_NIGHT_YES);
329 
330         assertThat(mUiModeManager.getNightModeCustomType())
331                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
332     }
333 
334     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
335     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
336             + " visible background users at the moment, so skipping these tests for"
337             + " secondary_user_on_secondary_display.")
338     @Test
testGetNightModeCustomType_nightModeAuto_shouldReturnUnknown()339     public void testGetNightModeCustomType_nightModeAuto_shouldReturnUnknown() {
340         acquireModifyNightModePermission();
341         mUiModeManager.setNightMode(MODE_NIGHT_AUTO);
342 
343         assertThat(mUiModeManager.getNightModeCustomType())
344                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
345     }
346 
347     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
348     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
349             + " visible background users at the moment, so skipping these tests for"
350             + " secondary_user_on_secondary_display.")
351     @Test
testGetNightModeCustomType_nightModeCustom_shouldReturnScheduleAsDefault()352     public void testGetNightModeCustomType_nightModeCustom_shouldReturnScheduleAsDefault() {
353         acquireModifyNightModePermission();
354         mUiModeManager.setNightMode(MODE_NIGHT_CUSTOM);
355 
356         assertThat(mUiModeManager.getNightModeCustomType())
357                 .isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
358     }
359 
360     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
361     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
362             + " visible background users at the moment, so skipping these tests for"
363             + " secondary_user_on_secondary_display.")
364     @Test
testSetNightModeCustomType_hasPermission_bedtime_shouldNotActivateNightMode()365     public void testSetNightModeCustomType_hasPermission_bedtime_shouldNotActivateNightMode() {
366         acquireModifyNightModePermission();
367         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
368 
369         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
370     }
371 
372     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
373     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
374             + " visible background users at the moment, so skipping these tests for"
375             + " secondary_user_on_secondary_display.")
376     @Test
testSetNightModeActivatedForCustomMode_noPermission_customTypeBedtime_activateAtBedtime_shouldNotActivateNightMode()377     public void testSetNightModeActivatedForCustomMode_noPermission_customTypeBedtime_activateAtBedtime_shouldNotActivateNightMode() {
378         acquireModifyNightModePermission();
379         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
380         getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
381         mHasModifiedNightModePermissionAcquired = false;
382 
383         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
384                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
385 
386         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
387     }
388 
389     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
390     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
391             + " visible background users at the moment, so skipping these tests for"
392             + " secondary_user_on_secondary_display.")
393     @Test
testSetNightModeActivatedForCustomMode_customTypeBedtime_activateAtBedtime_shouldActivateNightMode()394     public void testSetNightModeActivatedForCustomMode_customTypeBedtime_activateAtBedtime_shouldActivateNightMode() {
395         acquireModifyNightModePermission();
396         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
397 
398         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
399                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isTrue();
400 
401         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
402     }
403 
404     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
405     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
406             + " visible background users at the moment, so skipping these tests for"
407             + " secondary_user_on_secondary_display.")
408     @Test
testSetNightModeActivatedForCustomMode_customTypeBedtime_deactivateAtBedtime_shouldDeactivateNightMode()409     public void testSetNightModeActivatedForCustomMode_customTypeBedtime_deactivateAtBedtime_shouldDeactivateNightMode() {
410         acquireModifyNightModePermission();
411         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
412 
413         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
414                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */)).isTrue();
415 
416         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
417     }
418 
419     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
420     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
421             + " visible background users at the moment, so skipping these tests for"
422             + " secondary_user_on_secondary_display.")
423     @Test
testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_shouldNotActivateNightMode()424     public void testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_shouldNotActivateNightMode() {
425         acquireModifyNightModePermission();
426         mUiModeManager.setNightMode(MODE_NIGHT_NO);
427 
428         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
429                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
430 
431         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
432     }
433 
434     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
435     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
436             + " visible background users at the moment, so skipping these tests for"
437             + " secondary_user_on_secondary_display.")
438     @Test
testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_shouldKeepNightModeActivated()439     public void testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_shouldKeepNightModeActivated() {
440         acquireModifyNightModePermission();
441         mUiModeManager.setNightMode(MODE_NIGHT_YES);
442 
443         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
444                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
445 
446         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
447     }
448 
449     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
450     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
451             + " visible background users at the moment, so skipping these tests for"
452             + " secondary_user_on_secondary_display.")
453     @Test
testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_shouldNotActivateNightMode()454     public void testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_shouldNotActivateNightMode() {
455         acquireModifyNightModePermission();
456         mUiModeManager.setNightMode(MODE_NIGHT_AUTO);
457         int uiMode = mContext.getResources().getConfiguration().uiMode
458                 & Configuration.UI_MODE_NIGHT_MASK;
459 
460         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
461                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
462 
463         assertVisibleNightModeInConfiguration(uiMode);
464     }
465 
466     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
467     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
468             + " visible background users at the moment, so skipping these tests for"
469             + " secondary_user_on_secondary_display.")
470     @Test
testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_shouldNotActivateNightMode()471     public void testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_shouldNotActivateNightMode() {
472         acquireModifyNightModePermission();
473         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
474         int uiMode = mContext.getResources().getConfiguration().uiMode
475                 & Configuration.UI_MODE_NIGHT_MASK;
476 
477         assertThat(mUiModeManager.setNightModeActivatedForCustomMode(
478                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */)).isFalse();
479 
480         assertVisibleNightModeInConfiguration(uiMode);
481     }
482 
483     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
484     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
485             + " visible background users at the moment, so skipping these tests for"
486             + " secondary_user_on_secondary_display.")
487     @Test
testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode()488     public void testSetNightModeActivatedForCustomMode_modeNo_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode() {
489         acquireModifyNightModePermission();
490         mUiModeManager.setNightMode(MODE_NIGHT_NO);
491         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
492                 true /* active */);
493 
494         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
495 
496         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
497     }
498 
499     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
500     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
501             + " visible background users at the moment, so skipping these tests for"
502             + " secondary_user_on_secondary_display.")
503     @Test
testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_thenCustomTypeBedtime_shouldActiveNightMode()504     public void testSetNightModeActivatedForCustomMode_modeYes_activateAtBedtime_thenCustomTypeBedtime_shouldActiveNightMode() {
505         acquireModifyNightModePermission();
506         mUiModeManager.setNightMode(MODE_NIGHT_YES);
507         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
508                 true /* active */);
509 
510         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
511 
512         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
513     }
514 
515     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
516     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
517             + " visible background users at the moment, so skipping these tests for"
518             + " secondary_user_on_secondary_display.")
519     @Test
testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode()520     public void testSetNightModeActivatedForCustomMode_modeAuto_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode() {
521         acquireModifyNightModePermission();
522         mUiModeManager.setNightMode(MODE_NIGHT_AUTO);
523         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
524                 true /* active */);
525 
526         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
527 
528         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
529     }
530 
531     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
532     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
533             + " visible background users at the moment, so skipping these tests for"
534             + " secondary_user_on_secondary_display.")
535     @Test
testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode()536     public void testSetNightModeActivatedForCustomMode_customTypeSchedule_activateAtBedtime_thenCustomTypeBedtime_shouldActivateNightMode() {
537         acquireModifyNightModePermission();
538         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
539         mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
540                 true /* active */);
541 
542         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
543 
544         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
545     }
546 
547     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
548     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
549             + " visible background users at the moment, so skipping these tests for"
550             + " secondary_user_on_secondary_display.")
551     @Test
testNightModeAutoNotPersistedCarMode()552     public void testNightModeAutoNotPersistedCarMode() {
553         if (mUiModeManager.isNightModeLocked()) {
554             Log.i(TAG, "testNightModeAutoNotPersistedCarMode skipped: night mode is locked");
555             return;
556         }
557 
558         // Reset the mode to no if it is set to another value
559         setNightMode(UiModeManager.MODE_NIGHT_NO);
560         mUiModeManager.enableCarMode(0);
561 
562         setNightMode(UiModeManager.MODE_NIGHT_AUTO);
563         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
564         mUiModeManager.disableCarMode(0);
565     }
566 
567     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
568     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
569             + " visible background users at the moment, so skipping these tests for"
570             + " secondary_user_on_secondary_display.")
571     @Test
testNightModeCustomTypeBedtimeNotPersistedInCarMode()572     public void testNightModeCustomTypeBedtimeNotPersistedInCarMode() throws InterruptedException {
573         if (mUiModeManager.isNightModeLocked()) {
574             Log.i(TAG, "testNightModeCustomTypeBedtimeNotPersistedInCarMode skipped: night mode is "
575                     + "locked");
576             return;
577         }
578 
579         acquireModifyNightModePermission();
580         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
581         mUiModeManager.enableCarMode(0);
582 
583         // We don't expect users modifing bedtime features when driving.
584         mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
585 
586         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
587         mUiModeManager.disableCarMode(0);
588     }
589 
590     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
591     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
592             + " visible background users at the moment, so skipping these tests for"
593             + " secondary_user_on_secondary_display.")
594     @Test
testNightModeInCarModeIsTransient()595     public void testNightModeInCarModeIsTransient() {
596         if (mUiModeManager.isNightModeLocked()) {
597             Log.i(TAG, "testNightModeInCarModeIsTransient skipped: night mode is locked");
598             return;
599         }
600 
601         assertNightModeChange(UiModeManager.MODE_NIGHT_NO);
602 
603         mUiModeManager.enableCarMode(0);
604         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
605 
606         assertNightModeChange(UiModeManager.MODE_NIGHT_YES);
607 
608         mUiModeManager.disableCarMode(0);
609         assertNotSame(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
610         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
611     }
612 
613     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
614     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
615             + " visible background users at the moment, so skipping these tests for"
616             + " secondary_user_on_secondary_display.")
617     @Test
testNightModeToggleInCarModeDoesNotChangeSetting()618     public void testNightModeToggleInCarModeDoesNotChangeSetting() {
619         if (mUiModeManager.isNightModeLocked()) {
620             Log.i(TAG, "testNightModeToggleInCarModeDoesNotChangeSetting skipped: "
621                     + "night mode is locked");
622             return;
623         }
624 
625         assertNightModeChange(UiModeManager.MODE_NIGHT_NO);
626         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
627 
628         mUiModeManager.enableCarMode(0);
629         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
630 
631         assertNightModeChange(UiModeManager.MODE_NIGHT_YES);
632         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
633 
634         mUiModeManager.disableCarMode(0);
635         assertStoredNightModeSetting(UiModeManager.MODE_NIGHT_NO);
636     }
637 
638     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
639     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
640             + " visible background users at the moment, so skipping these tests for"
641             + " secondary_user_on_secondary_display.")
642     @Test
testNightModeInCarModeOnPowerSaveIsTransient()643     public void testNightModeInCarModeOnPowerSaveIsTransient() throws Throwable {
644         if (mUiModeManager.isNightModeLocked() || !BatteryUtils.isBatterySaverSupported()) {
645             Log.i(TAG, "testNightModeInCarModeOnPowerSaveIsTransient skipped: "
646                     + "night mode is locked or battery saver is not supported");
647             return;
648         }
649 
650         BatteryUtils.runDumpsysBatteryUnplug();
651 
652         // Turn off battery saver, disable night mode
653         BatteryUtils.enableBatterySaver(false);
654         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
655         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
656         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
657 
658         // Then enable battery saver to check night mode is made visible
659         BatteryUtils.enableBatterySaver(true);
660         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
661         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
662 
663         // Then disable it, enable car mode, and check night mode is not visible
664         BatteryUtils.enableBatterySaver(false);
665         mUiModeManager.enableCarMode(0);
666         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
667         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
668 
669         // Enable battery saver, check that night mode is still not visible, overridden by car mode
670         BatteryUtils.enableBatterySaver(true);
671         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
672         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
673 
674         // Disable car mode
675         mUiModeManager.disableCarMode(0);
676 
677         // Toggle night mode to force propagation of uiMode update, since disabling car mode
678         // is deferred to a broadcast.
679         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_YES);
680         mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
681 
682         // Check battery saver mode now shows night mode
683         assertNotSame(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
684         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_YES);
685 
686         // Disable battery saver and check night mode back to not visible
687         BatteryUtils.enableBatterySaver(false);
688         assertEquals(UiModeManager.MODE_NIGHT_NO, mUiModeManager.getNightMode());
689         assertVisibleNightModeInConfiguration(Configuration.UI_MODE_NIGHT_NO);
690     }
691 
692     /**
693      * Verifies that an app holding the ENTER_CAR_MODE_PRIORITIZED permission can enter car mode
694      * while specifying a priority.
695      */
696     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
697     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
698             + " visible background users at the moment, so skipping these tests for"
699             + " secondary_user_on_secondary_display.")
700     @Test
testEnterCarModePrioritized()701     public void testEnterCarModePrioritized() {
702         if (mUiModeManager.isUiModeLocked()) {
703             return;
704         }
705 
706         runWithShellPermissionIdentity(() -> mUiModeManager.enableCarMode(100, 0),
707                 Manifest.permission.ENTER_CAR_MODE_PRIORITIZED);
708         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
709 
710         runWithShellPermissionIdentity(() -> mUiModeManager.disableCarMode(0),
711                 Manifest.permission.ENTER_CAR_MODE_PRIORITIZED);
712         assertEquals(Configuration.UI_MODE_TYPE_NORMAL, mUiModeManager.getCurrentModeType());
713     }
714 
715     /**
716      * Attempts to use the prioritized car mode API when the caller does not hold the correct
717      * permission to use that API.
718      */
719     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
720     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
721             + " visible background users at the moment, so skipping these tests for"
722             + " secondary_user_on_secondary_display.")
723     @Test
testEnterCarModePrioritizedDenied()724     public void testEnterCarModePrioritizedDenied() {
725         if (mUiModeManager.isUiModeLocked()) {
726             return;
727         }
728         try {
729             mUiModeManager.enableCarMode(100, 0);
730         } catch (SecurityException se) {
731             // Expect exception.
732             return;
733         }
734         fail("Expected SecurityException");
735     }
736 
737     /**
738      * Verifies that an app holding the TOGGLE_AUTOMOTIVE_PROJECTION permission can request/release
739      * automotive projection.
740      */
741     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
742     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
743             + " visible background users at the moment, so skipping these tests for"
744             + " secondary_user_on_secondary_display.")
745     @Test
testToggleAutomotiveProjection()746     public void testToggleAutomotiveProjection() throws Exception {
747         // If we didn't hold it in the first place, we didn't release it, so expect false.
748         assertFalse(releaseAutomotiveProjection());
749         assertTrue(requestAutomotiveProjection());
750         // Multiple calls are OK.
751         assertTrue(requestAutomotiveProjection());
752         assertTrue(releaseAutomotiveProjection());
753         // Once it's released, further calls return false since it was already released.
754         assertFalse(releaseAutomotiveProjection());
755     }
756 
757     /**
758      * Verifies that the system can correctly read the projection state.
759      */
760     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
761     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
762             + " visible background users at the moment, so skipping these tests for"
763             + " secondary_user_on_secondary_display.")
764     @Test
testReadProjectionState()765     public void testReadProjectionState() throws Exception {
766         assertEquals(UiModeManager.PROJECTION_TYPE_NONE, getActiveProjectionTypes());
767         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE).isEmpty());
768         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL).isEmpty());
769         requestAutomotiveProjection();
770         assertEquals(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, getActiveProjectionTypes());
771         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE)
772                 .contains(mTargetContext.getPackageName()));
773         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL)
774                 .contains(mTargetContext.getPackageName()));
775         releaseAutomotiveProjection();
776         assertEquals(UiModeManager.PROJECTION_TYPE_NONE, getActiveProjectionTypes());
777         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE).isEmpty());
778         assertTrue(getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL).isEmpty());
779     }
780 
781     /** Verifies that the system receives callbacks about the projection state at expected times. */
782     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
783     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
784             + " visible background users at the moment, so skipping these tests for"
785             + " secondary_user_on_secondary_display.")
786     @Test
testReadProjectionState_listener()787     public void testReadProjectionState_listener() throws Exception {
788         // Use AtomicInteger so it can be effectively final.
789         AtomicInteger activeProjectionTypes = new AtomicInteger();
790         Set<String> projectingPackages = new ArraySet<>();
791         AtomicInteger callbackInvocations = new AtomicInteger();
792         UiModeManager.OnProjectionStateChangedListener listener = (t, pkgs) -> {
793             Log.i(TAG, "onProjectionStateChanged(" + t + "," + pkgs + ")");
794             activeProjectionTypes.set(t);
795             projectingPackages.clear();
796             projectingPackages.addAll(pkgs);
797             callbackInvocations.incrementAndGet();
798         };
799 
800         requestAutomotiveProjection();
801         runWithShellPermissionIdentity(() -> mUiModeManager.addOnProjectionStateChangedListener(
802                 UiModeManager.PROJECTION_TYPE_ALL, MoreExecutors.directExecutor(), listener),
803                 Manifest.permission.READ_PROJECTION_STATE);
804 
805         // Should have called back immediately, but the call might not have gotten here yet.
806         CommonTestUtils.waitUntil("Callback wasn't invoked on listener addition!",
807                 MAX_WAIT_TIME_SECS, () -> callbackInvocations.get() == 1);
808         assertEquals(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, activeProjectionTypes.get());
809         assertEquals(1, projectingPackages.size());
810         assertTrue(projectingPackages.contains(mTargetContext.getPackageName()));
811 
812         // Callback should not be invoked again.
813         requestAutomotiveProjection();
814         Thread.sleep(MAX_WAIT_TIME_MS);
815         assertEquals(1, callbackInvocations.get());
816 
817         releaseAutomotiveProjection();
818         CommonTestUtils.waitUntil("Callback wasn't invoked on projection release!",
819                 MAX_WAIT_TIME_SECS, () -> callbackInvocations.get() == 2);
820         assertEquals(UiModeManager.PROJECTION_TYPE_NONE, activeProjectionTypes.get());
821         assertEquals(0, projectingPackages.size());
822 
823         // Again, no callback for noop call.
824         releaseAutomotiveProjection();
825         Thread.sleep(MAX_WAIT_TIME_MS);
826         assertEquals(2, callbackInvocations.get());
827 
828         // Test the case that isn't at time of registration.
829         requestAutomotiveProjection();
830         CommonTestUtils.waitUntil("Callback wasn't invoked on projection set!",
831                 MAX_WAIT_TIME_SECS, () -> callbackInvocations.get() == 3);
832         assertEquals(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, activeProjectionTypes.get());
833         assertEquals(1, projectingPackages.size());
834         assertTrue(projectingPackages.contains(mTargetContext.getPackageName()));
835 
836         // Unregister and shouldn't receive further callbacks.
837         runWithShellPermissionIdentity(() -> mUiModeManager.removeOnProjectionStateChangedListener(
838                 listener), Manifest.permission.READ_PROJECTION_STATE);
839 
840         releaseAutomotiveProjection();
841         requestAutomotiveProjection();
842         releaseAutomotiveProjection(); // Just to clean up.
843         Thread.sleep(MAX_WAIT_TIME_MS);
844         assertEquals(3, callbackInvocations.get());
845     }
846 
requestAutomotiveProjection()847     private boolean requestAutomotiveProjection() throws Exception {
848         return callWithShellPermissionIdentity(
849                 () -> mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE),
850                 Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION,
851                 Manifest.permission.INTERACT_ACROSS_USERS);
852     }
853 
releaseAutomotiveProjection()854     private boolean releaseAutomotiveProjection() throws Exception {
855         if (mUserHelper.isVisibleBackgroundUser()) {
856             // TODO(b/340960913): remove it once uimode supports visible background users.
857             return false;
858         }
859         return callWithShellPermissionIdentity(
860                 () -> mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE),
861                 Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION,
862                 Manifest.permission.INTERACT_ACROSS_USERS);
863     }
864 
getActiveProjectionTypes()865     private int getActiveProjectionTypes() throws Exception {
866         return callWithShellPermissionIdentity(mUiModeManager::getActiveProjectionTypes,
867                 Manifest.permission.READ_PROJECTION_STATE);
868     }
869 
getProjectingPackages(int projectionType)870     private Set<String> getProjectingPackages(int projectionType) throws Exception {
871         return callWithShellPermissionIdentity(
872                 () -> mUiModeManager.getProjectingPackages(projectionType),
873                 Manifest.permission.READ_PROJECTION_STATE);
874     }
875 
876     /**
877      * Attempts to request automotive projection without TOGGLE_AUTOMOTIVE_PROJECTION permission.
878      */
879     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
880     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
881             + " visible background users at the moment, so skipping these tests for"
882             + " secondary_user_on_secondary_display.")
883     @Test
testRequestAutomotiveProjectionDenied()884     public void testRequestAutomotiveProjectionDenied() {
885         try {
886             mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
887         } catch (SecurityException se) {
888             // Expect exception.
889             return;
890         }
891         fail("Expected SecurityException");
892     }
893 
894     /**
895      * Attempts to request automotive projection without TOGGLE_AUTOMOTIVE_PROJECTION permission.
896      */
897     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
898     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
899             + " visible background users at the moment, so skipping these tests for"
900             + " secondary_user_on_secondary_display.")
901     @Test
testReleaseAutomotiveProjectionDenied()902     public void testReleaseAutomotiveProjectionDenied() {
903         try {
904             mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
905         } catch (SecurityException se) {
906             // Expect exception.
907             return;
908         }
909         fail("Expected SecurityException");
910     }
911 
912     /**
913      * Attempts to request more than one projection type at once.
914      */
915     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
916     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
917             + " visible background users at the moment, so skipping these tests for"
918             + " secondary_user_on_secondary_display.")
919     @Test
testRequestAllProjectionTypes()920     public void testRequestAllProjectionTypes() {
921         try {
922             mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_ALL);
923         } catch (IllegalArgumentException iae) {
924             // Expect exception.
925             return;
926         }
927         fail("Expected IllegalArgumentException");
928     }
929 
930     /**
931      * Attempts to release more than one projection type.
932      */
933     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
934     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
935             + " visible background users at the moment, so skipping these tests for"
936             + " secondary_user_on_secondary_display.")
937     @Test
testReleaseAllProjectionTypes()938     public void testReleaseAllProjectionTypes() {
939         try {
940             mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_ALL);
941         } catch (IllegalArgumentException iae) {
942             // Expect exception.
943             return;
944         }
945         fail("Expected IllegalArgumentException");
946     }
947 
948     /** Attempts to request no projection types. */
949     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
950     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
951             + " visible background users at the moment, so skipping these tests for"
952             + " secondary_user_on_secondary_display.")
953     @Test
testRequestNoProjectionTypes()954     public void testRequestNoProjectionTypes() {
955         try {
956             mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_NONE);
957         } catch (IllegalArgumentException iae) {
958             // Expect exception.
959             return;
960         }
961         fail("Expected IllegalArgumentException");
962     }
963 
964     /** Attempts to release no projection types. */
965     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
966     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
967             + " visible background users at the moment, so skipping these tests for"
968             + " secondary_user_on_secondary_display.")
969     @Test
testReleaseNoProjectionTypes()970     public void testReleaseNoProjectionTypes() {
971         try {
972             mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_NONE);
973         } catch (IllegalArgumentException iae) {
974             // Expect exception.
975             return;
976         }
977         fail("Expected IllegalArgumentException");
978     }
979 
980     /** Attempts to call getActiveProjectionTypes without READ_PROJECTION_STATE permission. */
981     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
982     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
983             + " visible background users at the moment, so skipping these tests for"
984             + " secondary_user_on_secondary_display.")
985     @Test
testReadProjectionState_getActiveProjectionTypesDenied()986     public void testReadProjectionState_getActiveProjectionTypesDenied() {
987         try {
988             mUiModeManager.getActiveProjectionTypes();
989         } catch (SecurityException se) {
990             // Expect exception.
991             return;
992         }
993         fail("Expected SecurityException");
994     }
995 
996     /** Attempts to call getProjectingPackages without READ_PROJECTION_STATE permission. */
997     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
998     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
999             + " visible background users at the moment, so skipping these tests for"
1000             + " secondary_user_on_secondary_display.")
1001     @Test
testReadProjectionState_getProjectingPackagesDenied()1002     public void testReadProjectionState_getProjectingPackagesDenied() {
1003         try {
1004             mUiModeManager.getProjectingPackages(UiModeManager.PROJECTION_TYPE_ALL);
1005         } catch (SecurityException se) {
1006             // Expect exception.
1007             return;
1008         }
1009         fail("Expected SecurityException");
1010     }
1011 
1012     /**
1013      * Attempts to call addOnProjectionStateChangedListener without
1014      * READ_PROJECTION_STATE permission.
1015      */
1016     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
1017     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
1018             + " visible background users at the moment, so skipping these tests for"
1019             + " secondary_user_on_secondary_display.")
1020     @Test
testReadProjectionState_addOnProjectionStateChangedListenerDenied()1021     public void testReadProjectionState_addOnProjectionStateChangedListenerDenied() {
1022         try {
1023             mUiModeManager.addOnProjectionStateChangedListener(UiModeManager.PROJECTION_TYPE_ALL,
1024                     mContext.getMainExecutor(), (t, pkgs) -> { });
1025         } catch (SecurityException se) {
1026             // Expect exception.
1027             return;
1028         }
1029         fail("Expected SecurityException");
1030     }
1031 
1032     /**
1033      * Attempts to call removeOnProjectionStateChangedListener without
1034      * READ_PROJECTION_STATE permission.
1035      */
1036     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
1037     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
1038             + " visible background users at the moment, so skipping these tests for"
1039             + " secondary_user_on_secondary_display.")
1040     @Test
testReadProjectionState_removeOnProjectionStateChangedListenerDenied()1041     public void testReadProjectionState_removeOnProjectionStateChangedListenerDenied() {
1042         UiModeManager.OnProjectionStateChangedListener listener = (t, pkgs) -> { };
1043         runWithShellPermissionIdentity(() -> mUiModeManager.addOnProjectionStateChangedListener(
1044                 UiModeManager.PROJECTION_TYPE_ALL, mContext.getMainExecutor(), listener),
1045                 Manifest.permission.READ_PROJECTION_STATE);
1046         try {
1047             mUiModeManager.removeOnProjectionStateChangedListener(listener);
1048         } catch (SecurityException se) {
1049             // Expect exception.
1050             return;
1051         }
1052         fail("Expected SecurityException");
1053     }
1054 
1055     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
1056     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
1057             + " visible background users at the moment, so skipping these tests for"
1058             + " secondary_user_on_secondary_display.")
1059     @Test
testGetContrast()1060     public void testGetContrast() throws Exception {
1061         float initialContrast = mUiModeManager.getContrast();
1062         putContrastInSettings(0f);
1063         try {
1064             for (float testContrast : List.of(-1f, 0.5f)) {
1065                 putContrastInSettings(testContrast);
1066                 TestUtils.waitUntil("getContrast() should return the new contrast value",
1067                         (int) MAX_WAIT_TIME_SECS,
1068                         () -> Math.abs(mUiModeManager.getContrast() - testContrast) < 1e-10);
1069             }
1070         } finally {
1071             putContrastInSettings(initialContrast);
1072         }
1073     }
1074 
1075     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
1076     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
1077             + " visible background users at the moment, so skipping these tests for"
1078             + " secondary_user_on_secondary_display.")
1079     @Test
testAddContrastChangeListener()1080     public void testAddContrastChangeListener() {
1081         float initialContrast = mUiModeManager.getContrast();
1082         putContrastInSettings(0f);
1083         final Object waitObject = new Object();
1084         final List<ContrastChangeListener> listeners = new ArrayList<>();
1085 
1086         // store callback errors here to avoid crashing the whole test suite
1087         final AtomicReference<AssertionError> error = new AtomicReference<>(null);
1088         final AtomicBoolean atomicBoolean = new AtomicBoolean(false);
1089 
1090         String handlerThreadName = "UiModeManagerTestBackgroundThread";
1091         final HandlerThread backgroundThread = new HandlerThread(handlerThreadName);
1092         backgroundThread.start();
1093         Handler backgroundHandler = new Handler(backgroundThread.getLooper());
1094         Executor backgroundExecutor = backgroundHandler::post;
1095 
1096         try {
1097             for (float testContrast : List.of(-1f, 0f)) {
1098                 ContrastChangeListener listener = (float contrast) -> {
1099                     synchronized (waitObject) {
1100                         try {
1101                             assertEquals("Wrong value received by the color contrast listener",
1102                                     testContrast, contrast, 1e-10);
1103                             assertEquals("The executor should be used to invoke the callback",
1104                                     backgroundThread, Thread.currentThread());
1105                         } catch (AssertionError e) {
1106                             error.set(e);
1107                         }
1108                         atomicBoolean.set(true);
1109                         waitObject.notifyAll();
1110                     }
1111                 };
1112                 listeners.add(listener);
1113                 mUiModeManager.addContrastChangeListener(backgroundExecutor, listener);
1114                 putContrastInSettings(testContrast);
1115                 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject,
1116                         "The color contrast listener should be called when the setting changes");
1117                 if (error.get() != null) throw error.get();
1118                 mUiModeManager.removeContrastChangeListener(listener);
1119             }
1120         } finally {
1121             backgroundThread.quitSafely();
1122             putContrastInSettings(initialContrast);
1123             listeners.forEach(mUiModeManager::removeContrastChangeListener);
1124         }
1125     }
1126 
1127     // TODO(b/340960913): remove the annotation once uimode supports visible background users.
1128     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "The uimode does not support"
1129             + " visible background users at the moment, so skipping these tests for"
1130             + " secondary_user_on_secondary_display.")
1131     @Test
testRemoveContrastChangeListener()1132     public void testRemoveContrastChangeListener() {
1133         float initialContrast = mUiModeManager.getContrast();
1134         putContrastInSettings(0f);
1135         final Object waitObject = new Object();
1136 
1137         // store callback errors here to avoid crashing the whole test suite
1138         final AtomicReference<AssertionError> error = new AtomicReference<>(null);
1139         final AtomicBoolean atomicBoolean = new AtomicBoolean(false);
1140         Executor mainExecutor = mContext.getMainExecutor();
1141         ContrastChangeListener listener = (float value) -> {
1142             synchronized (waitObject) {
1143                 atomicBoolean.set(true);
1144                 waitObject.notifyAll();
1145             }
1146         };
1147 
1148         ContrastChangeListener removedListener = (float contrast) -> error.set(new AssertionError(
1149                 "The listener should not be invoked after being removed"));
1150 
1151         final List<ContrastChangeListener> listeners = List.of(listener, removedListener);
1152         try {
1153             for (float testContrast: List.of(0.5f, 1f)) {
1154                 mUiModeManager.addContrastChangeListener(mainExecutor, removedListener);
1155                 mUiModeManager.addContrastChangeListener(mainExecutor, listener);
1156                 mUiModeManager.removeContrastChangeListener(removedListener);
1157 
1158                 putContrastInSettings(testContrast);
1159                 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject,
1160                         "The color contrast listener should be called when the setting changes");
1161                 if (error.get() != null) throw error.get();
1162                 mUiModeManager.removeContrastChangeListener(listener);
1163             }
1164         } finally {
1165             putContrastInSettings(initialContrast);
1166             listeners.forEach(mUiModeManager::removeContrastChangeListener);
1167         }
1168     }
1169 
1170     // TODO(b/340960913): remove the test once uimode supports visible background users.
1171     @RequireRunOnVisibleBackgroundNonProfileUser
1172     @Test
testSetNightMode_visibleBackgroundUser_shouldThrow()1173     public void testSetNightMode_visibleBackgroundUser_shouldThrow() {
1174         acquireModifyNightModePermission();
1175 
1176         assertThrows(SecurityException.class,
1177                 () -> mUiModeManager.setNightMode(MODE_NIGHT_NO));
1178     }
1179 
1180     // TODO(b/340960913): remove the test once uimode supports visible background users.
1181     @RequireRunOnVisibleBackgroundNonProfileUser
1182     @Test
testSetNightModeActivatedForCustomMode_visibleBackgroundUser_shouldThrow()1183     public void testSetNightModeActivatedForCustomMode_visibleBackgroundUser_shouldThrow() {
1184         acquireModifyNightModePermission();
1185 
1186         assertThrows(SecurityException.class,
1187                 () -> mUiModeManager.setNightModeActivatedForCustomMode(
1188                     MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */));
1189     }
1190 
1191     // TODO(b/340960913): remove the test once uimode supports visible background users.
1192     @RequireRunOnVisibleBackgroundNonProfileUser
1193     @Test
testSetCustomNightModeStart_visibleBackgroundUser_shouldThrow()1194     public void testSetCustomNightModeStart_visibleBackgroundUser_shouldThrow() {
1195         acquireModifyNightModePermission();
1196 
1197         LocalTime now = LocalTime.now();
1198         assertThrows(SecurityException.class,
1199                 () -> mUiModeManager.setCustomNightModeStart(now));
1200     }
1201 
1202     // TODO(b/340960913): remove the test once uimode supports visible background users.
1203     @RequireRunOnVisibleBackgroundNonProfileUser
1204     @Test
testSetCustomNightModeEnd_visibleBackgroundUser_shouldThrow()1205     public void testSetCustomNightModeEnd_visibleBackgroundUser_shouldThrow() {
1206         acquireModifyNightModePermission();
1207 
1208         LocalTime now = LocalTime.now();
1209         assertThrows(SecurityException.class,
1210                 () -> mUiModeManager.setCustomNightModeEnd(now));
1211     }
1212 
1213     // TODO(b/340960913): remove the test once uimode supports visible background users.
1214     @RequireRunOnVisibleBackgroundNonProfileUser
1215     @Test
testRequestProjection_visibleBackgroundUser_shouldThrow()1216     public void testRequestProjection_visibleBackgroundUser_shouldThrow() {
1217         getInstrumentation().getUiAutomation()
1218                 .adoptShellPermissionIdentity(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION);
1219 
1220         assertThrows(SecurityException.class,
1221                 () -> mUiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE));
1222     }
1223 
1224     // TODO(b/340960913): remove the test once uimode supports visible background users.
1225     @RequireRunOnVisibleBackgroundNonProfileUser
1226     @Test
testReleaseProjection_visibleBackgroundUser_shouldThrow()1227     public void testReleaseProjection_visibleBackgroundUser_shouldThrow() {
1228         getInstrumentation().getUiAutomation()
1229                 .adoptShellPermissionIdentity(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION);
1230 
1231         assertThrows(SecurityException.class,
1232                 () -> mUiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE));
1233     }
1234 
1235     // TODO(b/340960913): remove the test once uimode supports visible background users.
1236     @RequireRunOnVisibleBackgroundNonProfileUser
1237     @Test
testAddOnProjectionStateChangedListener_visibleBackgroundUser_shouldThrow()1238     public void testAddOnProjectionStateChangedListener_visibleBackgroundUser_shouldThrow() {
1239         getInstrumentation().getUiAutomation()
1240                 .adoptShellPermissionIdentity(Manifest.permission.READ_PROJECTION_STATE);
1241 
1242         assertThrows(SecurityException.class,
1243                 () -> mUiModeManager.addOnProjectionStateChangedListener(
1244                     UiModeManager.PROJECTION_TYPE_ALL,
1245                     mContext.getMainExecutor(), (t, pkgs) -> { }));
1246     }
1247 
isAutomotive()1248     private boolean isAutomotive() {
1249         return mContext.getPackageManager().hasSystemFeature(
1250                 PackageManager.FEATURE_AUTOMOTIVE);
1251     }
1252 
isTelevision()1253     private boolean isTelevision() {
1254         PackageManager pm = mContext.getPackageManager();
1255         return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
1256                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
1257     }
1258 
isWatch()1259     private boolean isWatch() {
1260         return mContext.getPackageManager().hasSystemFeature(
1261                 PackageManager.FEATURE_WATCH);
1262     }
1263 
doTestUiModeForAutomotive()1264     private void doTestUiModeForAutomotive() throws Exception {
1265         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
1266         assertTrue(mUiModeManager.isUiModeLocked());
1267         doTestLockedUiMode();
1268     }
1269 
doTestUiModeGeneric()1270     private void doTestUiModeGeneric() throws Exception {
1271         if (mUiModeManager.isUiModeLocked()) {
1272             doTestLockedUiMode();
1273         } else {
1274             doTestUnlockedUiMode();
1275         }
1276     }
1277 
doTestLockedUiMode()1278     private void doTestLockedUiMode() throws Exception {
1279         int originalMode = mUiModeManager.getCurrentModeType();
1280         mUiModeManager.enableCarMode(0);
1281         assertEquals(originalMode, mUiModeManager.getCurrentModeType());
1282         mUiModeManager.disableCarMode(0);
1283         assertEquals(originalMode, mUiModeManager.getCurrentModeType());
1284     }
1285 
doTestUnlockedUiMode()1286     private void doTestUnlockedUiMode() throws Exception {
1287         mUiModeManager.enableCarMode(0);
1288         assertEquals(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
1289         mUiModeManager.disableCarMode(0);
1290         assertNotSame(Configuration.UI_MODE_TYPE_CAR, mUiModeManager.getCurrentModeType());
1291     }
1292 
doTestLockedNightMode()1293     private void doTestLockedNightMode() throws Exception {
1294         int currentMode = mUiModeManager.getNightMode();
1295         if (currentMode == UiModeManager.MODE_NIGHT_YES) {
1296             mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
1297             assertEquals(currentMode, mUiModeManager.getNightMode());
1298         } else {
1299             mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_YES);
1300             assertEquals(currentMode, mUiModeManager.getNightMode());
1301         }
1302     }
1303 
doTestUnlockedNightMode()1304     private void doTestUnlockedNightMode() throws Exception {
1305         // day night mode should be settable regardless of car mode.
1306         mUiModeManager.enableCarMode(0);
1307         doTestAllNightModes();
1308         mUiModeManager.disableCarMode(0);
1309         doTestAllNightModes();
1310     }
1311 
doTestAllNightModes()1312     private void doTestAllNightModes() {
1313         assertNightModeChange(UiModeManager.MODE_NIGHT_AUTO);
1314         assertNightModeChange(UiModeManager.MODE_NIGHT_YES);
1315         assertNightModeChange(UiModeManager.MODE_NIGHT_NO);
1316     }
1317 
assertNightModeChange(int mode)1318     private void assertNightModeChange(int mode) {
1319         mUiModeManager.setNightMode(mode);
1320         assertEquals(mode, mUiModeManager.getNightMode());
1321     }
1322 
assertVisibleNightModeInConfiguration(int mode)1323     private void assertVisibleNightModeInConfiguration(int mode) {
1324         int uiMode = mContext.getResources().getConfiguration().uiMode;
1325         int flags = uiMode & Configuration.UI_MODE_NIGHT_MASK;
1326         assertEquals(mode, flags);
1327     }
1328 
assertStoredNightModeSetting(int mode)1329     private void assertStoredNightModeSetting(int mode) {
1330         int storedModeInt = -1;
1331         // Settings.Secure.UI_NIGHT_MODE
1332         for (int i = 0; i < MAX_WAIT_TIME_MS; i += WAIT_TIME_INCR_MS) {
1333             String storedMode = getUiNightModeFromSetting();
1334             if (!TextUtils.isEmpty(storedMode)) {
1335                 storedModeInt = Integer.parseInt(storedMode);
1336             }
1337             if (mode == storedModeInt) break;
1338             try {
1339                 Thread.sleep(WAIT_TIME_INCR_MS);
1340             } catch (InterruptedException e) {
1341                 e.printStackTrace();
1342             }
1343         }
1344         assertEquals(mode, storedModeInt);
1345     }
1346 
setNightMode(int mode)1347     private void setNightMode(int mode) {
1348         String modeString = "unknown";
1349         switch (mode) {
1350             case UiModeManager.MODE_NIGHT_AUTO:
1351                 modeString = "auto";
1352                 break;
1353             case UiModeManager.MODE_NIGHT_NO:
1354                 modeString = "no";
1355                 break;
1356             case UiModeManager.MODE_NIGHT_YES:
1357                 modeString = "yes";
1358                 break;
1359         }
1360         final String command = " cmd uimode night " + modeString;
1361         applyCommand(command);
1362     }
1363 
setStartTime(LocalTime t)1364     private void setStartTime(LocalTime t) {
1365         final String command = " cmd uimode time start " + t.toString();
1366         applyCommand(command);
1367     }
1368 
setEndTime(LocalTime t)1369     private void setEndTime(LocalTime t) {
1370         final String command = " cmd uimode time end " + t.toString();
1371         applyCommand(command);
1372     }
1373 
applyCommand(String command)1374     private void applyCommand(String command) {
1375         final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
1376         uiAutomation.adoptShellPermissionIdentity(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1377         try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
1378             Assert.assertNotNull("Failed to execute shell command: " + command, fd);
1379             // Wait for the command to finish by reading until EOF
1380             try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
1381                 byte[] buffer = new byte[4096];
1382                 while (in.read(buffer) > 0) continue;
1383             } catch (IOException e) {
1384                 throw new IOException("Could not read stdout of command: " + command, e);
1385             }
1386         } catch (IOException e) {
1387             fail();
1388         } finally {
1389             uiAutomation.destroy();
1390         }
1391     }
1392 
getUiNightModeFromSetting()1393     private String getUiNightModeFromSetting() {
1394         return mSystemUserSettings.get("ui_night_mode");
1395     }
1396 
acquireModifyNightModePermission()1397     private void acquireModifyNightModePermission() {
1398         getInstrumentation().getUiAutomation()
1399                 .adoptShellPermissionIdentity(Manifest.permission.MODIFY_DAY_NIGHT_MODE);
1400         mHasModifiedNightModePermissionAcquired = true;
1401     }
1402 
waitForAtomicBooleanBecomes(AtomicBoolean atomicBoolean, boolean expectedValue, Object waitObject, String condition)1403     private void waitForAtomicBooleanBecomes(AtomicBoolean atomicBoolean,
1404             boolean expectedValue, Object waitObject, String condition) {
1405         TestUtils.waitOn(waitObject, () -> atomicBoolean.get() == expectedValue,
1406                 MAX_WAIT_TIME_MS, condition);
1407     }
1408 
putContrastInSettings(float contrast)1409     private void putContrastInSettings(float contrast) {
1410         runWithShellPermissionIdentity(() -> Settings.Secure.putFloat(
1411                 mContext.getContentResolver(), CONTRAST_LEVEL, contrast), WRITE_SECURE_SETTINGS);
1412     }
1413 }
1414