1 /* 2 * Copyright (C) 2008 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.annotation.IntRange; 20 import android.annotation.Nullable; 21 import android.app.Activity; 22 import android.app.ActivityManager; 23 import android.app.ActivityTaskManager; 24 import android.app.AlarmManager; 25 import android.app.IUiModeManager; 26 import android.app.Notification; 27 import android.app.NotificationManager; 28 import android.app.PendingIntent; 29 import android.app.StatusBarManager; 30 import android.app.UiModeManager; 31 import android.content.BroadcastReceiver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.pm.PackageManager; 36 import android.content.res.Configuration; 37 import android.content.res.Resources; 38 import android.database.ContentObserver; 39 import android.net.Uri; 40 import android.os.BatteryManager; 41 import android.os.Binder; 42 import android.os.Handler; 43 import android.os.PowerManager; 44 import android.os.PowerManager.ServiceType; 45 import android.os.PowerManagerInternal; 46 import android.os.Process; 47 import android.os.RemoteException; 48 import android.os.ResultReceiver; 49 import android.os.ServiceManager; 50 import android.os.ShellCallback; 51 import android.os.ShellCommand; 52 import android.os.SystemProperties; 53 import android.os.UserHandle; 54 import android.provider.Settings.Secure; 55 import android.service.dreams.Sandman; 56 import android.service.vr.IVrManager; 57 import android.service.vr.IVrStateCallbacks; 58 import android.util.ArraySet; 59 import android.util.Slog; 60 61 import com.android.internal.R; 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.internal.app.DisableCarModeActivity; 64 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 65 import com.android.internal.notification.SystemNotificationChannels; 66 import com.android.internal.util.DumpUtils; 67 import com.android.server.twilight.TwilightListener; 68 import com.android.server.twilight.TwilightManager; 69 import com.android.server.twilight.TwilightState; 70 import com.android.server.wm.WindowManagerInternal; 71 72 import java.io.FileDescriptor; 73 import java.io.PrintWriter; 74 import java.time.DateTimeException; 75 import java.time.LocalDateTime; 76 import java.time.LocalTime; 77 import java.time.ZoneId; 78 import java.util.Arrays; 79 import java.util.HashMap; 80 import java.util.List; 81 import java.util.Map; 82 import java.util.Set; 83 84 import static android.app.UiModeManager.MODE_NIGHT_AUTO; 85 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; 86 import static android.app.UiModeManager.MODE_NIGHT_YES; 87 import static android.os.UserHandle.USER_SYSTEM; 88 import static android.util.TimeUtils.isTimeBetween; 89 90 final class UiModeManagerService extends SystemService { 91 private static final String TAG = UiModeManager.class.getSimpleName(); 92 private static final boolean LOG = false; 93 94 // Enable launching of applications when entering the dock. 95 private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; 96 private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme"; 97 98 final Object mLock = new Object(); 99 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 100 101 private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 102 private int mNightMode = UiModeManager.MODE_NIGHT_NO; 103 private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0); 104 private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0); 105 private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME; 106 private LocalTime mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME; 107 108 private Map<Integer, String> mCarModePackagePriority = new HashMap<>(); 109 private boolean mCarModeEnabled = false; 110 private boolean mCharging = false; 111 private boolean mPowerSave = false; 112 // Do not change configuration now. wait until screen turns off. 113 // This prevents jank and activity restart when the user 114 // is actively using the device 115 private boolean mWaitForScreenOff = false; 116 private int mDefaultUiModeType; 117 private boolean mCarModeKeepsScreenOn; 118 private boolean mDeskModeKeepsScreenOn; 119 private boolean mTelevision; 120 private boolean mCar; 121 private boolean mWatch; 122 private boolean mVrHeadset; 123 private boolean mComputedNightMode; 124 private int mCarModeEnableFlags; 125 private boolean mSetupWizardComplete; 126 127 // flag set by resource, whether to enable Car dock launch when starting car mode. 128 private boolean mEnableCarDockLaunch = true; 129 // flag set by resource, whether to lock UI mode to the default one or not. 130 private boolean mUiModeLocked = false; 131 // flag set by resource, whether to night mode change for normal all or not. 132 private boolean mNightModeLocked = false; 133 134 int mCurUiMode = 0; 135 private int mSetUiMode = 0; 136 private boolean mHoldingConfiguration = false; 137 138 private Configuration mConfiguration = new Configuration(); 139 boolean mSystemReady; 140 141 private final Handler mHandler = new Handler(); 142 143 private TwilightManager mTwilightManager; 144 private NotificationManager mNotificationManager; 145 private StatusBarManager mStatusBarManager; 146 private WindowManagerInternal mWindowManager; 147 private AlarmManager mAlarmManager; 148 private PowerManager mPowerManager; 149 150 // In automatic scheduling, the user is able 151 // to override the computed night mode until the two match 152 // Example: Activate dark mode in the day time until sunrise the next day 153 private boolean mOverrideNightModeOn; 154 private boolean mOverrideNightModeOff; 155 private int mOverrideNightModeUser = USER_SYSTEM; 156 157 private PowerManager.WakeLock mWakeLock; 158 159 private final LocalService mLocalService = new LocalService(); 160 private PowerManagerInternal mLocalPowerManager; 161 UiModeManagerService(Context context)162 public UiModeManagerService(Context context) { 163 super(context); 164 mConfiguration.setToDefaults(); 165 } 166 167 @VisibleForTesting UiModeManagerService(Context context, boolean setupWizardComplete, TwilightManager tm)168 protected UiModeManagerService(Context context, boolean setupWizardComplete, 169 TwilightManager tm) { 170 this(context); 171 mSetupWizardComplete = setupWizardComplete; 172 mTwilightManager = tm; 173 } 174 buildHomeIntent(String category)175 private static Intent buildHomeIntent(String category) { 176 Intent intent = new Intent(Intent.ACTION_MAIN); 177 intent.addCategory(category); 178 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 179 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 180 return intent; 181 } 182 183 // The broadcast receiver which receives the result of the ordered broadcast sent when 184 // the dock state changes. The original ordered broadcast is sent with an initial result 185 // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., 186 // to RESULT_CANCELED, then the intent to start a dock app will not be sent. 187 private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 188 @Override 189 public void onReceive(Context context, Intent intent) { 190 if (getResultCode() != Activity.RESULT_OK) { 191 if (LOG) { 192 Slog.v(TAG, "Handling broadcast result for action " + intent.getAction() 193 + ": canceled: " + getResultCode()); 194 } 195 return; 196 } 197 198 final int enableFlags = intent.getIntExtra("enableFlags", 0); 199 final int disableFlags = intent.getIntExtra("disableFlags", 0); 200 synchronized (mLock) { 201 updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags); 202 } 203 } 204 }; 205 206 private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() { 207 @Override 208 public void onReceive(Context context, Intent intent) { 209 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 210 Intent.EXTRA_DOCK_STATE_UNDOCKED); 211 updateDockState(state); 212 } 213 }; 214 215 private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { 216 @Override 217 public void onReceive(Context context, Intent intent) { 218 switch (intent.getAction()) { 219 case Intent.ACTION_BATTERY_CHANGED: 220 mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0; 221 break; 222 } 223 synchronized (mLock) { 224 if (mSystemReady) { 225 updateLocked(0, 0); 226 } 227 } 228 } 229 }; 230 231 private final TwilightListener mTwilightListener = new TwilightListener() { 232 @Override 233 public void onTwilightStateChanged(@Nullable TwilightState state) { 234 synchronized (mLock) { 235 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) { 236 if (shouldApplyAutomaticChangesImmediately()) { 237 updateLocked(0, 0); 238 } else { 239 registerScreenOffEventLocked(); 240 } 241 } 242 } 243 } 244 }; 245 246 /** 247 * DO NOT USE DIRECTLY 248 * see register registerScreenOffEvent and unregisterScreenOffEvent 249 */ 250 private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() { 251 @Override 252 public void onReceive(Context context, Intent intent) { 253 synchronized (mLock) { 254 // must unregister first before updating 255 unregisterScreenOffEventLocked(); 256 updateLocked(0, 0); 257 } 258 } 259 }; 260 261 private final BroadcastReceiver mOnTimeChangedHandler = new BroadcastReceiver() { 262 @Override 263 public void onReceive(Context context, Intent intent) { 264 synchronized (mLock) { 265 updateCustomTimeLocked(); 266 } 267 } 268 }; 269 270 private final AlarmManager.OnAlarmListener mCustomTimeListener = () -> { 271 synchronized (mLock) { 272 updateCustomTimeLocked(); 273 } 274 }; 275 276 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 277 @Override 278 public void onVrStateChanged(boolean enabled) { 279 synchronized (mLock) { 280 mVrHeadset = enabled; 281 if (mSystemReady) { 282 updateLocked(0, 0); 283 } 284 } 285 } 286 }; 287 288 private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) { 289 @Override 290 public void onChange(boolean selfChange, Uri uri) { 291 synchronized (mLock) { 292 // setup wizard is done now so we can unblock 293 if (setupWizardCompleteForCurrentUser() && !selfChange) { 294 mSetupWizardComplete = true; 295 getContext().getContentResolver() 296 .unregisterContentObserver(mSetupWizardObserver); 297 // update night mode 298 Context context = getContext(); 299 updateNightModeFromSettingsLocked(context, context.getResources(), 300 UserHandle.getCallingUserId()); 301 updateLocked(0, 0); 302 } 303 } 304 } 305 }; 306 307 private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) { 308 @Override 309 public void onChange(boolean selfChange, Uri uri) { 310 updateSystemProperties(); 311 } 312 }; 313 updateSystemProperties()314 private void updateSystemProperties() { 315 int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, 316 mNightMode, 0); 317 if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) { 318 mode = MODE_NIGHT_YES; 319 } 320 SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode)); 321 } 322 323 @Override onSwitchUser(int userHandle)324 public void onSwitchUser(int userHandle) { 325 super.onSwitchUser(userHandle); 326 getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); 327 verifySetupWizardCompleted(); 328 } 329 330 @Override onBootPhase(int phase)331 public void onBootPhase(int phase) { 332 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 333 synchronized (mLock) { 334 final Context context = getContext(); 335 mSystemReady = true; 336 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 337 mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); 338 mWindowManager = LocalServices.getService(WindowManagerInternal.class); 339 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); 340 TwilightManager twilightManager = getLocalService(TwilightManager.class); 341 if (twilightManager != null) mTwilightManager = twilightManager; 342 mLocalPowerManager = 343 LocalServices.getService(PowerManagerInternal.class); 344 initPowerSave(); 345 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR; 346 registerVrStateListener(); 347 // register listeners 348 context.getContentResolver() 349 .registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE), 350 false, mDarkThemeObserver, 0); 351 context.registerReceiver(mDockModeReceiver, 352 new IntentFilter(Intent.ACTION_DOCK_EVENT)); 353 IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 354 context.registerReceiver(mBatteryReceiver, batteryFilter); 355 IntentFilter filter = new IntentFilter(); 356 filter.addAction(Intent.ACTION_USER_SWITCHED); 357 context.registerReceiver(mSettingsRestored, 358 new IntentFilter(Intent.ACTION_SETTING_RESTORED)); 359 context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler); 360 updateConfigurationLocked(); 361 applyConfigurationExternallyLocked(); 362 } 363 } 364 } 365 366 @Override onStart()367 public void onStart() { 368 final Context context = getContext(); 369 // If setup isn't complete for this user listen for completion so we can unblock 370 // being able to send a night mode configuration change event 371 verifySetupWizardCompleted(); 372 373 final Resources res = context.getResources(); 374 mNightMode = res.getInteger( 375 com.android.internal.R.integer.config_defaultNightMode); 376 mDefaultUiModeType = res.getInteger( 377 com.android.internal.R.integer.config_defaultUiModeType); 378 mCarModeKeepsScreenOn = (res.getInteger( 379 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1); 380 mDeskModeKeepsScreenOn = (res.getInteger( 381 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1); 382 mEnableCarDockLaunch = res.getBoolean( 383 com.android.internal.R.bool.config_enableCarDockHomeLaunch); 384 mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode); 385 mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode); 386 final PackageManager pm = context.getPackageManager(); 387 mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION) 388 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK); 389 mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); 390 mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH); 391 392 // Update the initial, static configurations. 393 SystemServerInitThreadPool.submit(() -> { 394 synchronized (mLock) { 395 TwilightManager twilightManager = getLocalService(TwilightManager.class); 396 if (twilightManager != null) mTwilightManager = twilightManager; 397 updateNightModeFromSettingsLocked(context, res, UserHandle.getCallingUserId()); 398 updateSystemProperties(); 399 } 400 401 }, TAG + ".onStart"); 402 publishBinderService(Context.UI_MODE_SERVICE, mService); 403 publishLocalService(UiModeManagerInternal.class, mLocalService); 404 } 405 406 private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() { 407 @Override 408 public void onReceive(Context context, Intent intent) { 409 List<String> settings = Arrays.asList( 410 Secure.UI_NIGHT_MODE, Secure.DARK_THEME_CUSTOM_START_TIME, 411 Secure.DARK_THEME_CUSTOM_END_TIME); 412 if (settings.contains(intent.getExtras().getCharSequence(Intent.EXTRA_SETTING_NAME))) { 413 synchronized (mLock) { 414 updateNightModeFromSettingsLocked(context, context.getResources(), 415 UserHandle.getCallingUserId()); 416 updateConfigurationLocked(); 417 } 418 } 419 } 420 }; 421 initPowerSave()422 private void initPowerSave() { 423 mPowerSave = 424 mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE) 425 .batterySaverEnabled; 426 mLocalPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE, state -> { 427 synchronized (mLock) { 428 if (mPowerSave == state.batterySaverEnabled) { 429 return; 430 } 431 mPowerSave = state.batterySaverEnabled; 432 if (mSystemReady) { 433 updateLocked(0, 0); 434 } 435 } 436 }); 437 } 438 439 @VisibleForTesting getService()440 protected IUiModeManager getService() { 441 return mService; 442 } 443 444 @VisibleForTesting getConfiguration()445 protected Configuration getConfiguration() { 446 return mConfiguration; 447 } 448 449 // Records whether setup wizard has happened or not and adds an observer for this user if not. verifySetupWizardCompleted()450 private void verifySetupWizardCompleted() { 451 final Context context = getContext(); 452 final int userId = UserHandle.getCallingUserId(); 453 if (!setupWizardCompleteForCurrentUser()) { 454 mSetupWizardComplete = false; 455 context.getContentResolver().registerContentObserver( 456 Secure.getUriFor( 457 Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId); 458 } else { 459 mSetupWizardComplete = true; 460 } 461 } 462 setupWizardCompleteForCurrentUser()463 private boolean setupWizardCompleteForCurrentUser() { 464 return Secure.getIntForUser(getContext().getContentResolver(), 465 Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1; 466 } 467 updateCustomTimeLocked()468 private void updateCustomTimeLocked() { 469 if (mNightMode != MODE_NIGHT_CUSTOM) return; 470 if (shouldApplyAutomaticChangesImmediately()) { 471 updateLocked(0, 0); 472 } else { 473 registerScreenOffEventLocked(); 474 } 475 scheduleNextCustomTimeListener(); 476 } 477 478 /** 479 * Updates the night mode setting in Settings.Global and returns if the value was successfully 480 * changed. 481 * 482 * @param context A valid context 483 * @param res A valid resource object 484 * @param userId The user to update the setting for 485 * @return True if the new value is different from the old value. False otherwise. 486 */ updateNightModeFromSettingsLocked(Context context, Resources res, int userId)487 private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) { 488 int oldNightMode = mNightMode; 489 if (mSetupWizardComplete) { 490 mNightMode = Secure.getIntForUser(context.getContentResolver(), 491 Secure.UI_NIGHT_MODE, mNightMode, userId); 492 mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), 493 Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0; 494 mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(), 495 Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0; 496 mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay( 497 Secure.getLongForUser(context.getContentResolver(), 498 Secure.DARK_THEME_CUSTOM_START_TIME, 499 DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000); 500 mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay( 501 Secure.getLongForUser(context.getContentResolver(), 502 Secure.DARK_THEME_CUSTOM_END_TIME, 503 DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000); 504 } 505 506 return oldNightMode != mNightMode; 507 } 508 toMilliSeconds(LocalTime t)509 private static long toMilliSeconds(LocalTime t) { 510 return t.toNanoOfDay() / 1000; 511 } 512 fromMilliseconds(long t)513 private static LocalTime fromMilliseconds(long t) { 514 return LocalTime.ofNanoOfDay(t * 1000); 515 } 516 registerScreenOffEventLocked()517 private void registerScreenOffEventLocked() { 518 if (mPowerSave) return; 519 mWaitForScreenOff = true; 520 final IntentFilter intentFilter = 521 new IntentFilter(Intent.ACTION_SCREEN_OFF); 522 getContext().registerReceiver(mOnScreenOffHandler, intentFilter); 523 } 524 cancelCustomAlarm()525 private void cancelCustomAlarm() { 526 mAlarmManager.cancel(mCustomTimeListener); 527 } 528 unregisterScreenOffEventLocked()529 private void unregisterScreenOffEventLocked() { 530 mWaitForScreenOff = false; 531 try { 532 getContext().unregisterReceiver(mOnScreenOffHandler); 533 } catch (IllegalArgumentException e) { 534 // we ignore this exception if the receiver is unregistered already. 535 } 536 } 537 registerTimeChangeEvent()538 private void registerTimeChangeEvent() { 539 final IntentFilter intentFilter = 540 new IntentFilter(Intent.ACTION_TIME_CHANGED); 541 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 542 getContext().registerReceiver(mOnTimeChangedHandler, intentFilter); 543 } 544 unregisterTimeChangeEvent()545 private void unregisterTimeChangeEvent() { 546 try { 547 getContext().unregisterReceiver(mOnTimeChangedHandler); 548 } catch (IllegalArgumentException e) { 549 // we ignore this exception if the receiver is unregistered already. 550 } 551 } 552 553 private final IUiModeManager.Stub mService = new IUiModeManager.Stub() { 554 @Override 555 public void enableCarMode(@UiModeManager.EnableCarMode int flags, 556 @IntRange(from = 0) int priority, String callingPackage) { 557 if (isUiModeLocked()) { 558 Slog.e(TAG, "enableCarMode while UI mode is locked"); 559 return; 560 } 561 562 if (priority != UiModeManager.DEFAULT_PRIORITY 563 && getContext().checkCallingOrSelfPermission( 564 android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) 565 != PackageManager.PERMISSION_GRANTED) { 566 throw new SecurityException("Enabling car mode with a priority requires " 567 + "permission ENTER_CAR_MODE_PRIORITIZED"); 568 } 569 570 final long ident = Binder.clearCallingIdentity(); 571 try { 572 synchronized (mLock) { 573 setCarModeLocked(true, flags, priority, callingPackage); 574 if (mSystemReady) { 575 updateLocked(flags, 0); 576 } 577 } 578 } finally { 579 Binder.restoreCallingIdentity(ident); 580 } 581 } 582 583 /** 584 * This method is only kept around for the time being; the AIDL has an UnsupportedAppUsage 585 * tag which means this method is technically considered part of the greylist "API". 586 * @param flags 587 */ 588 @Override 589 public void disableCarMode(@UiModeManager.DisableCarMode int flags) { 590 disableCarModeByCallingPackage(flags, null /* callingPackage */); 591 } 592 593 /** 594 * Handles requests to disable car mode. 595 * @param flags Disable car mode flags 596 * @param callingPackage 597 */ 598 @Override 599 public void disableCarModeByCallingPackage(@UiModeManager.DisableCarMode int flags, 600 String callingPackage) { 601 if (isUiModeLocked()) { 602 Slog.e(TAG, "disableCarMode while UI mode is locked"); 603 return; 604 } 605 606 // If the caller is the system, we will allow the DISABLE_CAR_MODE_ALL_PRIORITIES car 607 // mode flag to be specified; this is so that the user can disable car mode at all 608 // priorities using the persistent notification. 609 boolean isSystemCaller = Binder.getCallingUid() == Process.SYSTEM_UID; 610 final int carModeFlags = 611 isSystemCaller ? flags : flags & ~UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES; 612 613 final long ident = Binder.clearCallingIdentity(); 614 try { 615 synchronized (mLock) { 616 // Determine if the caller has enabled car mode at a priority other than the 617 // default one. If they have, then attempt to disable at that priority. 618 int priority = mCarModePackagePriority.entrySet() 619 .stream() 620 .filter(e -> e.getValue().equals(callingPackage)) 621 .findFirst() 622 .map(Map.Entry::getKey) 623 .orElse(UiModeManager.DEFAULT_PRIORITY); 624 625 setCarModeLocked(false, carModeFlags, priority, callingPackage); 626 if (mSystemReady) { 627 updateLocked(0, flags); 628 } 629 } 630 } finally { 631 Binder.restoreCallingIdentity(ident); 632 } 633 } 634 635 @Override 636 public int getCurrentModeType() { 637 final long ident = Binder.clearCallingIdentity(); 638 try { 639 synchronized (mLock) { 640 return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; 641 } 642 } finally { 643 Binder.restoreCallingIdentity(ident); 644 } 645 } 646 647 @Override 648 public void setNightMode(int mode) { 649 if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( 650 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 651 != PackageManager.PERMISSION_GRANTED)) { 652 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); 653 return; 654 } 655 switch (mode) { 656 case UiModeManager.MODE_NIGHT_NO: 657 case UiModeManager.MODE_NIGHT_YES: 658 case MODE_NIGHT_AUTO: 659 case MODE_NIGHT_CUSTOM: 660 break; 661 default: 662 throw new IllegalArgumentException("Unknown mode: " + mode); 663 } 664 665 final int user = UserHandle.getCallingUserId(); 666 final long ident = Binder.clearCallingIdentity(); 667 try { 668 synchronized (mLock) { 669 if (mNightMode != mode) { 670 if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) { 671 unregisterScreenOffEventLocked(); 672 cancelCustomAlarm(); 673 } 674 675 mNightMode = mode; 676 resetNightModeOverrideLocked(); 677 persistNightMode(user); 678 // on screen off will update configuration instead 679 if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM) 680 || shouldApplyAutomaticChangesImmediately()) { 681 unregisterScreenOffEventLocked(); 682 updateLocked(0, 0); 683 } else { 684 registerScreenOffEventLocked(); 685 } 686 } 687 } 688 } finally { 689 Binder.restoreCallingIdentity(ident); 690 } 691 } 692 693 @Override 694 public int getNightMode() { 695 synchronized (mLock) { 696 return mNightMode; 697 } 698 } 699 700 @Override 701 public boolean isUiModeLocked() { 702 synchronized (mLock) { 703 return mUiModeLocked; 704 } 705 } 706 707 @Override 708 public boolean isNightModeLocked() { 709 synchronized (mLock) { 710 return mNightModeLocked; 711 } 712 } 713 714 @Override 715 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 716 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 717 new Shell(mService).exec(mService, in, out, err, args, callback, resultReceiver); 718 } 719 720 @Override 721 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 722 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; 723 dumpImpl(pw); 724 } 725 726 @Override 727 public boolean setNightModeActivated(boolean active) { 728 synchronized (mLock) { 729 final int user = UserHandle.getCallingUserId(); 730 final long ident = Binder.clearCallingIdentity(); 731 try { 732 if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) { 733 unregisterScreenOffEventLocked(); 734 mOverrideNightModeOff = !active; 735 mOverrideNightModeOn = active; 736 mOverrideNightModeUser = user; 737 persistNightModeOverrides(user); 738 } else if (mNightMode == UiModeManager.MODE_NIGHT_NO 739 && active) { 740 mNightMode = UiModeManager.MODE_NIGHT_YES; 741 } else if (mNightMode == UiModeManager.MODE_NIGHT_YES 742 && !active) { 743 mNightMode = UiModeManager.MODE_NIGHT_NO; 744 } 745 updateConfigurationLocked(); 746 applyConfigurationExternallyLocked(); 747 persistNightMode(user); 748 return true; 749 } finally { 750 Binder.restoreCallingIdentity(ident); 751 } 752 } 753 } 754 755 @Override 756 public long getCustomNightModeStart() { 757 return mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000; 758 } 759 760 @Override 761 public void setCustomNightModeStart(long time) { 762 if (isNightModeLocked() && getContext().checkCallingOrSelfPermission( 763 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 764 != PackageManager.PERMISSION_GRANTED) { 765 Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission"); 766 return; 767 } 768 final int user = UserHandle.getCallingUserId(); 769 final long ident = Binder.clearCallingIdentity(); 770 try { 771 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); 772 if (newTime == null) return; 773 mCustomAutoNightModeStartMilliseconds = newTime; 774 persistNightMode(user); 775 onCustomTimeUpdated(user); 776 } catch (DateTimeException e) { 777 unregisterScreenOffEventLocked(); 778 } finally { 779 Binder.restoreCallingIdentity(ident); 780 } 781 } 782 783 @Override 784 public long getCustomNightModeEnd() { 785 return mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000; 786 } 787 788 @Override 789 public void setCustomNightModeEnd(long time) { 790 if (isNightModeLocked() && getContext().checkCallingOrSelfPermission( 791 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 792 != PackageManager.PERMISSION_GRANTED) { 793 Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission"); 794 return; 795 } 796 final int user = UserHandle.getCallingUserId(); 797 final long ident = Binder.clearCallingIdentity(); 798 try { 799 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); 800 if (newTime == null) return; 801 mCustomAutoNightModeEndMilliseconds = newTime; 802 onCustomTimeUpdated(user); 803 } catch (DateTimeException e) { 804 unregisterScreenOffEventLocked(); 805 } finally { 806 Binder.restoreCallingIdentity(ident); 807 } 808 } 809 }; 810 onCustomTimeUpdated(int user)811 private void onCustomTimeUpdated(int user) { 812 persistNightMode(user); 813 if (mNightMode != MODE_NIGHT_CUSTOM) return; 814 if (shouldApplyAutomaticChangesImmediately()) { 815 unregisterScreenOffEventLocked(); 816 updateLocked(0, 0); 817 } else { 818 registerScreenOffEventLocked(); 819 } 820 } 821 dumpImpl(PrintWriter pw)822 void dumpImpl(PrintWriter pw) { 823 synchronized (mLock) { 824 pw.println("Current UI Mode Service state:"); 825 pw.print(" mDockState="); pw.print(mDockState); 826 pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); 827 828 pw.print(" mNightMode="); pw.print(mNightMode); pw.print(" ("); 829 pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") "); 830 pw.print(" mOverrideOn/Off="); pw.print(mOverrideNightModeOn); 831 pw.print("/"); pw.print(mOverrideNightModeOff); 832 833 pw.print(" mNightModeLocked="); pw.println(mNightModeLocked); 834 835 pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); 836 pw.print(" (carModeApps="); 837 for (Map.Entry<Integer, String> entry : mCarModePackagePriority.entrySet()) { 838 pw.print(entry.getKey()); 839 pw.print(":"); 840 pw.print(entry.getValue()); 841 pw.print(" "); 842 } 843 pw.println(""); 844 pw.print(" waitScreenOff="); pw.print(mWaitForScreenOff); 845 pw.print(" mComputedNightMode="); pw.print(mComputedNightMode); 846 pw.print(" customStart="); pw.print(mCustomAutoNightModeStartMilliseconds); 847 pw.print(" customEnd"); pw.print(mCustomAutoNightModeEndMilliseconds); 848 pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags); 849 pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch); 850 851 pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode)); 852 pw.print(" mUiModeLocked="); pw.print(mUiModeLocked); 853 pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); 854 855 pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); 856 pw.print(" mSystemReady="); pw.println(mSystemReady); 857 858 if (mTwilightManager != null) { 859 // We may not have a TwilightManager. 860 pw.print(" mTwilightService.getLastTwilightState()="); 861 pw.println(mTwilightManager.getLastTwilightState()); 862 } 863 } 864 } 865 866 /** 867 * Updates the global car mode state. 868 * The device is considered to be in car mode if there exists an app at any priority level which 869 * has entered car mode. 870 * 871 * @param enabled {@code true} if the caller wishes to enable car mode, {@code false} otherwise. 872 * @param flags Flags used when enabling/disabling car mode. 873 * @param priority The priority level for entering or exiting car mode; defaults to 874 * {@link UiModeManager#DEFAULT_PRIORITY} for callers using 875 * {@link UiModeManager#enableCarMode(int)}. Callers using 876 * {@link UiModeManager#enableCarMode(int, int)} may specify a priority. 877 * @param packageName The package name of the app which initiated the request to enable or 878 * disable car mode. 879 */ setCarModeLocked(boolean enabled, int flags, int priority, String packageName)880 void setCarModeLocked(boolean enabled, int flags, int priority, String packageName) { 881 if (enabled) { 882 enableCarMode(priority, packageName); 883 } else { 884 disableCarMode(flags, priority, packageName); 885 } 886 boolean isCarModeNowEnabled = isCarModeEnabled(); 887 888 if (mCarModeEnabled != isCarModeNowEnabled) { 889 mCarModeEnabled = isCarModeNowEnabled; 890 // When exiting car mode, restore night mode from settings 891 if (!isCarModeNowEnabled) { 892 Context context = getContext(); 893 updateNightModeFromSettingsLocked(context, 894 context.getResources(), 895 UserHandle.getCallingUserId()); 896 } 897 } 898 mCarModeEnableFlags = flags; 899 } 900 901 /** 902 * Handles disabling car mode. 903 * <p> 904 * Car mode can be disabled at a priority level if any of the following is true: 905 * 1. The priority being disabled is the {@link UiModeManager#DEFAULT_PRIORITY}. 906 * 2. The priority level is enabled and the caller is the app who originally enabled it. 907 * 3. The {@link UiModeManager#DISABLE_CAR_MODE_ALL_PRIORITIES} flag was specified, meaning all 908 * car mode priorities are disabled. 909 * 910 * @param flags Car mode flags. 911 * @param priority The priority level at which to disable car mode. 912 * @param packageName The calling package which initiated the request. 913 */ disableCarMode(int flags, int priority, String packageName)914 private void disableCarMode(int flags, int priority, String packageName) { 915 boolean isDisableAll = (flags & UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES) != 0; 916 boolean isPriorityTracked = mCarModePackagePriority.keySet().contains(priority); 917 boolean isDefaultPriority = priority == UiModeManager.DEFAULT_PRIORITY; 918 boolean isChangeAllowed = 919 // Anyone can disable the default priority. 920 isDefaultPriority 921 // If priority was enabled, only enabling package can disable it. 922 || isPriorityTracked && mCarModePackagePriority.get(priority).equals( 923 packageName) 924 // Disable all priorities flag can disable all regardless. 925 || isDisableAll; 926 if (isChangeAllowed) { 927 Slog.d(TAG, "disableCarMode: disabling, priority=" + priority 928 + ", packageName=" + packageName); 929 if (isDisableAll) { 930 Set<Map.Entry<Integer, String>> entries = 931 new ArraySet<>(mCarModePackagePriority.entrySet()); 932 mCarModePackagePriority.clear(); 933 934 for (Map.Entry<Integer, String> entry : entries) { 935 notifyCarModeDisabled(entry.getKey(), entry.getValue()); 936 } 937 } else { 938 mCarModePackagePriority.remove(priority); 939 notifyCarModeDisabled(priority, packageName); 940 } 941 } 942 } 943 944 /** 945 * Handles enabling car mode. 946 * <p> 947 * Car mode can be enabled at any priority if it has not already been enabled at that priority. 948 * The calling package is tracked for the first app which enters priority at the 949 * {@link UiModeManager#DEFAULT_PRIORITY}, though any app can disable it at that priority. 950 * 951 * @param priority The priority for enabling car mode. 952 * @param packageName The calling package which initiated the request. 953 */ enableCarMode(int priority, String packageName)954 private void enableCarMode(int priority, String packageName) { 955 boolean isPriorityTracked = mCarModePackagePriority.containsKey(priority); 956 boolean isPackagePresent = mCarModePackagePriority.containsValue(packageName); 957 if (!isPriorityTracked && !isPackagePresent) { 958 Slog.d(TAG, "enableCarMode: enabled at priority=" + priority + ", packageName=" 959 + packageName); 960 mCarModePackagePriority.put(priority, packageName); 961 notifyCarModeEnabled(priority, packageName); 962 } else { 963 Slog.d(TAG, "enableCarMode: car mode at priority " + priority + " already enabled."); 964 } 965 966 } 967 notifyCarModeEnabled(int priority, String packageName)968 private void notifyCarModeEnabled(int priority, String packageName) { 969 Intent intent = new Intent(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED); 970 intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName); 971 intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority); 972 getContext().sendBroadcastAsUser(intent, UserHandle.ALL, 973 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES); 974 } 975 notifyCarModeDisabled(int priority, String packageName)976 private void notifyCarModeDisabled(int priority, String packageName) { 977 Intent intent = new Intent(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED); 978 intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName); 979 intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority); 980 getContext().sendBroadcastAsUser(intent, UserHandle.ALL, 981 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES); 982 } 983 984 /** 985 * Determines if car mode is enabled at any priority level. 986 * @return {@code true} if car mode is enabled, {@code false} otherwise. 987 */ isCarModeEnabled()988 private boolean isCarModeEnabled() { 989 return mCarModePackagePriority.size() > 0; 990 } 991 updateDockState(int newState)992 private void updateDockState(int newState) { 993 synchronized (mLock) { 994 if (newState != mDockState) { 995 mDockState = newState; 996 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0, 997 UiModeManager.DEFAULT_PRIORITY, "" /* packageName */); 998 if (mSystemReady) { 999 updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0); 1000 } 1001 } 1002 } 1003 } 1004 isDeskDockState(int state)1005 private static boolean isDeskDockState(int state) { 1006 switch (state) { 1007 case Intent.EXTRA_DOCK_STATE_DESK: 1008 case Intent.EXTRA_DOCK_STATE_LE_DESK: 1009 case Intent.EXTRA_DOCK_STATE_HE_DESK: 1010 return true; 1011 default: 1012 return false; 1013 } 1014 } 1015 persistNightMode(int user)1016 private void persistNightMode(int user) { 1017 // Only persist setting if not in car mode 1018 if (mCarModeEnabled) return; 1019 Secure.putIntForUser(getContext().getContentResolver(), 1020 Secure.UI_NIGHT_MODE, mNightMode, user); 1021 Secure.putLongForUser(getContext().getContentResolver(), 1022 Secure.DARK_THEME_CUSTOM_START_TIME, 1023 mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user); 1024 Secure.putLongForUser(getContext().getContentResolver(), 1025 Secure.DARK_THEME_CUSTOM_END_TIME, 1026 mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000, user); 1027 } 1028 persistNightModeOverrides(int user)1029 private void persistNightModeOverrides(int user) { 1030 // Only persist setting if not in car mode 1031 if (mCarModeEnabled) return; 1032 Secure.putIntForUser(getContext().getContentResolver(), 1033 Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user); 1034 Secure.putIntForUser(getContext().getContentResolver(), 1035 Secure.UI_NIGHT_MODE_OVERRIDE_OFF, mOverrideNightModeOff ? 1 : 0, user); 1036 } 1037 updateConfigurationLocked()1038 private void updateConfigurationLocked() { 1039 int uiMode = mDefaultUiModeType; 1040 if (mUiModeLocked) { 1041 // no-op, keeps default one 1042 } else if (mTelevision) { 1043 uiMode = Configuration.UI_MODE_TYPE_TELEVISION; 1044 } else if (mWatch) { 1045 uiMode = Configuration.UI_MODE_TYPE_WATCH; 1046 } else if (mCarModeEnabled) { 1047 uiMode = Configuration.UI_MODE_TYPE_CAR; 1048 } else if (isDeskDockState(mDockState)) { 1049 uiMode = Configuration.UI_MODE_TYPE_DESK; 1050 } else if (mVrHeadset) { 1051 uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET; 1052 } 1053 1054 if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) { 1055 updateComputedNightModeLocked(mNightMode == MODE_NIGHT_YES); 1056 } 1057 1058 if (mNightMode == MODE_NIGHT_AUTO) { 1059 boolean activateNightMode = mComputedNightMode; 1060 if (mTwilightManager != null) { 1061 mTwilightManager.registerListener(mTwilightListener, mHandler); 1062 final TwilightState lastState = mTwilightManager.getLastTwilightState(); 1063 activateNightMode = lastState == null ? mComputedNightMode : lastState.isNight(); 1064 } 1065 updateComputedNightModeLocked(activateNightMode); 1066 } else { 1067 if (mTwilightManager != null) { 1068 mTwilightManager.unregisterListener(mTwilightListener); 1069 } 1070 } 1071 1072 if (mNightMode == MODE_NIGHT_CUSTOM) { 1073 registerTimeChangeEvent(); 1074 final boolean activate = computeCustomNightMode(); 1075 updateComputedNightModeLocked(activate); 1076 scheduleNextCustomTimeListener(); 1077 } else { 1078 unregisterTimeChangeEvent(); 1079 } 1080 1081 // Override night mode in power save mode if not in car mode 1082 if (mPowerSave && !mCarModeEnabled) { 1083 uiMode &= ~Configuration.UI_MODE_NIGHT_NO; 1084 uiMode |= Configuration.UI_MODE_NIGHT_YES; 1085 } else { 1086 uiMode = getComputedUiModeConfiguration(uiMode); 1087 } 1088 1089 if (LOG) { 1090 Slog.d(TAG, 1091 "updateConfigurationLocked: mDockState=" + mDockState 1092 + "; mCarMode=" + mCarModeEnabled 1093 + "; mNightMode=" + mNightMode 1094 + "; uiMode=" + uiMode); 1095 } 1096 1097 mCurUiMode = uiMode; 1098 if (!mHoldingConfiguration && (!mWaitForScreenOff || mPowerSave)) { 1099 mConfiguration.uiMode = uiMode; 1100 } 1101 } 1102 1103 @UiModeManager.NightMode getComputedUiModeConfiguration(@iModeManager.NightMode int uiMode)1104 private int getComputedUiModeConfiguration(@UiModeManager.NightMode int uiMode) { 1105 uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES 1106 : Configuration.UI_MODE_NIGHT_NO; 1107 uiMode &= mComputedNightMode ? ~Configuration.UI_MODE_NIGHT_NO 1108 : ~Configuration.UI_MODE_NIGHT_YES; 1109 return uiMode; 1110 } 1111 computeCustomNightMode()1112 private boolean computeCustomNightMode() { 1113 return isTimeBetween(LocalTime.now(), 1114 mCustomAutoNightModeStartMilliseconds, 1115 mCustomAutoNightModeEndMilliseconds); 1116 } 1117 applyConfigurationExternallyLocked()1118 private void applyConfigurationExternallyLocked() { 1119 if (mSetUiMode != mConfiguration.uiMode) { 1120 mSetUiMode = mConfiguration.uiMode; 1121 // load splash screen instead of screenshot 1122 mWindowManager.clearSnapshotCache(); 1123 try { 1124 ActivityTaskManager.getService().updateConfiguration(mConfiguration); 1125 } catch (RemoteException e) { 1126 Slog.w(TAG, "Failure communicating with activity manager", e); 1127 } catch (SecurityException e) { 1128 Slog.e(TAG, "Activity does not have the ", e); 1129 } 1130 } 1131 } 1132 shouldApplyAutomaticChangesImmediately()1133 private boolean shouldApplyAutomaticChangesImmediately() { 1134 return mCar || !mPowerManager.isInteractive(); 1135 } 1136 scheduleNextCustomTimeListener()1137 private void scheduleNextCustomTimeListener() { 1138 cancelCustomAlarm(); 1139 LocalDateTime now = LocalDateTime.now(); 1140 final boolean active = computeCustomNightMode(); 1141 final LocalDateTime next = active 1142 ? getDateTimeAfter(mCustomAutoNightModeEndMilliseconds, now) 1143 : getDateTimeAfter(mCustomAutoNightModeStartMilliseconds, now); 1144 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); 1145 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, mCustomTimeListener, null); 1146 } 1147 getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime)1148 private LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) { 1149 final LocalDateTime ldt = LocalDateTime.of(compareTime.toLocalDate(), localTime); 1150 1151 // Check if the local time has passed, if so return the same time tomorrow. 1152 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; 1153 } 1154 updateLocked(int enableFlags, int disableFlags)1155 void updateLocked(int enableFlags, int disableFlags) { 1156 String action = null; 1157 String oldAction = null; 1158 if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { 1159 adjustStatusBarCarModeLocked(); 1160 oldAction = UiModeManager.ACTION_EXIT_CAR_MODE; 1161 } else if (isDeskDockState(mLastBroadcastState)) { 1162 oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; 1163 } 1164 1165 if (mCarModeEnabled) { 1166 if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) { 1167 adjustStatusBarCarModeLocked(); 1168 if (oldAction != null) { 1169 sendForegroundBroadcastToAllUsers(oldAction); 1170 } 1171 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; 1172 action = UiModeManager.ACTION_ENTER_CAR_MODE; 1173 } 1174 } else if (isDeskDockState(mDockState)) { 1175 if (!isDeskDockState(mLastBroadcastState)) { 1176 if (oldAction != null) { 1177 sendForegroundBroadcastToAllUsers(oldAction); 1178 } 1179 mLastBroadcastState = mDockState; 1180 action = UiModeManager.ACTION_ENTER_DESK_MODE; 1181 } 1182 } else { 1183 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 1184 action = oldAction; 1185 } 1186 1187 if (action != null) { 1188 if (LOG) { 1189 Slog.v(TAG, String.format( 1190 "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x", 1191 action, enableFlags, disableFlags)); 1192 } 1193 1194 // Send the ordered broadcast; the result receiver will receive after all 1195 // broadcasts have been sent. If any broadcast receiver changes the result 1196 // code from the initial value of RESULT_OK, then the result receiver will 1197 // not launch the corresponding dock application. This gives apps a chance 1198 // to override the behavior and stay in their app even when the device is 1199 // placed into a dock. 1200 Intent intent = new Intent(action); 1201 intent.putExtra("enableFlags", enableFlags); 1202 intent.putExtra("disableFlags", disableFlags); 1203 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1204 getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 1205 mResultReceiver, null, Activity.RESULT_OK, null, null); 1206 1207 // Attempting to make this transition a little more clean, we are going 1208 // to hold off on doing a configuration change until we have finished 1209 // the broadcast and started the home activity. 1210 mHoldingConfiguration = true; 1211 updateConfigurationLocked(); 1212 } else { 1213 String category = null; 1214 if (mCarModeEnabled) { 1215 if (mEnableCarDockLaunch 1216 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 1217 category = Intent.CATEGORY_CAR_DOCK; 1218 } 1219 } else if (isDeskDockState(mDockState)) { 1220 if (ENABLE_LAUNCH_DESK_DOCK_APP 1221 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 1222 category = Intent.CATEGORY_DESK_DOCK; 1223 } 1224 } else { 1225 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { 1226 category = Intent.CATEGORY_HOME; 1227 } 1228 } 1229 1230 if (LOG) { 1231 Slog.v(TAG, "updateLocked: null action, mDockState=" 1232 + mDockState +", category=" + category); 1233 } 1234 1235 sendConfigurationAndStartDreamOrDockAppLocked(category); 1236 } 1237 1238 // keep screen on when charging and in car mode 1239 boolean keepScreenOn = mCharging && 1240 ((mCarModeEnabled && mCarModeKeepsScreenOn && 1241 (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) || 1242 (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn)); 1243 if (keepScreenOn != mWakeLock.isHeld()) { 1244 if (keepScreenOn) { 1245 mWakeLock.acquire(); 1246 } else { 1247 mWakeLock.release(); 1248 } 1249 } 1250 } 1251 sendForegroundBroadcastToAllUsers(String action)1252 private void sendForegroundBroadcastToAllUsers(String action) { 1253 getContext().sendBroadcastAsUser(new Intent(action) 1254 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL); 1255 } 1256 updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags)1257 private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) { 1258 // Launch a dock activity 1259 String category = null; 1260 if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) { 1261 // Only launch car home when car mode is enabled and the caller 1262 // has asked us to switch to it. 1263 if (mEnableCarDockLaunch 1264 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 1265 category = Intent.CATEGORY_CAR_DOCK; 1266 } 1267 } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) { 1268 // Only launch car home when desk mode is enabled and the caller 1269 // has asked us to switch to it. Currently re-using the car 1270 // mode flag since we don't have a formal API for "desk mode". 1271 if (ENABLE_LAUNCH_DESK_DOCK_APP 1272 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 1273 category = Intent.CATEGORY_DESK_DOCK; 1274 } 1275 } else { 1276 // Launch the standard home app if requested. 1277 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { 1278 category = Intent.CATEGORY_HOME; 1279 } 1280 } 1281 1282 if (LOG) { 1283 Slog.v(TAG, String.format( 1284 "Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, " 1285 + "category=%s", 1286 action, enableFlags, disableFlags, category)); 1287 } 1288 1289 sendConfigurationAndStartDreamOrDockAppLocked(category); 1290 } 1291 sendConfigurationAndStartDreamOrDockAppLocked(String category)1292 private void sendConfigurationAndStartDreamOrDockAppLocked(String category) { 1293 // Update the configuration but don't send it yet. 1294 mHoldingConfiguration = false; 1295 updateConfigurationLocked(); 1296 1297 // Start the dock app, if there is one. 1298 boolean dockAppStarted = false; 1299 if (category != null) { 1300 // Now we are going to be careful about switching the 1301 // configuration and starting the activity -- we need to 1302 // do this in a specific order under control of the 1303 // activity manager, to do it cleanly. So compute the 1304 // new config, but don't set it yet, and let the 1305 // activity manager take care of both the start and config 1306 // change. 1307 Intent homeIntent = buildHomeIntent(category); 1308 if (Sandman.shouldStartDockApp(getContext(), homeIntent)) { 1309 try { 1310 int result = ActivityTaskManager.getService().startActivityWithConfig( 1311 null, getContext().getBasePackageName(), 1312 getContext().getAttributionTag(), homeIntent, null, null, null, 0, 0, 1313 mConfiguration, null, UserHandle.USER_CURRENT); 1314 if (ActivityManager.isStartResultSuccessful(result)) { 1315 dockAppStarted = true; 1316 } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { 1317 Slog.e(TAG, "Could not start dock app: " + homeIntent 1318 + ", startActivityWithConfig result " + result); 1319 } 1320 } catch (RemoteException ex) { 1321 Slog.e(TAG, "Could not start dock app: " + homeIntent, ex); 1322 } 1323 } 1324 } 1325 1326 // Send the new configuration. 1327 applyConfigurationExternallyLocked(); 1328 1329 // If we did not start a dock app, then start dreaming if supported. 1330 if (category != null && !dockAppStarted) { 1331 Sandman.startDreamWhenDockedIfAppropriate(getContext()); 1332 } 1333 } 1334 adjustStatusBarCarModeLocked()1335 private void adjustStatusBarCarModeLocked() { 1336 final Context context = getContext(); 1337 if (mStatusBarManager == null) { 1338 mStatusBarManager = (StatusBarManager) 1339 context.getSystemService(Context.STATUS_BAR_SERVICE); 1340 } 1341 1342 // Fear not: StatusBarManagerService manages a list of requests to disable 1343 // features of the status bar; these are ORed together to form the 1344 // active disabled list. So if (for example) the device is locked and 1345 // the status bar should be totally disabled, the calls below will 1346 // have no effect until the device is unlocked. 1347 if (mStatusBarManager != null) { 1348 mStatusBarManager.disable(mCarModeEnabled 1349 ? StatusBarManager.DISABLE_NOTIFICATION_TICKER 1350 : StatusBarManager.DISABLE_NONE); 1351 } 1352 1353 if (mNotificationManager == null) { 1354 mNotificationManager = (NotificationManager) 1355 context.getSystemService(Context.NOTIFICATION_SERVICE); 1356 } 1357 1358 if (mNotificationManager != null) { 1359 if (mCarModeEnabled) { 1360 Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class); 1361 1362 Notification.Builder n = 1363 new Notification.Builder(context, SystemNotificationChannels.CAR_MODE) 1364 .setSmallIcon(R.drawable.stat_notify_car_mode) 1365 .setDefaults(Notification.DEFAULT_LIGHTS) 1366 .setOngoing(true) 1367 .setWhen(0) 1368 .setColor(context.getColor( 1369 com.android.internal.R.color.system_notification_accent_color)) 1370 .setContentTitle( 1371 context.getString(R.string.car_mode_disable_notification_title)) 1372 .setContentText( 1373 context.getString(R.string.car_mode_disable_notification_message)) 1374 .setContentIntent( 1375 PendingIntent.getActivityAsUser(context, 0, 1376 carModeOffIntent, 0, 1377 null, UserHandle.CURRENT)); 1378 mNotificationManager.notifyAsUser(null, 1379 SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL); 1380 } else { 1381 mNotificationManager.cancelAsUser(null, 1382 SystemMessage.NOTE_CAR_MODE_DISABLE, UserHandle.ALL); 1383 } 1384 } 1385 } 1386 updateComputedNightModeLocked(boolean activate)1387 private void updateComputedNightModeLocked(boolean activate) { 1388 mComputedNightMode = activate; 1389 if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) { 1390 return; 1391 } 1392 if (mOverrideNightModeOn && !mComputedNightMode) { 1393 mComputedNightMode = true; 1394 return; 1395 } 1396 if (mOverrideNightModeOff && mComputedNightMode) { 1397 mComputedNightMode = false; 1398 return; 1399 } 1400 resetNightModeOverrideLocked(); 1401 } 1402 resetNightModeOverrideLocked()1403 private boolean resetNightModeOverrideLocked() { 1404 if (mOverrideNightModeOff || mOverrideNightModeOn) { 1405 mOverrideNightModeOff = false; 1406 mOverrideNightModeOn = false; 1407 persistNightModeOverrides(mOverrideNightModeUser); 1408 mOverrideNightModeUser = USER_SYSTEM; 1409 return true; 1410 } 1411 return false; 1412 } 1413 registerVrStateListener()1414 private void registerVrStateListener() { 1415 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService( 1416 Context.VR_SERVICE)); 1417 try { 1418 if (vrManager != null) { 1419 vrManager.registerListener(mVrStateCallbacks); 1420 } 1421 } catch (RemoteException e) { 1422 Slog.e(TAG, "Failed to register VR mode state listener: " + e); 1423 } 1424 } 1425 1426 /** 1427 * Handles "adb shell" commands. 1428 */ 1429 private static class Shell extends ShellCommand { 1430 public static final String NIGHT_MODE_STR_YES = "yes"; 1431 public static final String NIGHT_MODE_STR_NO = "no"; 1432 public static final String NIGHT_MODE_STR_AUTO = "auto"; 1433 public static final String NIGHT_MODE_STR_CUSTOM = "custom"; 1434 public static final String NIGHT_MODE_STR_UNKNOWN = "unknown"; 1435 private final IUiModeManager mInterface; 1436 Shell(IUiModeManager iface)1437 Shell(IUiModeManager iface) { 1438 mInterface = iface; 1439 } 1440 1441 @Override onHelp()1442 public void onHelp() { 1443 final PrintWriter pw = getOutPrintWriter(); 1444 pw.println("UiModeManager service (uimode) commands:"); 1445 pw.println(" help"); 1446 pw.println(" Print this help text."); 1447 pw.println(" night [yes|no|auto|custom]"); 1448 pw.println(" Set or read night mode."); 1449 pw.println(" time [start|end] <ISO time>"); 1450 pw.println(" Set custom start/end schedule time" 1451 + " (night mode must be set to custom to apply)."); 1452 } 1453 1454 @Override onCommand(String cmd)1455 public int onCommand(String cmd) { 1456 if (cmd == null) { 1457 return handleDefaultCommands(cmd); 1458 } 1459 1460 try { 1461 switch (cmd) { 1462 case "night": 1463 return handleNightMode(); 1464 case "time": 1465 return handleCustomTime(); 1466 default: 1467 return handleDefaultCommands(cmd); 1468 } 1469 } catch (RemoteException e) { 1470 final PrintWriter err = getErrPrintWriter(); 1471 err.println("Remote exception: " + e); 1472 } 1473 return -1; 1474 } 1475 handleCustomTime()1476 private int handleCustomTime() throws RemoteException { 1477 final String modeStr = getNextArg(); 1478 if (modeStr == null) { 1479 printCustomTime(); 1480 return 0; 1481 } 1482 switch (modeStr) { 1483 case "start": 1484 final String start = getNextArg(); 1485 mInterface.setCustomNightModeStart(toMilliSeconds(LocalTime.parse(start))); 1486 return 0; 1487 case "end": 1488 final String end = getNextArg(); 1489 mInterface.setCustomNightModeEnd(toMilliSeconds(LocalTime.parse(end))); 1490 return 0; 1491 default: 1492 getErrPrintWriter().println("command must be in [start|end]"); 1493 return -1; 1494 } 1495 } 1496 printCustomTime()1497 private void printCustomTime() throws RemoteException { 1498 getOutPrintWriter().println("start " + fromMilliseconds( 1499 mInterface.getCustomNightModeStart()).toString()); 1500 getOutPrintWriter().println("end " + fromMilliseconds( 1501 mInterface.getCustomNightModeEnd()).toString()); 1502 } 1503 handleNightMode()1504 private int handleNightMode() throws RemoteException { 1505 final PrintWriter err = getErrPrintWriter(); 1506 final String modeStr = getNextArg(); 1507 if (modeStr == null) { 1508 printCurrentNightMode(); 1509 return 0; 1510 } 1511 1512 final int mode = strToNightMode(modeStr); 1513 if (mode >= 0) { 1514 mInterface.setNightMode(mode); 1515 printCurrentNightMode(); 1516 return 0; 1517 } else { 1518 err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '" 1519 + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO 1520 + "', or '" + NIGHT_MODE_STR_CUSTOM + "'"); 1521 return -1; 1522 } 1523 } 1524 printCurrentNightMode()1525 private void printCurrentNightMode() throws RemoteException { 1526 final PrintWriter pw = getOutPrintWriter(); 1527 final int currMode = mInterface.getNightMode(); 1528 final String currModeStr = nightModeToStr(currMode); 1529 pw.println("Night mode: " + currModeStr); 1530 } 1531 nightModeToStr(int mode)1532 private static String nightModeToStr(int mode) { 1533 switch (mode) { 1534 case UiModeManager.MODE_NIGHT_YES: 1535 return NIGHT_MODE_STR_YES; 1536 case UiModeManager.MODE_NIGHT_NO: 1537 return NIGHT_MODE_STR_NO; 1538 case UiModeManager.MODE_NIGHT_AUTO: 1539 return NIGHT_MODE_STR_AUTO; 1540 case MODE_NIGHT_CUSTOM: 1541 return NIGHT_MODE_STR_CUSTOM; 1542 default: 1543 return NIGHT_MODE_STR_UNKNOWN; 1544 } 1545 } 1546 strToNightMode(String modeStr)1547 private static int strToNightMode(String modeStr) { 1548 switch (modeStr) { 1549 case NIGHT_MODE_STR_YES: 1550 return UiModeManager.MODE_NIGHT_YES; 1551 case NIGHT_MODE_STR_NO: 1552 return UiModeManager.MODE_NIGHT_NO; 1553 case NIGHT_MODE_STR_AUTO: 1554 return UiModeManager.MODE_NIGHT_AUTO; 1555 case NIGHT_MODE_STR_CUSTOM: 1556 return UiModeManager.MODE_NIGHT_CUSTOM; 1557 default: 1558 return -1; 1559 } 1560 } 1561 } 1562 1563 public final class LocalService extends UiModeManagerInternal { 1564 1565 @Override isNightMode()1566 public boolean isNightMode() { 1567 synchronized (mLock) { 1568 final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0; 1569 if (LOG) { 1570 Slog.d(TAG, 1571 "LocalService.isNightMode(): mNightMode=" + mNightMode 1572 + "; mComputedNightMode=" + mComputedNightMode 1573 + "; uiMode=" + mConfiguration.uiMode 1574 + "; isIt=" + isIt); 1575 } 1576 return isIt; 1577 } 1578 } 1579 } 1580 1581 private final class UserSwitchedReceiver extends BroadcastReceiver { 1582 @Override onReceive(Context context, Intent intent)1583 public void onReceive(Context context, Intent intent) { 1584 synchronized (mLock) { 1585 final int currentId = intent.getIntExtra( 1586 Intent.EXTRA_USER_HANDLE, USER_SYSTEM); 1587 // only update if the value is actually changed 1588 if (updateNightModeFromSettingsLocked(context, context.getResources(), currentId)) { 1589 updateLocked(0, 0); 1590 } 1591 } 1592 } 1593 } 1594 } 1595