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 android.app.AlarmManager; 20 import android.app.IUiModeManager; 21 import android.content.BroadcastReceiver; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.PackageManager; 27 import android.content.res.Configuration; 28 import android.content.res.Resources; 29 import android.os.Handler; 30 import android.os.PowerManager; 31 import android.os.PowerManagerInternal; 32 import android.os.PowerSaveState; 33 import android.os.RemoteException; 34 import android.testing.AndroidTestingRunner; 35 import android.testing.TestableLooper; 36 37 import com.android.server.twilight.TwilightListener; 38 import com.android.server.twilight.TwilightManager; 39 import com.android.server.twilight.TwilightState; 40 import com.android.server.wm.WindowManagerInternal; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.mockito.Mock; 45 46 import java.time.LocalDateTime; 47 import java.time.LocalTime; 48 import java.time.ZoneId; 49 import java.util.function.Consumer; 50 51 import static android.app.UiModeManager.MODE_NIGHT_AUTO; 52 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; 53 import static android.app.UiModeManager.MODE_NIGHT_NO; 54 import static android.app.UiModeManager.MODE_NIGHT_YES; 55 import static junit.framework.TestCase.assertFalse; 56 import static junit.framework.TestCase.assertTrue; 57 import static org.junit.Assert.assertEquals; 58 import static org.mockito.ArgumentMatchers.any; 59 import static org.mockito.ArgumentMatchers.anyInt; 60 import static org.mockito.ArgumentMatchers.anyLong; 61 import static org.mockito.ArgumentMatchers.anyString; 62 import static org.mockito.ArgumentMatchers.eq; 63 import static org.mockito.ArgumentMatchers.notNull; 64 import static org.mockito.BDDMockito.given; 65 import static org.mockito.Mockito.atLeast; 66 import static org.mockito.Mockito.atLeastOnce; 67 import static org.mockito.Mockito.doAnswer; 68 import static org.mockito.Mockito.doReturn; 69 import static org.mockito.Mockito.times; 70 import static org.mockito.Mockito.verify; 71 import static org.mockito.Mockito.when; 72 import static org.mockito.MockitoAnnotations.initMocks; 73 74 @RunWith(AndroidTestingRunner.class) 75 @TestableLooper.RunWithLooper 76 public class UiModeManagerServiceTest extends UiServiceTestCase { 77 private UiModeManagerService mUiManagerService; 78 private IUiModeManager mService; 79 @Mock 80 private ContentResolver mContentResolver; 81 @Mock 82 private WindowManagerInternal mWindowManager; 83 @Mock 84 private Context mContext; 85 @Mock 86 private Resources mResources; 87 @Mock 88 private TwilightManager mTwilightManager; 89 @Mock 90 private PowerManager.WakeLock mWakeLock; 91 @Mock 92 private AlarmManager mAlarmManager; 93 @Mock 94 private PowerManager mPowerManager; 95 @Mock 96 private TwilightState mTwilightState; 97 @Mock 98 PowerManagerInternal mLocalPowerManager; 99 100 private BroadcastReceiver mScreenOffCallback; 101 private BroadcastReceiver mTimeChangedCallback; 102 private AlarmManager.OnAlarmListener mCustomListener; 103 private Consumer<PowerSaveState> mPowerSaveConsumer; 104 private TwilightListener mTwilightListener; 105 106 @Before setUp()107 public void setUp() { 108 initMocks(this); 109 when(mContext.checkCallingOrSelfPermission(anyString())) 110 .thenReturn(PackageManager.PERMISSION_GRANTED); 111 doAnswer(inv -> { 112 mTwilightListener = (TwilightListener) inv.getArgument(0); 113 return null; 114 }).when(mTwilightManager).registerListener(any(), any()); 115 doAnswer(inv -> { 116 mPowerSaveConsumer = (Consumer<PowerSaveState>) inv.getArgument(1); 117 return null; 118 }).when(mLocalPowerManager).registerLowPowerModeObserver(anyInt(), any()); 119 when(mLocalPowerManager.getLowPowerState(anyInt())) 120 .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(false).build()); 121 when(mContext.getResources()).thenReturn(mResources); 122 when(mContext.getContentResolver()).thenReturn(mContentResolver); 123 when(mPowerManager.isInteractive()).thenReturn(true); 124 when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock); 125 when(mTwilightManager.getLastTwilightState()).thenReturn(mTwilightState); 126 when(mTwilightState.isNight()).thenReturn(true); 127 when(mContext.registerReceiver(notNull(), notNull())).then(inv -> { 128 IntentFilter filter = inv.getArgument(1); 129 if (filter.hasAction(Intent.ACTION_TIMEZONE_CHANGED)) { 130 mTimeChangedCallback = inv.getArgument(0); 131 } 132 if (filter.hasAction(Intent.ACTION_SCREEN_OFF)) { 133 mScreenOffCallback = inv.getArgument(0); 134 } 135 return null; 136 }); 137 doAnswer(inv -> { 138 mCustomListener = inv.getArgument(3); 139 return null; 140 }).when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(), 141 any(AlarmManager.OnAlarmListener.class), any(Handler.class)); 142 143 doAnswer(inv -> { 144 mCustomListener = () -> {}; 145 return null; 146 }).when(mAlarmManager).cancel(eq(mCustomListener)); 147 when(mContext.getSystemService(eq(Context.POWER_SERVICE))) 148 .thenReturn(mPowerManager); 149 when(mContext.getSystemService(eq(Context.ALARM_SERVICE))) 150 .thenReturn(mAlarmManager); 151 addLocalService(WindowManagerInternal.class, mWindowManager); 152 addLocalService(PowerManagerInternal.class, mLocalPowerManager); 153 addLocalService(TwilightManager.class, mTwilightManager); 154 155 mUiManagerService = new UiModeManagerService(mContext, true, 156 mTwilightManager); 157 try { 158 mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); 159 } catch (SecurityException e) {/* ignore for permission denial */} 160 mService = mUiManagerService.getService(); 161 } 162 addLocalService(Class<T> clazz, T service)163 private <T> void addLocalService(Class<T> clazz, T service) { 164 LocalServices.removeServiceForTest(clazz); 165 LocalServices.addService(clazz, service); 166 } 167 168 @Test setNightMoveActivated_overridesFunctionCorrectly()169 public void setNightMoveActivated_overridesFunctionCorrectly() throws RemoteException { 170 // set up 171 when(mPowerManager.isInteractive()).thenReturn(false); 172 mService.setNightMode(MODE_NIGHT_NO); 173 assertFalse(mUiManagerService.getConfiguration().isNightModeActive()); 174 175 // assume it is day time 176 doReturn(false).when(mTwilightState).isNight(); 177 178 // set mode to auto 179 mService.setNightMode(MODE_NIGHT_AUTO); 180 181 // set night mode on overriding current config 182 mService.setNightModeActivated(true); 183 184 assertTrue(mUiManagerService.getConfiguration().isNightModeActive()); 185 186 // now it is night time 187 doReturn(true).when(mTwilightState).isNight(); 188 mTwilightListener.onTwilightStateChanged(mTwilightState); 189 190 assertTrue(mUiManagerService.getConfiguration().isNightModeActive()); 191 192 // now it is next day mid day 193 doReturn(false).when(mTwilightState).isNight(); 194 mTwilightListener.onTwilightStateChanged(mTwilightState); 195 196 assertFalse(mUiManagerService.getConfiguration().isNightModeActive()); 197 } 198 199 @Test setAutoMode_screenOffRegistered()200 public void setAutoMode_screenOffRegistered() throws RemoteException { 201 try { 202 mService.setNightMode(MODE_NIGHT_NO); 203 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 204 mService.setNightMode(MODE_NIGHT_AUTO); 205 verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class), any()); 206 } 207 208 @Test setAutoMode_screenOffUnRegistered()209 public void setAutoMode_screenOffUnRegistered() throws RemoteException { 210 try { 211 mService.setNightMode(MODE_NIGHT_AUTO); 212 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 213 try { 214 mService.setNightMode(MODE_NIGHT_NO); 215 } catch (SecurityException e) { /*we should ignore this update config exception*/ } 216 given(mContext.registerReceiver(any(), any())).willThrow(SecurityException.class); 217 verify(mContext, atLeastOnce()).unregisterReceiver(any(BroadcastReceiver.class)); 218 } 219 220 @Test setNightModeActivated_fromNoToYesAndBAck()221 public void setNightModeActivated_fromNoToYesAndBAck() throws RemoteException { 222 mService.setNightMode(MODE_NIGHT_NO); 223 mService.setNightModeActivated(true); 224 assertTrue(isNightModeActivated()); 225 mService.setNightModeActivated(false); 226 assertFalse(isNightModeActivated()); 227 } 228 229 @Test autoNightModeSwitch_batterySaverOn()230 public void autoNightModeSwitch_batterySaverOn() throws RemoteException { 231 mService.setNightMode(MODE_NIGHT_NO); 232 when(mTwilightState.isNight()).thenReturn(false); 233 mService.setNightMode(MODE_NIGHT_AUTO); 234 235 // night NO 236 assertFalse(isNightModeActivated()); 237 238 mPowerSaveConsumer.accept( 239 new PowerSaveState.Builder().setBatterySaverEnabled(true).build()); 240 241 // night YES 242 assertTrue(isNightModeActivated()); 243 } 244 245 @Test setAutoMode_clearCache()246 public void setAutoMode_clearCache() throws RemoteException { 247 try { 248 mService.setNightMode(MODE_NIGHT_AUTO); 249 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 250 try { 251 mService.setNightMode(MODE_NIGHT_NO); 252 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 253 verify(mWindowManager).clearSnapshotCache(); 254 } 255 256 @Test setNightModeActive_fromNightModeYesToNoWhenFalse()257 public void setNightModeActive_fromNightModeYesToNoWhenFalse() throws RemoteException { 258 try { 259 mService.setNightMode(MODE_NIGHT_YES); 260 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 261 try { 262 mService.setNightModeActivated(false); 263 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 264 assertEquals(MODE_NIGHT_NO, mService.getNightMode()); 265 } 266 267 @Test setNightModeActive_fromNightModeNoToYesWhenTrue()268 public void setNightModeActive_fromNightModeNoToYesWhenTrue() throws RemoteException { 269 try { 270 mService.setNightMode(MODE_NIGHT_NO); 271 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 272 try { 273 mService.setNightModeActivated(true); 274 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 275 assertEquals(MODE_NIGHT_YES, mService.getNightMode()); 276 } 277 278 @Test setNightModeActive_autoNightModeNoChanges()279 public void setNightModeActive_autoNightModeNoChanges() throws RemoteException { 280 try { 281 mService.setNightMode(MODE_NIGHT_AUTO); 282 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 283 try { 284 mService.setNightModeActivated(true); 285 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 286 assertEquals(MODE_NIGHT_AUTO, mService.getNightMode()); 287 } 288 289 @Test isNightModeActive_nightModeYes()290 public void isNightModeActive_nightModeYes() throws RemoteException { 291 try { 292 mService.setNightMode(MODE_NIGHT_YES); 293 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 294 assertTrue(isNightModeActivated()); 295 } 296 297 @Test isNightModeActive_nightModeNo()298 public void isNightModeActive_nightModeNo() throws RemoteException { 299 try { 300 mService.setNightMode(MODE_NIGHT_NO); 301 } catch (SecurityException e) { /* we should ignore this update config exception*/ } 302 assertFalse(isNightModeActivated()); 303 } 304 305 @Test customTime_darkThemeOn()306 public void customTime_darkThemeOn() throws RemoteException { 307 LocalTime now = LocalTime.now(); 308 mService.setNightMode(MODE_NIGHT_NO); 309 mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000); 310 mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000); 311 mService.setNightMode(MODE_NIGHT_CUSTOM); 312 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 313 assertTrue(isNightModeActivated()); 314 } 315 316 @Test customTime_darkThemeOff()317 public void customTime_darkThemeOff() throws RemoteException { 318 LocalTime now = LocalTime.now(); 319 mService.setNightMode(MODE_NIGHT_YES); 320 mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000); 321 mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000); 322 mService.setNightMode(MODE_NIGHT_CUSTOM); 323 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 324 assertFalse(isNightModeActivated()); 325 } 326 327 @Test customTime_darkThemeOff_afterStartEnd()328 public void customTime_darkThemeOff_afterStartEnd() throws RemoteException { 329 LocalTime now = LocalTime.now(); 330 mService.setNightMode(MODE_NIGHT_YES); 331 mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000); 332 mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000); 333 mService.setNightMode(MODE_NIGHT_CUSTOM); 334 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 335 assertFalse(isNightModeActivated()); 336 } 337 338 @Test customTime_darkThemeOn_afterStartEnd()339 public void customTime_darkThemeOn_afterStartEnd() throws RemoteException { 340 LocalTime now = LocalTime.now(); 341 mService.setNightMode(MODE_NIGHT_YES); 342 mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000); 343 mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000); 344 mService.setNightMode(MODE_NIGHT_CUSTOM); 345 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 346 assertFalse(isNightModeActivated()); 347 } 348 349 350 351 @Test customTime_darkThemeOn_beforeStartEnd()352 public void customTime_darkThemeOn_beforeStartEnd() throws RemoteException { 353 LocalTime now = LocalTime.now(); 354 mService.setNightMode(MODE_NIGHT_YES); 355 mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000); 356 mService.setCustomNightModeEnd(now.minusHours(2L).toNanoOfDay() / 1000); 357 mService.setNightMode(MODE_NIGHT_CUSTOM); 358 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 359 assertTrue(isNightModeActivated()); 360 } 361 362 @Test customTime_darkThemeOff_beforeStartEnd()363 public void customTime_darkThemeOff_beforeStartEnd() throws RemoteException { 364 LocalTime now = LocalTime.now(); 365 mService.setNightMode(MODE_NIGHT_YES); 366 mService.setCustomNightModeStart(now.minusHours(2L).toNanoOfDay() / 1000); 367 mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000); 368 mService.setNightMode(MODE_NIGHT_CUSTOM); 369 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 370 assertFalse(isNightModeActivated()); 371 } 372 373 @Test customTIme_customAlarmSetWhenScreenTimeChanges()374 public void customTIme_customAlarmSetWhenScreenTimeChanges() throws RemoteException { 375 when(mPowerManager.isInteractive()).thenReturn(false); 376 mService.setNightMode(MODE_NIGHT_CUSTOM); 377 verify(mAlarmManager, times(1)) 378 .setExact(anyInt(), anyLong(), anyString(), any(), any()); 379 mTimeChangedCallback.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED)); 380 verify(mAlarmManager, atLeast(2)) 381 .setExact(anyInt(), anyLong(), anyString(), any(), any()); 382 } 383 384 @Test customTime_alarmSetInTheFutureWhenOn()385 public void customTime_alarmSetInTheFutureWhenOn() throws RemoteException { 386 LocalDateTime now = LocalDateTime.now(); 387 when(mPowerManager.isInteractive()).thenReturn(false); 388 mService.setNightMode(MODE_NIGHT_YES); 389 mService.setCustomNightModeStart(now.toLocalTime().minusHours(1L).toNanoOfDay() / 1000); 390 mService.setCustomNightModeEnd(now.toLocalTime().plusHours(1L).toNanoOfDay() / 1000); 391 LocalDateTime next = now.plusHours(1L); 392 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); 393 mService.setNightMode(MODE_NIGHT_CUSTOM); 394 verify(mAlarmManager) 395 .setExact(anyInt(), eq(millis), anyString(), any(), any()); 396 } 397 398 @Test customTime_appliesImmediatelyWhenScreenOff()399 public void customTime_appliesImmediatelyWhenScreenOff() throws RemoteException { 400 when(mPowerManager.isInteractive()).thenReturn(false); 401 LocalTime now = LocalTime.now(); 402 mService.setNightMode(MODE_NIGHT_NO); 403 mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000); 404 mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000); 405 mService.setNightMode(MODE_NIGHT_CUSTOM); 406 assertTrue(isNightModeActivated()); 407 } 408 409 @Test customTime_appliesOnlyWhenScreenOff()410 public void customTime_appliesOnlyWhenScreenOff() throws RemoteException { 411 LocalTime now = LocalTime.now(); 412 mService.setNightMode(MODE_NIGHT_NO); 413 mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000); 414 mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000); 415 mService.setNightMode(MODE_NIGHT_CUSTOM); 416 assertFalse(isNightModeActivated()); 417 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 418 assertTrue(isNightModeActivated()); 419 } 420 421 @Test nightAuto_appliesOnlyWhenScreenOff()422 public void nightAuto_appliesOnlyWhenScreenOff() throws RemoteException { 423 when(mTwilightState.isNight()).thenReturn(true); 424 mService.setNightMode(MODE_NIGHT_NO); 425 mService.setNightMode(MODE_NIGHT_AUTO); 426 assertFalse(isNightModeActivated()); 427 mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 428 assertTrue(isNightModeActivated()); 429 } 430 isNightModeActivated()431 private boolean isNightModeActivated() { 432 return (mUiManagerService.getConfiguration().uiMode 433 & Configuration.UI_MODE_NIGHT_YES) != 0; 434 } 435 } 436