1 /*
2  * Copyright (C) 20019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import static android.Manifest.permission.MODIFY_DAY_NIGHT_MODE;
20 import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_DAY;
21 import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT;
22 import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
23 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
24 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
25 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
26 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
27 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
28 import static android.app.UiModeManager.MODE_NIGHT_NO;
29 import static android.app.UiModeManager.MODE_NIGHT_YES;
30 import static android.app.UiModeManager.PROJECTION_TYPE_ALL;
31 import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
32 import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
33 
34 import static com.android.server.UiModeManagerService.SUPPORTED_NIGHT_MODE_CUSTOM_TYPES;
35 
36 import static com.google.common.truth.Truth.assertThat;
37 
38 import static junit.framework.Assert.fail;
39 import static junit.framework.TestCase.assertFalse;
40 import static junit.framework.TestCase.assertTrue;
41 
42 import static org.hamcrest.Matchers.contains;
43 import static org.hamcrest.Matchers.empty;
44 import static org.junit.Assert.assertEquals;
45 import static org.junit.Assert.assertThat;
46 import static org.mockito.ArgumentMatchers.any;
47 import static org.mockito.ArgumentMatchers.anyInt;
48 import static org.mockito.ArgumentMatchers.anyLong;
49 import static org.mockito.ArgumentMatchers.anyString;
50 import static org.mockito.ArgumentMatchers.eq;
51 import static org.mockito.ArgumentMatchers.notNull;
52 import static org.mockito.ArgumentMatchers.nullable;
53 import static org.mockito.BDDMockito.given;
54 import static org.mockito.Mockito.atLeast;
55 import static org.mockito.Mockito.atLeastOnce;
56 import static org.mockito.Mockito.doAnswer;
57 import static org.mockito.Mockito.doReturn;
58 import static org.mockito.Mockito.doThrow;
59 import static org.mockito.Mockito.mock;
60 import static org.mockito.Mockito.never;
61 import static org.mockito.Mockito.spy;
62 import static org.mockito.Mockito.times;
63 import static org.mockito.Mockito.verify;
64 import static org.mockito.Mockito.verifyNoMoreInteractions;
65 import static org.mockito.Mockito.verifyZeroInteractions;
66 import static org.mockito.Mockito.when;
67 import static org.testng.Assert.assertThrows;
68 
69 import android.Manifest;
70 import android.app.Activity;
71 import android.app.AlarmManager;
72 import android.app.Flags;
73 import android.app.IOnProjectionStateChangedListener;
74 import android.app.IUiModeManager;
75 import android.content.BroadcastReceiver;
76 import android.content.Context;
77 import android.content.Intent;
78 import android.content.IntentFilter;
79 import android.content.pm.PackageManager;
80 import android.content.res.Configuration;
81 import android.content.res.Resources;
82 import android.os.Bundle;
83 import android.os.Handler;
84 import android.os.IBinder;
85 import android.os.IpcDataCache;
86 import android.os.PowerManager;
87 import android.os.PowerManagerInternal;
88 import android.os.PowerSaveState;
89 import android.os.Process;
90 import android.os.RemoteException;
91 import android.os.UserHandle;
92 import android.os.test.FakePermissionEnforcer;
93 import android.platform.test.annotations.EnableFlags;
94 import android.platform.test.flag.junit.SetFlagsRule;
95 import android.provider.Settings;
96 import android.service.dreams.DreamManagerInternal;
97 import android.test.mock.MockContentResolver;
98 import android.testing.AndroidTestingRunner;
99 import android.testing.TestableLooper;
100 
101 import com.android.internal.util.test.FakeSettingsProvider;
102 import com.android.server.twilight.TwilightListener;
103 import com.android.server.twilight.TwilightManager;
104 import com.android.server.twilight.TwilightState;
105 import com.android.server.wm.WindowManagerInternal;
106 
107 import org.junit.Before;
108 import org.junit.Ignore;
109 import org.junit.Rule;
110 import org.junit.Test;
111 import org.junit.runner.RunWith;
112 import org.mockito.ArgumentCaptor;
113 import org.mockito.Captor;
114 import org.mockito.Mock;
115 import org.mockito.Spy;
116 
117 import java.time.LocalDateTime;
118 import java.time.LocalTime;
119 import java.time.ZoneId;
120 import java.util.List;
121 import java.util.Map;
122 import java.util.function.Consumer;
123 
124 @RunWith(AndroidTestingRunner.class)
125 @TestableLooper.RunWithLooper
126 public class UiModeManagerServiceTest extends UiServiceTestCase {
127     private static final String PACKAGE_NAME = "Diane Coffee";
128     private UiModeManagerService mUiManagerService;
129     private IUiModeManager mService;
130     private MockContentResolver mContentResolver;
131     @Mock
132     private WindowManagerInternal mWindowManager;
133     @Mock
134     private Context mContext;
135     @Mock
136     private Resources mResources;
137     @Mock
138     private TwilightManager mTwilightManager;
139     @Mock
140     private PowerManager.WakeLock mWakeLock;
141     @Mock
142     private AlarmManager mAlarmManager;
143     @Mock
144     private PowerManager mPowerManager;
145     @Mock
146     private TwilightState mTwilightState;
147     @Mock
148     PowerManagerInternal mLocalPowerManager;
149 
150     @Mock
151     DreamManagerInternal mDreamManagerInternal;
152     @Mock
153     private PackageManager mPackageManager;
154     @Mock
155     private IBinder mBinder;
156     @Spy
157     private TestInjector mInjector;
158     @Captor
159     private ArgumentCaptor<Intent> mOrderedBroadcastIntent;
160     @Captor
161     private ArgumentCaptor<BroadcastReceiver> mOrderedBroadcastReceiver;
162 
163     private BroadcastReceiver mScreenOffCallback;
164     private BroadcastReceiver mDreamingStartedCallback;
165     private BroadcastReceiver mTimeChangedCallback;
166     private BroadcastReceiver mDockStateChangedCallback;
167     private AlarmManager.OnAlarmListener mCustomListener;
168     private Consumer<PowerSaveState> mPowerSaveConsumer;
169     private TwilightListener mTwilightListener;
170     private FakePermissionEnforcer mPermissionEnforcer;
171 
172     @Rule
173     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
174             SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
175 
176 
177     @Before
disableProcessCaches()178     public void disableProcessCaches() {
179         IpcDataCache.disableForTestMode();
180     }
181 
182     @Before
setUp()183     public void setUp() {
184         // The AIDL stub will use PermissionEnforcer to check permission from the caller.
185         mPermissionEnforcer = new FakePermissionEnforcer();
186         mPermissionEnforcer.grant(Manifest.permission.MODIFY_DAY_NIGHT_MODE);
187         mPermissionEnforcer.grant(Manifest.permission.READ_PROJECTION_STATE);
188         doReturn(mPermissionEnforcer).when(mContext).getSystemService(
189                 eq(Context.PERMISSION_ENFORCER_SERVICE));
190         doAnswer(inv -> {
191             mTwilightListener = (TwilightListener) inv.getArgument(0);
192             return null;
193         }).when(mTwilightManager).registerListener(any(), any());
194         doAnswer(inv -> {
195             mPowerSaveConsumer = (Consumer<PowerSaveState>) inv.getArgument(1);
196             return null;
197         }).when(mLocalPowerManager).registerLowPowerModeObserver(anyInt(), any());
198         when(mLocalPowerManager.getLowPowerState(anyInt()))
199                 .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
200         when(mContext.getResources()).thenReturn(mResources);
201         when(mResources.getString(com.android.internal.R.string.config_somnambulatorComponent))
202                 .thenReturn("somnambulator");
203         mContentResolver = new MockContentResolver();
204         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
205         when(mContext.getContentResolver()).thenReturn(mContentResolver);
206         when(mContext.getPackageManager()).thenReturn(mPackageManager);
207         when(mPowerManager.isInteractive()).thenReturn(true);
208         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
209         when(mTwilightManager.getLastTwilightState()).thenReturn(mTwilightState);
210         when(mTwilightState.isNight()).thenReturn(true);
211         when(mContext.registerReceiver(notNull(), notNull())).then(inv -> {
212             IntentFilter filter = inv.getArgument(1);
213             if (filter.hasAction(Intent.ACTION_TIMEZONE_CHANGED)) {
214                 mTimeChangedCallback = inv.getArgument(0);
215             }
216             if (filter.hasAction(Intent.ACTION_SCREEN_OFF)) {
217                 mScreenOffCallback = inv.getArgument(0);
218             }
219             if (filter.hasAction(Intent.ACTION_DREAMING_STARTED)) {
220                 mDreamingStartedCallback = inv.getArgument(0);
221             }
222             if (filter.hasAction(Intent.ACTION_DOCK_EVENT)) {
223                 mDockStateChangedCallback = inv.getArgument(0);
224             }
225             return null;
226         });
227         doAnswer(inv -> {
228             mCustomListener = inv.getArgument(3);
229             return null;
230         }).when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(),
231                 any(AlarmManager.OnAlarmListener.class), any(Handler.class));
232 
233         doAnswer(inv -> {
234             mCustomListener = () -> {};
235             return null;
236         }).when(mAlarmManager).cancel(eq(mCustomListener));
237         when(mContext.getSystemService(eq(Context.POWER_SERVICE)))
238                 .thenReturn(mPowerManager);
239         when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
240         when(mContext.getSystemService(eq(Context.ALARM_SERVICE)))
241                 .thenReturn(mAlarmManager);
242         addLocalService(WindowManagerInternal.class, mWindowManager);
243         addLocalService(PowerManagerInternal.class, mLocalPowerManager);
244         addLocalService(TwilightManager.class, mTwilightManager);
245         addLocalService(DreamManagerInternal.class, mDreamManagerInternal);
246 
247         mInjector = spy(new TestInjector());
248         mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
249                 mTwilightManager, mInjector);
250         try {
251             mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
252         } catch (SecurityException e) {/* ignore for permission denial */}
253         mService = mUiManagerService.getService();
254     }
255 
addLocalService(Class<T> clazz, T service)256     private <T> void addLocalService(Class<T> clazz, T service) {
257         LocalServices.removeServiceForTest(clazz);
258         LocalServices.addService(clazz, service);
259     }
260 
261     @Ignore // b/152719290 - Fails on stage-aosp-master
262     @Test
setNightModeActivated_overridesFunctionCorrectly()263     public void setNightModeActivated_overridesFunctionCorrectly() throws RemoteException {
264         // set up
265         when(mPowerManager.isInteractive()).thenReturn(false);
266         mService.setNightMode(MODE_NIGHT_NO);
267         assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
268 
269         // assume it is day time
270         doReturn(false).when(mTwilightState).isNight();
271 
272         // set mode to auto
273         mService.setNightMode(MODE_NIGHT_AUTO);
274 
275         // set night mode on overriding current config
276         mService.setNightModeActivated(true);
277 
278         assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
279 
280         // now it is night time
281         doReturn(true).when(mTwilightState).isNight();
282         mTwilightListener.onTwilightStateChanged(mTwilightState);
283 
284         assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
285 
286         // now it is next day mid day
287         doReturn(false).when(mTwilightState).isNight();
288         mTwilightListener.onTwilightStateChanged(mTwilightState);
289 
290         assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
291     }
292 
293     @Test
setNightModeActivated_true_withCustomModeBedtime_shouldOverrideNightModeCorrectly()294     public void setNightModeActivated_true_withCustomModeBedtime_shouldOverrideNightModeCorrectly()
295             throws RemoteException {
296         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
297         assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
298 
299         mService.setNightModeActivated(true);
300 
301         assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isTrue();
302     }
303 
304     @Test
setNightModeActivated_false_withCustomModeBedtime_shouldOverrideNightModeCorrectly()305     public void setNightModeActivated_false_withCustomModeBedtime_shouldOverrideNightModeCorrectly()
306             throws RemoteException {
307         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
308         assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
309 
310         mService.setNightModeActivated(true);
311         mService.setNightModeActivated(false);
312 
313         assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isFalse();
314     }
315 
316     @Test
setAutoMode_deviceInactiveRegistered()317     public void setAutoMode_deviceInactiveRegistered() throws RemoteException {
318         try {
319             mService.setNightMode(MODE_NIGHT_NO);
320         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
321         mService.setNightMode(MODE_NIGHT_AUTO);
322         verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class), any());
323     }
324 
325     @Ignore // b/152719290 - Fails on stage-aosp-master
326     @Test
setAutoMode_deviceInactiveUnRegistered()327     public void setAutoMode_deviceInactiveUnRegistered() throws RemoteException {
328         try {
329             mService.setNightMode(MODE_NIGHT_AUTO);
330         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
331         try {
332             mService.setNightMode(MODE_NIGHT_NO);
333         } catch (SecurityException e) { /*we should ignore this update config exception*/ }
334         given(mContext.registerReceiver(any(), any())).willThrow(SecurityException.class);
335         verify(mContext, atLeastOnce()).unregisterReceiver(any(BroadcastReceiver.class));
336     }
337 
338     @Test
setNightModeCustomType_bedtime_shouldNotActivateNightMode()339     public void setNightModeCustomType_bedtime_shouldNotActivateNightMode() throws RemoteException {
340         try {
341             mService.setNightMode(MODE_NIGHT_NO);
342         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
343         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
344 
345         assertThat(isNightModeActivated()).isFalse();
346     }
347 
348     @Test
setNightModeCustomType_noPermission_shouldThrow()349     public void setNightModeCustomType_noPermission_shouldThrow() throws RemoteException {
350         mPermissionEnforcer.revoke(MODIFY_DAY_NIGHT_MODE);
351 
352         assertThrows(SecurityException.class,
353                 () -> mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME));
354     }
355 
356     @Test
setNightModeCustomType_customTypeUnknown_shouldThrow()357     public void setNightModeCustomType_customTypeUnknown_shouldThrow() throws RemoteException {
358         assertThrows(IllegalArgumentException.class,
359                 () -> mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN));
360     }
361 
362     @Test
setNightModeCustomType_customTypeUnsupported_shouldThrow()363     public void setNightModeCustomType_customTypeUnsupported_shouldThrow() throws RemoteException {
364         assertThrows(IllegalArgumentException.class,
365                 () -> {
366                     int maxSupportedCustomType = 0;
367                     for (Integer supportedType : SUPPORTED_NIGHT_MODE_CUSTOM_TYPES) {
368                         maxSupportedCustomType = Math.max(maxSupportedCustomType, supportedType);
369                     }
370                     mService.setNightModeCustomType(maxSupportedCustomType + 1);
371                 });
372     }
373 
374     @Test
setNightModeCustomType_bedtime_shouldHaveNoScreenOffRegistered()375     public void setNightModeCustomType_bedtime_shouldHaveNoScreenOffRegistered()
376             throws RemoteException {
377         try {
378             mService.setNightMode(MODE_NIGHT_NO);
379         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
380         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
381         ArgumentCaptor<IntentFilter> intentFiltersCaptor = ArgumentCaptor.forClass(
382                 IntentFilter.class);
383         verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class),
384                 intentFiltersCaptor.capture());
385 
386         List<IntentFilter> intentFilters = intentFiltersCaptor.getAllValues();
387         for (IntentFilter intentFilter : intentFilters) {
388             assertThat(intentFilter.hasAction(Intent.ACTION_SCREEN_OFF)).isFalse();
389         }
390     }
391 
392     @Test
setNightModeActivated_fromNoToYesAndBack()393     public void setNightModeActivated_fromNoToYesAndBack() throws RemoteException {
394         mService.setNightMode(MODE_NIGHT_NO);
395         mService.setNightModeActivated(true);
396         assertTrue(isNightModeActivated());
397         mService.setNightModeActivated(false);
398         assertFalse(isNightModeActivated());
399     }
400 
401     @Test
setNightModeActivated_permissionToChangeOtherUsers()402     public void setNightModeActivated_permissionToChangeOtherUsers() throws RemoteException {
403         SystemService.TargetUser user = mock(SystemService.TargetUser.class);
404         doReturn(9).when(user).getUserIdentifier();
405         mUiManagerService.onUserSwitching(user, user);
406         when(mContext.checkCallingOrSelfPermission(
407                 eq(Manifest.permission.INTERACT_ACROSS_USERS)))
408                 .thenReturn(PackageManager.PERMISSION_DENIED);
409         assertFalse(mService.setNightModeActivated(true));
410     }
411 
412     @Test
setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_shouldActivate()413     public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_shouldActivate()
414             throws RemoteException {
415         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
416         mService.setNightModeActivatedForCustomMode(
417                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
418 
419         assertThat(isNightModeActivated()).isTrue();
420     }
421 
422     @Test
setNightModeActivatedForCustomMode_customTypeBedtime_withParamOffAndBedtime_shouldDeactivate()423     public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOffAndBedtime_shouldDeactivate()
424             throws RemoteException {
425         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
426         mService.setNightModeActivatedForCustomMode(
427                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
428 
429         assertThat(isNightModeActivated()).isFalse();
430     }
431 
432     @Test
setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndSchedule_shouldNotActivate()433     public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndSchedule_shouldNotActivate()
434             throws RemoteException {
435         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
436         mService.setNightModeActivatedForCustomMode(
437                 MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, true /* active */);
438 
439         assertThat(isNightModeActivated()).isFalse();
440     }
441 
442     @Test
setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_shouldNotActivate()443     public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_shouldNotActivate()
444             throws RemoteException {
445         mService.setNightMode(MODE_NIGHT_CUSTOM);
446         mService.setNightModeActivatedForCustomMode(
447                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
448 
449         assertThat(isNightModeActivated()).isFalse();
450     }
451 
452     @Test
setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_thenCustomTypeBedtime_shouldActivate()453     public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_thenCustomTypeBedtime_shouldActivate()
454             throws RemoteException {
455         mService.setNightMode(MODE_NIGHT_CUSTOM);
456         mService.setNightModeActivatedForCustomMode(
457                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
458 
459         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
460 
461         assertThat(isNightModeActivated()).isTrue();
462     }
463 
464     @Test
setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeSchedule_shouldKeepNightModeActivate()465     public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeSchedule_shouldKeepNightModeActivate()
466             throws RemoteException {
467         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
468         mService.setNightModeActivatedForCustomMode(
469                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
470 
471         mService.setNightMode(MODE_NIGHT_CUSTOM);
472         LocalTime now = LocalTime.now();
473         mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
474         mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
475 
476         assertThat(isNightModeActivated()).isTrue();
477     }
478 
479     @Test
setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeScheduleAndScreenOff_shouldDeactivateNightMode()480     public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeScheduleAndScreenOff_shouldDeactivateNightMode()
481             throws RemoteException {
482         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
483         mService.setNightModeActivatedForCustomMode(
484                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
485 
486         mService.setNightMode(MODE_NIGHT_CUSTOM);
487         LocalTime now = LocalTime.now();
488         mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
489         mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
490         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
491 
492         assertThat(isNightModeActivated()).isFalse();
493     }
494 
495     @Test
autoNightModeSwitch_batterySaverOn()496     public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
497         mService.setNightMode(MODE_NIGHT_NO);
498         when(mTwilightState.isNight()).thenReturn(false);
499         mService.setNightMode(MODE_NIGHT_AUTO);
500 
501         // night NO
502         assertFalse(isNightModeActivated());
503 
504         mPowerSaveConsumer.accept(
505                 new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
506 
507         // night YES
508         assertTrue(isNightModeActivated());
509     }
510 
511     @Test
nightModeCustomBedtime_batterySaverOn_notInBedtime_shouldActivateNightMode()512     public void nightModeCustomBedtime_batterySaverOn_notInBedtime_shouldActivateNightMode()
513             throws RemoteException {
514         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
515 
516         mPowerSaveConsumer.accept(
517                 new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
518 
519         assertThat(isNightModeActivated()).isTrue();
520     }
521 
522     @Test
nightModeCustomBedtime_batterySaverOn_afterBedtime_shouldKeepNightModeActivated()523     public void nightModeCustomBedtime_batterySaverOn_afterBedtime_shouldKeepNightModeActivated()
524             throws RemoteException {
525         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
526         mPowerSaveConsumer.accept(
527                 new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
528 
529         mService.setNightModeActivatedForCustomMode(
530                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
531 
532         assertThat(isNightModeActivated()).isTrue();
533     }
534 
535     @Test
nightModeBedtime_duringBedtime_batterySaverOnThenOff_shouldKeepNightModeActivated()536     public void nightModeBedtime_duringBedtime_batterySaverOnThenOff_shouldKeepNightModeActivated()
537             throws RemoteException {
538         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
539         mService.setNightModeActivatedForCustomMode(
540                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
541 
542         mPowerSaveConsumer.accept(
543                 new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
544         mPowerSaveConsumer.accept(
545                 new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
546 
547         assertThat(isNightModeActivated()).isTrue();
548     }
549 
550     @Test
nightModeCustomBedtime_duringBedtime_batterySaverOnThenOff_finallyAfterBedtime_shouldDeactivateNightMode()551     public void nightModeCustomBedtime_duringBedtime_batterySaverOnThenOff_finallyAfterBedtime_shouldDeactivateNightMode()
552             throws RemoteException {
553         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
554         mService.setNightModeActivatedForCustomMode(
555                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
556         mPowerSaveConsumer.accept(
557                 new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
558         mPowerSaveConsumer.accept(
559                 new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
560 
561         mService.setNightModeActivatedForCustomMode(
562                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
563 
564         assertThat(isNightModeActivated()).isFalse();
565     }
566 
567     @Test
nightModeCustomBedtime_duringBedtime_changeModeToNo_shouldDeactivateNightMode()568     public void nightModeCustomBedtime_duringBedtime_changeModeToNo_shouldDeactivateNightMode()
569             throws RemoteException {
570         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
571         mService.setNightModeActivatedForCustomMode(
572                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
573 
574         mService.setNightMode(MODE_NIGHT_NO);
575 
576         assertThat(isNightModeActivated()).isFalse();
577     }
578 
579     @Test
nightModeCustomBedtime_duringBedtime_changeModeToNoAndThenExitBedtime_shouldKeepNightModeDeactivated()580     public void nightModeCustomBedtime_duringBedtime_changeModeToNoAndThenExitBedtime_shouldKeepNightModeDeactivated()
581             throws RemoteException {
582         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
583         mService.setNightModeActivatedForCustomMode(
584                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
585         mService.setNightMode(MODE_NIGHT_NO);
586 
587         mService.setNightModeActivatedForCustomMode(
588                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
589 
590         assertThat(isNightModeActivated()).isFalse();
591     }
592 
593     @Test
nightModeCustomBedtime_duringBedtime_changeModeToYes_shouldKeepNightModeActivated()594     public void nightModeCustomBedtime_duringBedtime_changeModeToYes_shouldKeepNightModeActivated()
595             throws RemoteException {
596         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
597         mService.setNightModeActivatedForCustomMode(
598                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
599 
600         mService.setNightMode(MODE_NIGHT_YES);
601 
602         assertThat(isNightModeActivated()).isTrue();
603     }
604 
605     @Test
nightModeCustomBedtime_duringBedtime_changeModeToYesAndThenExitBedtime_shouldKeepNightModeActivated()606     public void nightModeCustomBedtime_duringBedtime_changeModeToYesAndThenExitBedtime_shouldKeepNightModeActivated()
607             throws RemoteException {
608         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
609         mService.setNightModeActivatedForCustomMode(
610                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
611 
612         mService.setNightMode(MODE_NIGHT_YES);
613         mService.setNightModeActivatedForCustomMode(
614                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
615 
616         assertThat(isNightModeActivated()).isTrue();
617     }
618 
619     @Test
nightModeNo_duringBedtime_shouldKeepNightModeDeactivated()620     public void nightModeNo_duringBedtime_shouldKeepNightModeDeactivated()
621             throws RemoteException {
622         mService.setNightMode(MODE_NIGHT_NO);
623 
624         mService.setNightModeActivatedForCustomMode(
625                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
626 
627         assertThat(isNightModeActivated()).isFalse();
628     }
629 
630     @Test
nightModeNo_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()631     public void nightModeNo_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()
632             throws RemoteException {
633         mService.setNightMode(MODE_NIGHT_NO);
634 
635         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
636         mService.setNightModeActivatedForCustomMode(
637                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
638 
639         assertThat(isNightModeActivated()).isTrue();
640     }
641 
642     @Test
nightModeYes_thenChangeToCustomTypeBedtime_shouldDeactivateNightMode()643     public void nightModeYes_thenChangeToCustomTypeBedtime_shouldDeactivateNightMode()
644             throws RemoteException {
645         mService.setNightMode(MODE_NIGHT_YES);
646 
647         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
648 
649         assertThat(isNightModeActivated()).isFalse();
650     }
651 
652     @Test
nightModeYes_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()653     public void nightModeYes_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()
654             throws RemoteException {
655         mService.setNightMode(MODE_NIGHT_YES);
656 
657         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
658         mService.setNightModeActivatedForCustomMode(
659                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
660 
661         assertThat(isNightModeActivated()).isTrue();
662     }
663 
664     @Test
nightModeAuto_thenChangeToCustomTypeBedtime_notInBedtime_shouldDeactivateNightMode()665     public void nightModeAuto_thenChangeToCustomTypeBedtime_notInBedtime_shouldDeactivateNightMode()
666             throws RemoteException {
667         // set mode to auto
668         mService.setNightMode(MODE_NIGHT_AUTO);
669         mService.setNightModeActivated(true);
670         // now it is night time
671         doReturn(true).when(mTwilightState).isNight();
672         mTwilightListener.onTwilightStateChanged(mTwilightState);
673 
674         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
675 
676         assertThat(isNightModeActivated()).isFalse();
677     }
678 
679     @Test
nightModeAuto_thenChangeToCustomTypeBedtime_duringBedtime_shouldActivateNightMode()680     public void nightModeAuto_thenChangeToCustomTypeBedtime_duringBedtime_shouldActivateNightMode()
681             throws RemoteException {
682         // set mode to auto
683         mService.setNightMode(MODE_NIGHT_AUTO);
684         mService.setNightModeActivated(true);
685         // now it is night time
686         doReturn(true).when(mTwilightState).isNight();
687         mTwilightListener.onTwilightStateChanged(mTwilightState);
688 
689         mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
690         mService.setNightModeActivatedForCustomMode(
691                 MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
692 
693         assertThat(isNightModeActivated()).isTrue();
694     }
695 
696     @Test
setAutoMode_clearCache()697     public void setAutoMode_clearCache() throws RemoteException {
698         try {
699             mService.setNightMode(MODE_NIGHT_AUTO);
700         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
701         try {
702             mService.setNightMode(MODE_NIGHT_NO);
703         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
704         verify(mWindowManager).clearSnapshotCache();
705     }
706 
707     @Test
setNightModeActive_fromNightModeYesToNoWhenFalse()708     public void setNightModeActive_fromNightModeYesToNoWhenFalse() throws RemoteException {
709         try {
710             mService.setNightMode(MODE_NIGHT_YES);
711         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
712         try {
713             mService.setNightModeActivated(false);
714         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
715         assertEquals(MODE_NIGHT_NO, mService.getNightMode());
716     }
717 
718     @Test
setNightModeActive_fromNightModeNoToYesWhenTrue()719     public void setNightModeActive_fromNightModeNoToYesWhenTrue() throws RemoteException {
720         try {
721             mService.setNightMode(MODE_NIGHT_NO);
722         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
723         try {
724             mService.setNightModeActivated(true);
725         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
726         assertEquals(MODE_NIGHT_YES, mService.getNightMode());
727     }
728 
729     @Test
setNightModeActive_autoNightModeNoChanges()730     public void setNightModeActive_autoNightModeNoChanges() throws RemoteException {
731         try {
732             mService.setNightMode(MODE_NIGHT_AUTO);
733         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
734         try {
735             mService.setNightModeActivated(true);
736         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
737         assertEquals(MODE_NIGHT_AUTO, mService.getNightMode());
738     }
739 
740     @Test
getNightModeCustomType_nightModeNo_shouldReturnUnknown()741     public void getNightModeCustomType_nightModeNo_shouldReturnUnknown() throws RemoteException {
742         try {
743             mService.setNightMode(MODE_NIGHT_NO);
744         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
745 
746         assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
747     }
748 
749     @Test
getNightModeCustomType_nightModeYes_shouldReturnUnknown()750     public void getNightModeCustomType_nightModeYes_shouldReturnUnknown() throws RemoteException {
751         try {
752             mService.setNightMode(MODE_NIGHT_YES);
753         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
754 
755         assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
756     }
757 
758     @Test
getNightModeCustomType_nightModeAuto_shouldReturnUnknown()759     public void getNightModeCustomType_nightModeAuto_shouldReturnUnknown() throws RemoteException {
760         try {
761             mService.setNightMode(MODE_NIGHT_AUTO);
762         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
763 
764         assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
765     }
766 
767     @Test
getNightModeCustomType_nightModeCustom_shouldReturnSchedule()768     public void getNightModeCustomType_nightModeCustom_shouldReturnSchedule()
769             throws RemoteException {
770         try {
771             mService.setNightMode(MODE_NIGHT_CUSTOM);
772         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
773 
774         assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
775     }
776 
777     @Test
getNightModeCustomType_nightModeCustomBedtime_shouldReturnBedtime()778     public void getNightModeCustomType_nightModeCustomBedtime_shouldReturnBedtime()
779             throws RemoteException {
780         try {
781             mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
782         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
783 
784         assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
785     }
786 
787     @Test
getNightModeCustomType_permissionNotGranted_shouldThrow()788     public void getNightModeCustomType_permissionNotGranted_shouldThrow()
789             throws RemoteException {
790         mPermissionEnforcer.revoke(MODIFY_DAY_NIGHT_MODE);
791 
792         assertThrows(SecurityException.class, () -> mService.getNightModeCustomType());
793     }
794 
795     @Test
isNightModeActive_nightModeYes()796     public void isNightModeActive_nightModeYes() throws RemoteException {
797         try {
798             mService.setNightMode(MODE_NIGHT_YES);
799         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
800         assertTrue(isNightModeActivated());
801     }
802 
803     @Test
isNightModeActive_nightModeNo()804     public void isNightModeActive_nightModeNo() throws RemoteException {
805         try {
806             mService.setNightMode(MODE_NIGHT_NO);
807         } catch (SecurityException e) { /* we should ignore this update config exception*/ }
808         assertFalse(isNightModeActivated());
809     }
810 
811     @Test
customTime_darkThemeOn_afterScreenOff()812     public void customTime_darkThemeOn_afterScreenOff() throws RemoteException {
813         LocalTime now = LocalTime.now();
814         mService.setNightMode(MODE_NIGHT_NO);
815         mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
816         mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
817         mService.setNightMode(MODE_NIGHT_CUSTOM);
818         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
819         assertTrue(isNightModeActivated());
820     }
821 
822     @Test
customTime_darkThemeOff_afterScreenOff()823     public void customTime_darkThemeOff_afterScreenOff() throws RemoteException {
824         LocalTime now = LocalTime.now();
825         mService.setNightMode(MODE_NIGHT_YES);
826         mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
827         mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
828         mService.setNightMode(MODE_NIGHT_CUSTOM);
829         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
830         assertFalse(isNightModeActivated());
831     }
832 
833     @Test
customTime_darkThemeOn_afterDreamingStarted()834     public void customTime_darkThemeOn_afterDreamingStarted() throws RemoteException {
835         LocalTime now = LocalTime.now();
836         mService.setNightMode(MODE_NIGHT_NO);
837         mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
838         mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
839         mService.setNightMode(MODE_NIGHT_CUSTOM);
840         mDreamingStartedCallback.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STARTED));
841         assertTrue(isNightModeActivated());
842     }
843 
844     @Test
customTime_darkThemeOff_afterDreamingStarted()845     public void customTime_darkThemeOff_afterDreamingStarted() throws RemoteException {
846         LocalTime now = LocalTime.now();
847         mService.setNightMode(MODE_NIGHT_YES);
848         mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
849         mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
850         mService.setNightMode(MODE_NIGHT_CUSTOM);
851         mDreamingStartedCallback.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STARTED));
852         assertFalse(isNightModeActivated());
853     }
854 
855     @Test
customTime_darkThemeOff_afterStartEnd()856     public void customTime_darkThemeOff_afterStartEnd() throws RemoteException {
857         LocalTime now = LocalTime.now();
858         mService.setNightMode(MODE_NIGHT_YES);
859         mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
860         mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
861         mService.setNightMode(MODE_NIGHT_CUSTOM);
862         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
863         assertFalse(isNightModeActivated());
864     }
865 
866     @Test
customTime_darkThemeOn_afterStartEnd()867     public void customTime_darkThemeOn_afterStartEnd() throws RemoteException {
868         LocalTime now = LocalTime.now();
869         mService.setNightMode(MODE_NIGHT_YES);
870         mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
871         mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
872         mService.setNightMode(MODE_NIGHT_CUSTOM);
873         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
874         assertFalse(isNightModeActivated());
875     }
876 
877 
878     @Test
customTime_darkThemeOn_beforeStartEnd()879     public void customTime_darkThemeOn_beforeStartEnd() throws RemoteException {
880         LocalTime now = LocalTime.now();
881         mService.setNightMode(MODE_NIGHT_YES);
882         mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
883         mService.setCustomNightModeEnd(now.minusHours(2L).toNanoOfDay() / 1000);
884         mService.setNightMode(MODE_NIGHT_CUSTOM);
885         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
886         assertTrue(isNightModeActivated());
887     }
888 
889     @Test
customTime_darkThemeOff_beforeStartEnd()890     public void customTime_darkThemeOff_beforeStartEnd() throws RemoteException {
891         LocalTime now = LocalTime.now();
892         mService.setNightMode(MODE_NIGHT_YES);
893         mService.setCustomNightModeStart(now.minusHours(2L).toNanoOfDay() / 1000);
894         mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
895         mService.setNightMode(MODE_NIGHT_CUSTOM);
896         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
897         assertFalse(isNightModeActivated());
898     }
899 
900     @Test
customTIme_customAlarmSetWhenScreenTimeChanges()901     public void customTIme_customAlarmSetWhenScreenTimeChanges() throws RemoteException {
902         when(mPowerManager.isInteractive()).thenReturn(false);
903         mService.setNightMode(MODE_NIGHT_CUSTOM);
904         verify(mAlarmManager, times(1))
905                 .setExact(anyInt(), anyLong(), anyString(), any(), any());
906         mTimeChangedCallback.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
907         verify(mAlarmManager, atLeast(2))
908                 .setExact(anyInt(), anyLong(), anyString(), any(), any());
909     }
910 
911     @Test
customTime_alarmSetInTheFutureWhenOn()912     public void customTime_alarmSetInTheFutureWhenOn() throws RemoteException {
913         LocalDateTime now = LocalDateTime.now();
914         when(mPowerManager.isInteractive()).thenReturn(false);
915         mService.setNightMode(MODE_NIGHT_YES);
916         mService.setCustomNightModeStart(now.toLocalTime().minusHours(1L).toNanoOfDay() / 1000);
917         mService.setCustomNightModeEnd(now.toLocalTime().plusHours(1L).toNanoOfDay() / 1000);
918         LocalDateTime next = now.plusHours(1L);
919         final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
920         mService.setNightMode(MODE_NIGHT_CUSTOM);
921         verify(mAlarmManager)
922                 .setExact(anyInt(), eq(millis), anyString(), any(), any());
923     }
924 
925     @Test
customTime_appliesImmediatelyWhenScreenOff()926     public void customTime_appliesImmediatelyWhenScreenOff() throws RemoteException {
927         when(mPowerManager.isInteractive()).thenReturn(false);
928         LocalTime now = LocalTime.now();
929         mService.setNightMode(MODE_NIGHT_NO);
930         mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
931         mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
932         mService.setNightMode(MODE_NIGHT_CUSTOM);
933         assertTrue(isNightModeActivated());
934     }
935 
936     @Test
customTime_appliesOnlyWhenScreenOff()937     public void customTime_appliesOnlyWhenScreenOff() throws RemoteException {
938         LocalTime now = LocalTime.now();
939         mService.setNightMode(MODE_NIGHT_NO);
940         mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
941         mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
942         mService.setNightMode(MODE_NIGHT_CUSTOM);
943         assertFalse(isNightModeActivated());
944         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
945         assertTrue(isNightModeActivated());
946     }
947 
948     @Test
nightAuto_appliesOnlyWhenScreenOff()949     public void nightAuto_appliesOnlyWhenScreenOff() throws RemoteException {
950         when(mTwilightState.isNight()).thenReturn(true);
951         mService.setNightMode(MODE_NIGHT_NO);
952         mService.setNightMode(MODE_NIGHT_AUTO);
953         assertFalse(isNightModeActivated());
954         mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
955         assertTrue(isNightModeActivated());
956     }
957 
isNightModeActivated()958     private boolean isNightModeActivated() {
959         return (mUiManagerService.getConfiguration().uiMode
960                 & Configuration.UI_MODE_NIGHT_YES) != 0;
961     }
962 
963     @Test
requestProjection_failsForBogusPackageName()964     public void requestProjection_failsForBogusPackageName() throws Exception {
965         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
966                 .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
967 
968         assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
969                 PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
970         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
971     }
972 
973     @Test
requestProjection_failsIfNameNotFound()974     public void requestProjection_failsIfNameNotFound() throws Exception {
975         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
976                 .thenThrow(new PackageManager.NameNotFoundException());
977 
978         assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
979                 PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
980         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
981     }
982 
983     @Test
requestProjection_failsIfNoProjectionTypes()984     public void requestProjection_failsIfNoProjectionTypes() throws Exception {
985         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
986                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
987 
988         assertThrows(IllegalArgumentException.class,
989                 () -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
990         verify(mContext, never()).enforceCallingPermission(
991                 eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
992         verifyZeroInteractions(mBinder);
993         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
994     }
995 
996     @Test
requestProjection_failsIfMultipleProjectionTypes()997     public void requestProjection_failsIfMultipleProjectionTypes() throws Exception {
998         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
999                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1000 
1001         // Don't use PROJECTION_TYPE_ALL because that's actually == -1 and will fail the > 0 check.
1002         int multipleProjectionTypes = PROJECTION_TYPE_AUTOMOTIVE | 0x0002 | 0x0004;
1003 
1004         assertThrows(IllegalArgumentException.class,
1005                 () -> mService.requestProjection(mBinder, multipleProjectionTypes, PACKAGE_NAME));
1006         verify(mContext, never()).enforceCallingPermission(
1007                 eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
1008         verifyZeroInteractions(mBinder);
1009         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
1010     }
1011 
1012     @Test
requestProjection_enforcesToggleAutomotiveProjectionPermission()1013     public void requestProjection_enforcesToggleAutomotiveProjectionPermission() throws Exception {
1014         doThrow(new SecurityException())
1015                 .when(mPackageManager).getPackageUidAsUser(eq(PACKAGE_NAME), anyInt());
1016 
1017         assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
1018                 PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
1019         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
1020     }
1021 
1022     @Test
requestProjection_automotive_failsIfAlreadySetByOtherPackage()1023     public void requestProjection_automotive_failsIfAlreadySetByOtherPackage() throws Exception {
1024         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1025                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1026         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1027         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1028 
1029         String otherPackage = "Raconteurs";
1030         when(mPackageManager.getPackageUidAsUser(eq(otherPackage), anyInt()))
1031                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1032         assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, otherPackage));
1033         assertThat(mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE),
1034                 contains(PACKAGE_NAME));
1035     }
1036 
1037     @Test
requestProjection_failsIfCannotLinkToDeath()1038     public void requestProjection_failsIfCannotLinkToDeath() throws Exception {
1039         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1040                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1041         doThrow(new RemoteException()).when(mBinder).linkToDeath(any(), anyInt());
1042 
1043         assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
1044         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
1045     }
1046 
1047     @Test
requestProjection()1048     public void requestProjection() throws Exception {
1049         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1050                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1051         // Should work for all powers of two.
1052         for (int i = 0; i < Integer.SIZE; ++i) {
1053             int projectionType = 1 << i;
1054             assertTrue(mService.requestProjection(mBinder, projectionType, PACKAGE_NAME));
1055             assertTrue((mService.getActiveProjectionTypes() & projectionType) != 0);
1056             assertThat(mService.getProjectingPackages(projectionType), contains(PACKAGE_NAME));
1057             // Subsequent calls should still succeed.
1058             assertTrue(mService.requestProjection(mBinder, projectionType, PACKAGE_NAME));
1059         }
1060         assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
1061     }
1062 
1063     @Test
releaseProjection_failsForBogusPackageName()1064     public void releaseProjection_failsForBogusPackageName() throws Exception {
1065         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1066                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1067         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1068         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1069 
1070         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1071                 .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
1072 
1073         assertThrows(SecurityException.class, () -> mService.releaseProjection(
1074                 PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
1075         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1076     }
1077 
1078     @Test
releaseProjection_failsIfNameNotFound()1079     public void releaseProjection_failsIfNameNotFound() throws Exception {
1080         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1081                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1082         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1083         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1084         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1085                 .thenThrow(new PackageManager.NameNotFoundException());
1086 
1087         assertThrows(SecurityException.class, () -> mService.releaseProjection(
1088                 PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
1089         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1090     }
1091 
1092     @Test
releaseProjection_enforcesToggleAutomotiveProjectionPermission()1093     public void releaseProjection_enforcesToggleAutomotiveProjectionPermission() throws Exception {
1094         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1095                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1096         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1097         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1098         doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
1099                 eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
1100 
1101         // Should not be enforced for other types of projection.
1102         int nonAutomotiveProjectionType = PROJECTION_TYPE_AUTOMOTIVE * 2;
1103         mService.releaseProjection(nonAutomotiveProjectionType, PACKAGE_NAME);
1104         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1105 
1106         assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
1107                 PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
1108         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1109     }
1110 
1111     @Test
releaseProjection()1112     public void releaseProjection() throws Exception {
1113         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1114                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1115         requestAllPossibleProjectionTypes();
1116         assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
1117 
1118         assertTrue(mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
1119         int everythingButAutomotive = PROJECTION_TYPE_ALL & ~PROJECTION_TYPE_AUTOMOTIVE;
1120         assertEquals(everythingButAutomotive, mService.getActiveProjectionTypes());
1121 
1122         for (int i = 0; i < Integer.SIZE; ++i) {
1123             int projectionType = 1 << i;
1124             assertEquals(projectionType != PROJECTION_TYPE_AUTOMOTIVE,
1125                     (boolean) mService.releaseProjection(projectionType, PACKAGE_NAME));
1126         }
1127 
1128         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
1129     }
1130 
1131     @Test
binderDeath_releasesProjection()1132     public void binderDeath_releasesProjection() throws Exception {
1133         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1134                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1135         requestAllPossibleProjectionTypes();
1136         assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
1137         ArgumentCaptor<IBinder.DeathRecipient> deathRecipientCaptor = ArgumentCaptor.forClass(
1138                 IBinder.DeathRecipient.class);
1139         verify(mBinder, atLeastOnce()).linkToDeath(deathRecipientCaptor.capture(), anyInt());
1140 
1141         // Wipe them out. All of them.
1142         deathRecipientCaptor.getAllValues().forEach(IBinder.DeathRecipient::binderDied);
1143         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
1144     }
1145 
1146     @Test
getActiveProjectionTypes()1147     public void getActiveProjectionTypes() throws Exception {
1148         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
1149         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1150                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1151         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1152         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1153         mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1154         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
1155     }
1156 
1157     @Test
getProjectingPackages()1158     public void getProjectingPackages() throws Exception {
1159         assertTrue(mService.getProjectingPackages(PROJECTION_TYPE_ALL).isEmpty());
1160         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1161                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1162         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1163         assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE).size());
1164         assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_ALL).size());
1165         assertThat(mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE),
1166                 contains(PACKAGE_NAME));
1167         assertThat(mService.getProjectingPackages(PROJECTION_TYPE_ALL), contains(PACKAGE_NAME));
1168         mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1169         assertThat(mService.getProjectingPackages(PROJECTION_TYPE_ALL), empty());
1170     }
1171 
1172     @Test
addOnProjectionStateChangedListener_enforcesReadProjStatePermission()1173     public void addOnProjectionStateChangedListener_enforcesReadProjStatePermission() {
1174         mPermissionEnforcer.revoke(android.Manifest.permission.READ_PROJECTION_STATE);
1175         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
1176 
1177         assertThrows(SecurityException.class, () -> mService.addOnProjectionStateChangedListener(
1178                 listener, PROJECTION_TYPE_ALL));
1179     }
1180 
1181     @Test
addOnProjectionStateChangedListener_callsListenerIfProjectionActive()1182     public void addOnProjectionStateChangedListener_callsListenerIfProjectionActive()
1183             throws Exception {
1184         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1185                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1186         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1187         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
1188 
1189         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
1190         when(listener.asBinder()).thenReturn(mBinder);  // Any binder will do
1191         mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
1192         verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_AUTOMOTIVE),
1193                 eq(List.of(PACKAGE_NAME)));
1194     }
1195 
1196     @Test
removeOnProjectionStateChangedListener_enforcesReadProjStatePermission()1197     public void removeOnProjectionStateChangedListener_enforcesReadProjStatePermission() {
1198         mPermissionEnforcer.revoke(android.Manifest.permission.READ_PROJECTION_STATE);
1199         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
1200 
1201         assertThrows(SecurityException.class, () -> mService.removeOnProjectionStateChangedListener(
1202                 listener));
1203     }
1204 
1205     @Test
removeOnProjectionStateChangedListener()1206     public void removeOnProjectionStateChangedListener() throws Exception {
1207         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
1208         when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
1209         mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
1210 
1211         mService.removeOnProjectionStateChangedListener(listener);
1212         // Now set automotive projection, should not call back.
1213         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1214                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1215         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1216         verify(listener, never()).onProjectionStateChanged(anyInt(), any());
1217     }
1218 
1219     @Test
projectionStateChangedListener_calledWhenStateChanges()1220     public void projectionStateChangedListener_calledWhenStateChanges() throws Exception {
1221         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
1222         when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
1223         mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
1224         verify(listener, atLeastOnce()).asBinder(); // Called twice during register.
1225 
1226         // No calls initially, no projection state set.
1227         verifyNoMoreInteractions(listener);
1228 
1229         // Now set automotive projection, should call back.
1230         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1231                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1232         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1233         verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_AUTOMOTIVE),
1234                 eq(List.of(PACKAGE_NAME)));
1235 
1236         // Subsequent calls that are noops do nothing.
1237         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1238         int unsetProjectionType = 0x0002;
1239         mService.releaseProjection(unsetProjectionType, PACKAGE_NAME);
1240         verifyNoMoreInteractions(listener);
1241 
1242         // Release should call back though.
1243         mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1244         verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_NONE),
1245                 eq(List.of()));
1246 
1247         // But only the first time.
1248         mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1249         verifyNoMoreInteractions(listener);
1250     }
1251 
1252     @Test
projectionStateChangedListener_calledForAnyRelevantStateChange()1253     public void projectionStateChangedListener_calledForAnyRelevantStateChange() throws Exception {
1254         int fakeProjectionType = 0x0002;
1255         int otherFakeProjectionType = 0x0004;
1256         String otherPackageName = "Internet Arms";
1257         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1258                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1259         when(mPackageManager.getPackageUidAsUser(eq(otherPackageName), anyInt()))
1260                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1261         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
1262         when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
1263         IOnProjectionStateChangedListener listener2 = mock(IOnProjectionStateChangedListener.class);
1264         when(listener2.asBinder()).thenReturn(mBinder); // Any binder will do.
1265         mService.addOnProjectionStateChangedListener(listener, fakeProjectionType);
1266         mService.addOnProjectionStateChangedListener(listener2,
1267                 fakeProjectionType | otherFakeProjectionType);
1268         verify(listener, atLeastOnce()).asBinder(); // Called twice during register.
1269         verify(listener2, atLeastOnce()).asBinder(); // Called twice during register.
1270 
1271         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1272         verifyNoMoreInteractions(listener, listener2);
1273 
1274         // fakeProjectionType should trigger both.
1275         mService.requestProjection(mBinder, fakeProjectionType, PACKAGE_NAME);
1276         verify(listener).onProjectionStateChanged(eq(fakeProjectionType),
1277                 eq(List.of(PACKAGE_NAME)));
1278         verify(listener2).onProjectionStateChanged(eq(fakeProjectionType),
1279                 eq(List.of(PACKAGE_NAME)));
1280 
1281         // otherFakeProjectionType should only trigger the second listener.
1282         mService.requestProjection(mBinder, otherFakeProjectionType, otherPackageName);
1283         verifyNoMoreInteractions(listener);
1284         verify(listener2).onProjectionStateChanged(
1285                 eq(fakeProjectionType | otherFakeProjectionType),
1286                 eq(List.of(PACKAGE_NAME, otherPackageName)));
1287 
1288         // Turning off fakeProjectionType should trigger both again.
1289         mService.releaseProjection(fakeProjectionType, PACKAGE_NAME);
1290         verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_NONE), eq(List.of()));
1291         verify(listener2).onProjectionStateChanged(eq(otherFakeProjectionType),
1292                 eq(List.of(otherPackageName)));
1293 
1294         // Turning off otherFakeProjectionType should only trigger the second listener.
1295         mService.releaseProjection(otherFakeProjectionType, otherPackageName);
1296         verifyNoMoreInteractions(listener);
1297         verify(listener2).onProjectionStateChanged(eq(PROJECTION_TYPE_NONE), eq(List.of()));
1298     }
1299 
1300     @Test
projectionStateChangedListener_unregisteredOnDeath()1301     public void projectionStateChangedListener_unregisteredOnDeath() throws Exception {
1302         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
1303         IBinder listenerBinder = mock(IBinder.class);
1304         when(listener.asBinder()).thenReturn(listenerBinder);
1305         mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
1306         ArgumentCaptor<IBinder.DeathRecipient> listenerDeathRecipient = ArgumentCaptor.forClass(
1307                 IBinder.DeathRecipient.class);
1308         verify(listenerBinder).linkToDeath(listenerDeathRecipient.capture(), anyInt());
1309 
1310         // Now kill the binder for the listener. This should remove it from the list of listeners.
1311         listenerDeathRecipient.getValue().binderDied();
1312         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1313                 .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1314         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
1315         verify(listener, never()).onProjectionStateChanged(anyInt(), any());
1316     }
1317 
1318     @Test
enableCarMode_failsForBogusPackageName()1319     public void enableCarMode_failsForBogusPackageName() throws Exception {
1320         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1321             .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
1322 
1323         assertThrows(SecurityException.class, () -> mService.enableCarMode(0, 0, PACKAGE_NAME));
1324         assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
1325     }
1326 
1327     @Test
enableCarMode_shell()1328     public void enableCarMode_shell() throws Exception {
1329         mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
1330                 mTwilightManager, new TestInjector(Process.SHELL_UID));
1331         try {
1332             mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
1333         } catch (SecurityException e) {/* ignore for permission denial */}
1334         mService = mUiManagerService.getService();
1335 
1336         mService.enableCarMode(0, 0, PACKAGE_NAME);
1337         assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
1338     }
1339 
1340     @Test
disableCarMode_failsForBogusPackageName()1341     public void disableCarMode_failsForBogusPackageName() throws Exception {
1342         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1343             .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1344         mService.enableCarMode(0, 0, PACKAGE_NAME);
1345         assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
1346         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1347             .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
1348 
1349         assertThrows(SecurityException.class,
1350             () -> mService.disableCarModeByCallingPackage(0, PACKAGE_NAME));
1351         assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
1352 
1353         // Clean up
1354         when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
1355             .thenReturn(TestInjector.DEFAULT_CALLING_UID);
1356         mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
1357         assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
1358     }
1359 
1360     @Test
disableCarMode_shell()1361     public void disableCarMode_shell() throws Exception {
1362         mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
1363                 mTwilightManager, new TestInjector(Process.SHELL_UID));
1364         try {
1365             mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
1366         } catch (SecurityException e) {/* ignore for permission denial */}
1367         mService = mUiManagerService.getService();
1368 
1369         mService.enableCarMode(0, 0, PACKAGE_NAME);
1370         assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
1371 
1372         mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
1373         assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
1374     }
1375 
1376     @Test
dreamWhenDocked()1377     public void dreamWhenDocked() {
1378         triggerDockIntent();
1379         verifyAndSendResultBroadcast();
1380         verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
1381     }
1382 
1383     @Test
noDreamWhenDocked_keyguardNotShowing_interactive()1384     public void noDreamWhenDocked_keyguardNotShowing_interactive() {
1385         mUiManagerService.setStartDreamImmediatelyOnDock(false);
1386         when(mWindowManager.isKeyguardShowingAndNotOccluded()).thenReturn(false);
1387         when(mPowerManager.isInteractive()).thenReturn(true);
1388 
1389         triggerDockIntent();
1390         verifyAndSendResultBroadcast();
1391         verify(mInjector, never()).startDreamWhenDockedIfAppropriate(mContext);
1392     }
1393 
1394     @Test
dreamWhenDocked_keyguardShowing_interactive()1395     public void dreamWhenDocked_keyguardShowing_interactive() {
1396         mUiManagerService.setStartDreamImmediatelyOnDock(false);
1397         when(mWindowManager.isKeyguardShowingAndNotOccluded()).thenReturn(true);
1398         when(mPowerManager.isInteractive()).thenReturn(false);
1399 
1400         triggerDockIntent();
1401         verifyAndSendResultBroadcast();
1402         verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
1403     }
1404 
1405     @Test
dreamWhenDocked_keyguardNotShowing_notInteractive()1406     public void dreamWhenDocked_keyguardNotShowing_notInteractive() {
1407         mUiManagerService.setStartDreamImmediatelyOnDock(false);
1408         when(mWindowManager.isKeyguardShowingAndNotOccluded()).thenReturn(false);
1409         when(mPowerManager.isInteractive()).thenReturn(false);
1410 
1411         triggerDockIntent();
1412         verifyAndSendResultBroadcast();
1413         verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
1414     }
1415 
1416     @Test
dreamWhenDocked_keyguardShowing_notInteractive()1417     public void dreamWhenDocked_keyguardShowing_notInteractive() {
1418         mUiManagerService.setStartDreamImmediatelyOnDock(false);
1419         when(mWindowManager.isKeyguardShowingAndNotOccluded()).thenReturn(true);
1420         when(mPowerManager.isInteractive()).thenReturn(false);
1421 
1422         triggerDockIntent();
1423         verifyAndSendResultBroadcast();
1424         verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
1425     }
1426 
1427     @Test
dreamWhenDocked_ambientModeSuppressed_suppressionEnabled()1428     public void dreamWhenDocked_ambientModeSuppressed_suppressionEnabled() {
1429         mUiManagerService.setStartDreamImmediatelyOnDock(true);
1430         mUiManagerService.setDreamsDisabledByAmbientModeSuppression(true);
1431 
1432         when(mLocalPowerManager.isAmbientDisplaySuppressed()).thenReturn(true);
1433         triggerDockIntent();
1434         verifyAndSendResultBroadcast();
1435         verify(mInjector, never()).startDreamWhenDockedIfAppropriate(mContext);
1436     }
1437 
1438     @Test
dreamWhenDocked_ambientModeSuppressed_suppressionDisabled()1439     public void dreamWhenDocked_ambientModeSuppressed_suppressionDisabled() {
1440         mUiManagerService.setStartDreamImmediatelyOnDock(true);
1441         mUiManagerService.setDreamsDisabledByAmbientModeSuppression(false);
1442 
1443         when(mLocalPowerManager.isAmbientDisplaySuppressed()).thenReturn(true);
1444         triggerDockIntent();
1445         verifyAndSendResultBroadcast();
1446         verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
1447     }
1448 
1449     @Test
dreamWhenDocked_ambientModeNotSuppressed_suppressionEnabled()1450     public void dreamWhenDocked_ambientModeNotSuppressed_suppressionEnabled() {
1451         mUiManagerService.setStartDreamImmediatelyOnDock(true);
1452         mUiManagerService.setDreamsDisabledByAmbientModeSuppression(true);
1453 
1454         when(mLocalPowerManager.isAmbientDisplaySuppressed()).thenReturn(false);
1455         triggerDockIntent();
1456         verifyAndSendResultBroadcast();
1457         verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
1458     }
1459 
testAttentionModeThemeOverlay(boolean modeNight)1460     private void testAttentionModeThemeOverlay(boolean modeNight) throws RemoteException {
1461         //setup
1462         if (modeNight) {
1463             mService.setNightMode(MODE_NIGHT_YES);
1464             assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
1465         } else {
1466             mService.setNightMode(MODE_NIGHT_NO);
1467             assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
1468         }
1469 
1470         // attention modes with expected night modes
1471         Map<Integer, Boolean> modes = Map.of(
1472                 MODE_ATTENTION_THEME_OVERLAY_OFF, modeNight,
1473                 MODE_ATTENTION_THEME_OVERLAY_DAY, false,
1474                 MODE_ATTENTION_THEME_OVERLAY_NIGHT, true
1475         );
1476 
1477         // test
1478         for (int aMode : modes.keySet()) {
1479             try {
1480                 mService.setAttentionModeThemeOverlay(aMode);
1481 
1482                 int appliedAMode = mService.getAttentionModeThemeOverlay();
1483                 boolean nMode = modes.get(aMode);
1484 
1485                 assertEquals(aMode, appliedAMode);
1486                 assertEquals(isNightModeActivated(), nMode);
1487             } catch (RemoteException e) {
1488                 fail("Error communicating with server: " + e.getMessage());
1489             }
1490         }
1491     }
1492 
1493     @Test
1494     @EnableFlags(Flags.FLAG_MODES_API)
testAttentionModeThemeOverlay_nightModeDisabled()1495     public void testAttentionModeThemeOverlay_nightModeDisabled() throws RemoteException {
1496         testAttentionModeThemeOverlay(false);
1497     }
1498 
1499     @Test
1500     @EnableFlags(Flags.FLAG_MODES_API)
testAttentionModeThemeOverlay_nightModeEnabled()1501     public void testAttentionModeThemeOverlay_nightModeEnabled() throws RemoteException {
1502         testAttentionModeThemeOverlay(true);
1503     }
1504 
triggerDockIntent()1505     private void triggerDockIntent() {
1506         final Intent dockedIntent =
1507                 new Intent(Intent.ACTION_DOCK_EVENT)
1508                         .putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_DESK);
1509         mDockStateChangedCallback.onReceive(mContext, dockedIntent);
1510     }
1511 
verifyAndSendResultBroadcast()1512     private void verifyAndSendResultBroadcast() {
1513         verify(mContext).sendOrderedBroadcastAsUser(
1514                 mOrderedBroadcastIntent.capture(),
1515                 any(UserHandle.class),
1516                 nullable(String.class),
1517                 mOrderedBroadcastReceiver.capture(),
1518                 nullable(Handler.class),
1519                 anyInt(),
1520                 nullable(String.class),
1521                 nullable(Bundle.class));
1522 
1523         mOrderedBroadcastReceiver.getValue().setPendingResult(
1524                 new BroadcastReceiver.PendingResult(
1525                         Activity.RESULT_OK,
1526                         /* resultData= */ "",
1527                         /* resultExtras= */ null,
1528                         /* type= */ 0,
1529                         /* ordered= */ true,
1530                         /* sticky= */ false,
1531                         /* token= */ null,
1532                         /* userId= */ 0,
1533                         /* flags= */ 0));
1534         mOrderedBroadcastReceiver.getValue().onReceive(
1535                 mContext,
1536                 mOrderedBroadcastIntent.getValue());
1537     }
1538 
requestAllPossibleProjectionTypes()1539     private void requestAllPossibleProjectionTypes() throws RemoteException {
1540         for (int i = 0; i < Integer.SIZE; ++i) {
1541             mService.requestProjection(mBinder, 1 << i, PACKAGE_NAME);
1542         }
1543     }
1544 
1545     private static class TestInjector extends UiModeManagerService.Injector {
1546         private static final int DEFAULT_CALLING_UID = 8675309;
1547 
1548         private final int callingUid;
1549 
TestInjector()1550         public TestInjector() {
1551           this(DEFAULT_CALLING_UID);
1552         }
1553 
TestInjector(int callingUid)1554         public TestInjector(int callingUid) {
1555             this.callingUid = callingUid;
1556         }
1557 
1558         @Override
getCallingUid()1559         public int getCallingUid() {
1560             return callingUid;
1561         }
1562 
1563         @Override
startDreamWhenDockedIfAppropriate(Context context)1564         public void startDreamWhenDockedIfAppropriate(Context context) {
1565             // do nothing
1566         }
1567     }
1568 }
1569