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 static android.app.Flags.enableCurrentModeTypeBinderCache; 20 import static android.app.Flags.enableNightModeBinderCache; 21 import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE; 22 import static android.app.UiModeManager.DEFAULT_PRIORITY; 23 import static android.app.UiModeManager.FORCE_INVERT_TYPE_DARK; 24 import static android.app.UiModeManager.FORCE_INVERT_TYPE_OFF; 25 import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF; 26 import static android.app.UiModeManager.MODE_NIGHT_AUTO; 27 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; 28 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; 29 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE; 30 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; 31 import static android.app.UiModeManager.MODE_NIGHT_NO; 32 import static android.app.UiModeManager.MODE_NIGHT_YES; 33 import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE; 34 import static android.app.UiModeManager.PROJECTION_TYPE_NONE; 35 import static android.os.UserHandle.USER_SYSTEM; 36 import static android.os.UserHandle.getCallingUserId; 37 import static android.provider.Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED; 38 import static android.provider.Settings.Secure.CONTRAST_LEVEL; 39 import static android.util.TimeUtils.isTimeBetween; 40 41 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; 42 import static com.android.server.pm.UserManagerService.enforceCurrentUserIfVisibleBackgroundEnabled; 43 44 import android.annotation.IntRange; 45 import android.annotation.NonNull; 46 import android.annotation.Nullable; 47 import android.app.Activity; 48 import android.app.ActivityManager; 49 import android.app.ActivityTaskManager; 50 import android.app.ActivityThread; 51 import android.app.AlarmManager; 52 import android.app.IOnProjectionStateChangedListener; 53 import android.app.IUiModeManager; 54 import android.app.IUiModeManagerCallback; 55 import android.app.KeyguardManager; 56 import android.app.Notification; 57 import android.app.NotificationManager; 58 import android.app.PendingIntent; 59 import android.app.StatusBarManager; 60 import android.app.UiModeManager; 61 import android.app.UiModeManager.AttentionModeThemeOverlayType; 62 import android.app.UiModeManager.ForceInvertType; 63 import android.app.UiModeManager.NightModeCustomReturnType; 64 import android.app.UiModeManager.NightModeCustomType; 65 import android.content.BroadcastReceiver; 66 import android.content.Context; 67 import android.content.Intent; 68 import android.content.IntentFilter; 69 import android.content.pm.PackageManager; 70 import android.content.res.Configuration; 71 import android.content.res.Resources; 72 import android.database.ContentObserver; 73 import android.net.Uri; 74 import android.os.BatteryManager; 75 import android.os.Binder; 76 import android.os.Handler; 77 import android.os.IBinder; 78 import android.os.PermissionEnforcer; 79 import android.os.PowerManager; 80 import android.os.PowerManager.ServiceType; 81 import android.os.PowerManagerInternal; 82 import android.os.Process; 83 import android.os.RemoteCallbackList; 84 import android.os.RemoteException; 85 import android.os.ResultReceiver; 86 import android.os.ServiceManager; 87 import android.os.ShellCallback; 88 import android.os.ShellCommand; 89 import android.os.SystemProperties; 90 import android.os.UserHandle; 91 import android.provider.Settings; 92 import android.provider.Settings.Secure; 93 import android.service.dreams.DreamManagerInternal; 94 import android.service.dreams.Sandman; 95 import android.service.vr.IVrManager; 96 import android.service.vr.IVrStateCallbacks; 97 import android.util.ArraySet; 98 import android.util.Slog; 99 import android.util.SparseArray; 100 import android.util.SparseIntArray; 101 102 import com.android.internal.R; 103 import com.android.internal.annotations.GuardedBy; 104 import com.android.internal.annotations.VisibleForTesting; 105 import com.android.internal.app.DisableCarModeActivity; 106 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 107 import com.android.internal.notification.SystemNotificationChannels; 108 import com.android.internal.util.DumpUtils; 109 import com.android.server.twilight.TwilightListener; 110 import com.android.server.twilight.TwilightManager; 111 import com.android.server.twilight.TwilightState; 112 import com.android.server.wm.ActivityTaskManagerInternal; 113 import com.android.server.wm.WindowManagerInternal; 114 115 import java.io.FileDescriptor; 116 import java.io.PrintWriter; 117 import java.time.DateTimeException; 118 import java.time.LocalDateTime; 119 import java.time.LocalTime; 120 import java.time.ZoneId; 121 import java.util.ArrayList; 122 import java.util.Arrays; 123 import java.util.Collection; 124 import java.util.HashMap; 125 import java.util.List; 126 import java.util.Map; 127 import java.util.Set; 128 129 final class UiModeManagerService extends SystemService { 130 private static final String TAG = UiModeManager.class.getSimpleName(); 131 private static final boolean LOG = false; 132 133 // Enable launching of applications when entering the dock. 134 private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; 135 private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme"; 136 @VisibleForTesting 137 public static final Set<Integer> SUPPORTED_NIGHT_MODE_CUSTOM_TYPES = new ArraySet( 138 new Integer[]{MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, MODE_NIGHT_CUSTOM_TYPE_BEDTIME}); 139 140 private final Injector mInjector; 141 private final Object mLock = new Object(); 142 143 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 144 145 private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 146 147 private final IntProperty mNightMode = new IntProperty(){ 148 private int mNightModeValue = UiModeManager.MODE_NIGHT_NO; 149 150 @Override 151 public int get() { 152 return mNightModeValue; 153 } 154 155 @Override 156 public void set(int mode) { 157 mNightModeValue = mode; 158 if (enableNightModeBinderCache()) { 159 UiModeManager.invalidateNightModeCache(); 160 } 161 } 162 }; 163 private int mNightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; 164 private int mAttentionModeThemeOverlay = UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF; 165 private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0); 166 private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0); 167 private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME; 168 private LocalTime mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME; 169 170 private Map<Integer, String> mCarModePackagePriority = new HashMap<>(); 171 private boolean mCarModeEnabled = false; 172 private boolean mCharging = false; 173 private boolean mPowerSave = false; 174 // Do not change configuration now. wait until the device is inactive (eg screen off, dreaming) 175 // This prevents jank and activity restart when the user 176 // is actively using the device 177 private boolean mWaitForDeviceInactive = false; 178 private int mDefaultUiModeType; 179 private boolean mCarModeKeepsScreenOn; 180 private boolean mDeskModeKeepsScreenOn; 181 private boolean mTelevision; 182 private boolean mCar; 183 private boolean mWatch; 184 private boolean mVrHeadset; 185 private boolean mComputedNightMode; 186 private boolean mLastBedtimeRequestedNightMode = false; 187 private int mCarModeEnableFlags; 188 private boolean mSetupWizardComplete; 189 190 // flag set by resource, whether to start dream immediately upon docking even if unlocked. 191 private boolean mStartDreamImmediatelyOnDock = true; 192 // flag set by resource, whether to disable dreams when ambient mode suppression is enabled. 193 private boolean mDreamsDisabledByAmbientModeSuppression = false; 194 // flag set by resource, whether to enable Car dock launch when starting car mode. 195 private boolean mEnableCarDockLaunch = true; 196 // flag set by resource, whether to lock UI mode to the default one or not. 197 private boolean mUiModeLocked = false; 198 // flag set by resource, whether to night mode change for normal all or not. 199 private boolean mNightModeLocked = false; 200 201 private final IntProperty mCurUiMode = new IntProperty(){ 202 private int mCurrentModeTypeValue = 0; 203 204 @Override 205 public int get() { 206 return mCurrentModeTypeValue; 207 } 208 209 @Override 210 public void set(int mode) { 211 mCurrentModeTypeValue = mode; 212 if (enableCurrentModeTypeBinderCache()) { 213 UiModeManager.invalidateCurrentModeTypeCache(); 214 } 215 } 216 }; 217 private int mSetUiMode = 0; 218 private boolean mHoldingConfiguration = false; 219 private int mCurrentUser; 220 221 private Configuration mConfiguration = new Configuration(); 222 boolean mSystemReady; 223 224 private final Handler mHandler = new Handler(); 225 226 private TwilightManager mTwilightManager; 227 private NotificationManager mNotificationManager; 228 private StatusBarManager mStatusBarManager; 229 private WindowManagerInternal mWindowManager; 230 private ActivityTaskManagerInternal mActivityTaskManager; 231 private AlarmManager mAlarmManager; 232 private PowerManager mPowerManager; 233 private KeyguardManager mKeyguardManager; 234 235 // In automatic scheduling, the user is able 236 // to override the computed night mode until the two match 237 // Example: Activate dark mode in the day time until sunrise the next day 238 private boolean mOverrideNightModeOn; 239 private boolean mOverrideNightModeOff; 240 private int mOverrideNightModeUser = USER_SYSTEM; 241 242 private PowerManager.WakeLock mWakeLock; 243 244 private final LocalService mLocalService = new LocalService(); 245 private PowerManagerInternal mLocalPowerManager; 246 private DreamManagerInternal mDreamManagerInternal; 247 248 private final IUiModeManager.Stub mService; 249 250 @GuardedBy("mLock") 251 private final SparseArray<RemoteCallbackList<IUiModeManagerCallback>> mUiModeManagerCallbacks = 252 new SparseArray<>(); 253 254 @GuardedBy("mLock") 255 @Nullable 256 private SparseArray<List<ProjectionHolder>> mProjectionHolders; 257 @GuardedBy("mLock") 258 @Nullable 259 private SparseArray<RemoteCallbackList<IOnProjectionStateChangedListener>> mProjectionListeners; 260 261 @GuardedBy("mLock") 262 private final SparseArray<Float> mContrasts = new SparseArray<>(); 263 264 @GuardedBy("mLock") 265 private final SparseIntArray mForceInvertStates = new SparseIntArray(); 266 UiModeManagerService(Context context)267 public UiModeManagerService(Context context) { 268 this(context, /* setupWizardComplete= */ false, /* tm= */ null, new Injector()); 269 } 270 271 @VisibleForTesting UiModeManagerService(Context context, boolean setupWizardComplete, TwilightManager tm, Injector injector)272 protected UiModeManagerService(Context context, boolean setupWizardComplete, 273 TwilightManager tm, Injector injector) { 274 super(context); 275 mService = new Stub(context); 276 mConfiguration.setToDefaults(); 277 mSetupWizardComplete = setupWizardComplete; 278 mTwilightManager = tm; 279 mInjector = injector; 280 } 281 buildHomeIntent(String category)282 private static Intent buildHomeIntent(String category) { 283 Intent intent = new Intent(Intent.ACTION_MAIN); 284 intent.addCategory(category); 285 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 286 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 287 return intent; 288 } 289 290 // The broadcast receiver which receives the result of the ordered broadcast sent when 291 // the dock state changes. The original ordered broadcast is sent with an initial result 292 // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., 293 // to RESULT_CANCELED, then the intent to start a dock app will not be sent. 294 private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 295 @Override 296 public void onReceive(Context context, Intent intent) { 297 if (getResultCode() != Activity.RESULT_OK) { 298 if (LOG) { 299 Slog.v(TAG, "Handling broadcast result for action " + intent.getAction() 300 + ": canceled: " + getResultCode()); 301 } 302 return; 303 } 304 305 final int enableFlags = intent.getIntExtra("enableFlags", 0); 306 final int disableFlags = intent.getIntExtra("disableFlags", 0); 307 synchronized (mLock) { 308 updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags); 309 } 310 } 311 }; 312 313 private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() { 314 @Override 315 public void onReceive(Context context, Intent intent) { 316 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 317 Intent.EXTRA_DOCK_STATE_UNDOCKED); 318 updateDockState(state); 319 } 320 }; 321 322 private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { 323 @Override 324 public void onReceive(Context context, Intent intent) { 325 switch (intent.getAction()) { 326 case Intent.ACTION_BATTERY_CHANGED: 327 mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0; 328 break; 329 } 330 synchronized (mLock) { 331 if (mSystemReady) { 332 updateLocked(0, 0); 333 } 334 } 335 } 336 }; 337 338 private final TwilightListener mTwilightListener = new TwilightListener() { 339 @Override 340 public void onTwilightStateChanged(@Nullable TwilightState state) { 341 synchronized (mLock) { 342 if (mNightMode.get() == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) { 343 if (shouldApplyAutomaticChangesImmediately()) { 344 updateLocked(0, 0); 345 } else { 346 registerDeviceInactiveListenerLocked(); 347 } 348 } 349 } 350 } 351 }; 352 353 /** 354 * DO NOT USE DIRECTLY 355 * see register registerScreenOffEvent and unregisterScreenOffEvent 356 */ 357 private final BroadcastReceiver mDeviceInactiveListener = new BroadcastReceiver() { 358 @Override 359 public void onReceive(Context context, Intent intent) { 360 synchronized (mLock) { 361 // must unregister first before updating 362 unregisterDeviceInactiveListenerLocked(); 363 updateLocked(0, 0); 364 } 365 } 366 }; 367 368 private final BroadcastReceiver mOnTimeChangedHandler = new BroadcastReceiver() { 369 @Override 370 public void onReceive(Context context, Intent intent) { 371 synchronized (mLock) { 372 updateCustomTimeLocked(); 373 } 374 } 375 }; 376 377 private final AlarmManager.OnAlarmListener mCustomTimeListener = () -> { 378 synchronized (mLock) { 379 updateCustomTimeLocked(); 380 } 381 }; 382 383 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 384 @Override 385 public void onVrStateChanged(boolean enabled) { 386 synchronized (mLock) { 387 mVrHeadset = enabled; 388 if (mSystemReady) { 389 updateLocked(0, 0); 390 } 391 } 392 } 393 }; 394 395 private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) { 396 @Override 397 public void onChange(boolean selfChange, Uri uri) { 398 synchronized (mLock) { 399 // setup wizard is done now so we can unblock 400 if (setupWizardCompleteForCurrentUser() && !selfChange) { 401 mSetupWizardComplete = true; 402 getContext().getContentResolver() 403 .unregisterContentObserver(mSetupWizardObserver); 404 // update night mode 405 Context context = getContext(); 406 updateNightModeFromSettingsLocked(context, context.getResources(), 407 UserHandle.getCallingUserId()); 408 updateLocked(0, 0); 409 } 410 } 411 } 412 }; 413 414 private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) { 415 @Override 416 public void onChange(boolean selfChange, Uri uri) { 417 updateSystemProperties(); 418 updateForceInvertStates(); 419 } 420 }; 421 422 private final ContentObserver mForceInvertStateObserver = new ContentObserver(mHandler) { 423 @Override 424 public void onChange(boolean selfChange, Uri uri) { 425 updateForceInvertStates(); 426 } 427 }; 428 updateForceInvertStates()429 private void updateForceInvertStates() { 430 if (!android.view.accessibility.Flags.forceInvertColor()) { 431 return; 432 } 433 434 synchronized (mLock) { 435 if (updateForceInvertStateLocked()) { 436 int forceInvertState = getForceInvertStateLocked(); 437 mUiModeManagerCallbacks.get(mCurrentUser, new RemoteCallbackList<>()) 438 .broadcast(ignoreRemoteException( 439 callback -> 440 callback.notifyForceInvertStateChanged(forceInvertState))); 441 } 442 } 443 } 444 445 private final ContentObserver mContrastObserver = new ContentObserver(mHandler) { 446 @Override 447 public void onChange(boolean selfChange, Uri uri) { 448 synchronized (mLock) { 449 if (updateContrastLocked()) { 450 float contrast = getContrastLocked(); 451 mUiModeManagerCallbacks.get(mCurrentUser, new RemoteCallbackList<>()) 452 .broadcast(ignoreRemoteException( 453 callback -> callback.notifyContrastChanged(contrast))); 454 } 455 } 456 } 457 }; 458 updateSystemProperties()459 private void updateSystemProperties() { 460 int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, 461 mNightMode.get(), 0); 462 if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) { 463 mode = MODE_NIGHT_YES; 464 } 465 SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode)); 466 } 467 468 @VisibleForTesting setStartDreamImmediatelyOnDock(boolean startDreamImmediatelyOnDock)469 void setStartDreamImmediatelyOnDock(boolean startDreamImmediatelyOnDock) { 470 mStartDreamImmediatelyOnDock = startDreamImmediatelyOnDock; 471 } 472 473 @VisibleForTesting setDreamsDisabledByAmbientModeSuppression(boolean disabledByAmbientModeSuppression)474 void setDreamsDisabledByAmbientModeSuppression(boolean disabledByAmbientModeSuppression) { 475 mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression; 476 } 477 478 @VisibleForTesting setCurrentUser(int currentUserId)479 void setCurrentUser(int currentUserId) { 480 mCurrentUser = currentUserId; 481 } 482 483 @Override onUserSwitching(@ullable TargetUser from, @NonNull TargetUser to)484 public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { 485 mCurrentUser = to.getUserIdentifier(); 486 if (mNightMode.get() == MODE_NIGHT_AUTO) persistComputedNightMode(from.getUserIdentifier()); 487 getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); 488 verifySetupWizardCompleted(); 489 synchronized (mLock) { 490 updateNightModeFromSettingsLocked(getContext(), getContext().getResources(), 491 to.getUserIdentifier()); 492 updateLocked(0, 0); 493 } 494 } 495 496 @Override onBootPhase(int phase)497 public void onBootPhase(int phase) { 498 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 499 synchronized (mLock) { 500 final Context context = getContext(); 501 mSystemReady = true; 502 mKeyguardManager = context.getSystemService(KeyguardManager.class); 503 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 504 mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); 505 mWindowManager = LocalServices.getService(WindowManagerInternal.class); 506 mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class); 507 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); 508 TwilightManager twilightManager = getLocalService(TwilightManager.class); 509 if (twilightManager != null) mTwilightManager = twilightManager; 510 mLocalPowerManager = 511 LocalServices.getService(PowerManagerInternal.class); 512 mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); 513 initPowerSave(); 514 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR; 515 registerVrStateListener(); 516 // register listeners 517 // LINT.IfChange(fi_cb) 518 context.getContentResolver() 519 .registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE), 520 false, mDarkThemeObserver, 0); 521 if (android.view.accessibility.Flags.forceInvertColor()) { 522 context.getContentResolver() 523 .registerContentObserver( 524 Secure.getUriFor(ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED), 525 false, mForceInvertStateObserver, UserHandle.USER_ALL); 526 } 527 // LINT.ThenChange(/core/java/android/view/ViewRootImpl.java:fi_cb) 528 context.getContentResolver().registerContentObserver( 529 Secure.getUriFor(Secure.CONTRAST_LEVEL), false, 530 mContrastObserver, UserHandle.USER_ALL); 531 context.registerReceiver(mDockModeReceiver, 532 new IntentFilter(Intent.ACTION_DOCK_EVENT)); 533 IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 534 context.registerReceiver(mBatteryReceiver, batteryFilter); 535 context.registerReceiver(mSettingsRestored, 536 new IntentFilter(Intent.ACTION_SETTING_RESTORED)); 537 context.registerReceiver(mOnShutdown, 538 new IntentFilter(Intent.ACTION_SHUTDOWN)); 539 updateConfigurationLocked(); 540 applyConfigurationExternallyLocked(); 541 } 542 } 543 } 544 545 @Override onStart()546 public void onStart() { 547 final Context context = getContext(); 548 // If setup isn't complete for this user listen for completion so we can unblock 549 // being able to send a night mode configuration change event 550 verifySetupWizardCompleted(); 551 552 final Resources res = context.getResources(); 553 mNightMode.set(res.getInteger( 554 com.android.internal.R.integer.config_defaultNightMode)); 555 mStartDreamImmediatelyOnDock = res.getBoolean( 556 com.android.internal.R.bool.config_startDreamImmediatelyOnDock); 557 mDreamsDisabledByAmbientModeSuppression = res.getBoolean( 558 com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig); 559 mDefaultUiModeType = res.getInteger( 560 com.android.internal.R.integer.config_defaultUiModeType); 561 mCarModeKeepsScreenOn = (res.getInteger( 562 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1); 563 mDeskModeKeepsScreenOn = (res.getInteger( 564 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1); 565 mEnableCarDockLaunch = res.getBoolean( 566 com.android.internal.R.bool.config_enableCarDockHomeLaunch); 567 mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode); 568 mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode); 569 final PackageManager pm = context.getPackageManager(); 570 mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION) 571 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK); 572 mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); 573 mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH); 574 575 // Update the initial, static configurations. 576 SystemServerInitThreadPool.submit(() -> { 577 synchronized (mLock) { 578 TwilightManager twilightManager = getLocalService(TwilightManager.class); 579 if (twilightManager != null) mTwilightManager = twilightManager; 580 updateNightModeFromSettingsLocked(context, res, UserHandle.getCallingUserId()); 581 updateSystemProperties(); 582 } 583 584 }, TAG + ".onStart"); 585 publishBinderService(Context.UI_MODE_SERVICE, mService); 586 publishLocalService(UiModeManagerInternal.class, mLocalService); 587 } 588 589 private final BroadcastReceiver mOnShutdown = new BroadcastReceiver() { 590 @Override 591 public void onReceive(Context context, Intent intent) { 592 if (mNightMode.get() == MODE_NIGHT_AUTO) { 593 persistComputedNightMode(mCurrentUser); 594 } 595 } 596 }; 597 persistComputedNightMode(int userId)598 private void persistComputedNightMode(int userId) { 599 Secure.putIntForUser(getContext().getContentResolver(), 600 Secure.UI_NIGHT_MODE_LAST_COMPUTED, mComputedNightMode ? 1 : 0, 601 userId); 602 } 603 604 private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() { 605 @Override 606 public void onReceive(Context context, Intent intent) { 607 List<String> settings = Arrays.asList( 608 Secure.UI_NIGHT_MODE, Secure.DARK_THEME_CUSTOM_START_TIME, 609 Secure.DARK_THEME_CUSTOM_END_TIME); 610 if (settings.contains(intent.getExtras().getCharSequence(Intent.EXTRA_SETTING_NAME))) { 611 synchronized (mLock) { 612 updateNightModeFromSettingsLocked(context, context.getResources(), 613 UserHandle.getCallingUserId()); 614 updateConfigurationLocked(); 615 } 616 } 617 } 618 }; 619 initPowerSave()620 private void initPowerSave() { 621 mPowerSave = 622 mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE) 623 .batterySaverEnabled; 624 mLocalPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE, state -> { 625 synchronized (mLock) { 626 if (mPowerSave == state.batterySaverEnabled) { 627 return; 628 } 629 mPowerSave = state.batterySaverEnabled; 630 if (mSystemReady) { 631 updateLocked(0, 0); 632 } 633 } 634 }); 635 } 636 637 @VisibleForTesting getService()638 protected IUiModeManager getService() { 639 return mService; 640 } 641 642 @VisibleForTesting getConfiguration()643 protected Configuration getConfiguration() { 644 return mConfiguration; 645 } 646 647 // Records whether setup wizard has happened or not and adds an observer for this user if not. verifySetupWizardCompleted()648 private void verifySetupWizardCompleted() { 649 final Context context = getContext(); 650 final int userId = UserHandle.getCallingUserId(); 651 if (!setupWizardCompleteForCurrentUser()) { 652 mSetupWizardComplete = false; 653 context.getContentResolver().registerContentObserver( 654 Secure.getUriFor( 655 Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId); 656 } else { 657 mSetupWizardComplete = true; 658 } 659 } 660 setupWizardCompleteForCurrentUser()661 private boolean setupWizardCompleteForCurrentUser() { 662 return Secure.getIntForUser(getContext().getContentResolver(), 663 Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1; 664 } 665 updateCustomTimeLocked()666 private void updateCustomTimeLocked() { 667 if (mNightMode.get() != MODE_NIGHT_CUSTOM) return; 668 if (shouldApplyAutomaticChangesImmediately()) { 669 updateLocked(0, 0); 670 } else { 671 registerDeviceInactiveListenerLocked(); 672 } 673 scheduleNextCustomTimeListener(); 674 } 675 676 /** 677 * Updates the night mode setting in Settings.Secure 678 * 679 * @param context A valid context 680 * @param res A valid resource object 681 * @param userId The user to update the setting for 682 */ updateNightModeFromSettingsLocked(Context context, Resources res, int userId)683 private void updateNightModeFromSettingsLocked(Context context, Resources res, int userId) { 684 if (mCarModeEnabled || mCar) { 685 return; 686 } 687 if (mSetupWizardComplete) { 688 mNightMode.set(Secure.getIntForUser(context.getContentResolver(), 689 Secure.UI_NIGHT_MODE, res.getInteger( 690 com.android.internal.R.integer.config_defaultNightMode), userId)); 691 mNightModeCustomType = Secure.getIntForUser(context.getContentResolver(), 692 Secure.UI_NIGHT_MODE_CUSTOM_TYPE, MODE_NIGHT_CUSTOM_TYPE_UNKNOWN, userId); 693 mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), 694 Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0; 695 mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(), 696 Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0; 697 mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay( 698 Secure.getLongForUser(context.getContentResolver(), 699 Secure.DARK_THEME_CUSTOM_START_TIME, 700 DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000); 701 mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay( 702 Secure.getLongForUser(context.getContentResolver(), 703 Secure.DARK_THEME_CUSTOM_END_TIME, 704 DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000); 705 if (mNightMode.get() == MODE_NIGHT_AUTO) { 706 mComputedNightMode = Secure.getIntForUser(context.getContentResolver(), 707 Secure.UI_NIGHT_MODE_LAST_COMPUTED, 0, userId) != 0; 708 } 709 } 710 } 711 toMilliSeconds(LocalTime t)712 private static long toMilliSeconds(LocalTime t) { 713 return t.toNanoOfDay() / 1000; 714 } 715 fromMilliseconds(long t)716 private static LocalTime fromMilliseconds(long t) { 717 return LocalTime.ofNanoOfDay(t * 1000); 718 } 719 registerDeviceInactiveListenerLocked()720 private void registerDeviceInactiveListenerLocked() { 721 if (mPowerSave) return; 722 mWaitForDeviceInactive = true; 723 final IntentFilter intentFilter = 724 new IntentFilter(Intent.ACTION_SCREEN_OFF); 725 intentFilter.addAction(Intent.ACTION_DREAMING_STARTED); 726 getContext().registerReceiver(mDeviceInactiveListener, intentFilter); 727 } 728 cancelCustomAlarm()729 private void cancelCustomAlarm() { 730 mAlarmManager.cancel(mCustomTimeListener); 731 } 732 unregisterDeviceInactiveListenerLocked()733 private void unregisterDeviceInactiveListenerLocked() { 734 mWaitForDeviceInactive = false; 735 try { 736 getContext().unregisterReceiver(mDeviceInactiveListener); 737 } catch (IllegalArgumentException e) { 738 // we ignore this exception if the receiver is unregistered already. 739 } 740 } 741 registerTimeChangeEvent()742 private void registerTimeChangeEvent() { 743 final IntentFilter intentFilter = 744 new IntentFilter(Intent.ACTION_TIME_CHANGED); 745 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 746 getContext().registerReceiver(mOnTimeChangedHandler, intentFilter); 747 } 748 unregisterTimeChangeEvent()749 private void unregisterTimeChangeEvent() { 750 try { 751 getContext().unregisterReceiver(mOnTimeChangedHandler); 752 } catch (IllegalArgumentException e) { 753 // we ignore this exception if the receiver is unregistered already. 754 } 755 } 756 757 private final class Stub extends IUiModeManager.Stub { Stub(Context context)758 Stub(Context context) { 759 super(PermissionEnforcer.fromContext(context)); 760 } 761 762 @Override addCallback(IUiModeManagerCallback callback)763 public void addCallback(IUiModeManagerCallback callback) { 764 int userId = getCallingUserId(); 765 synchronized (mLock) { 766 if (!mUiModeManagerCallbacks.contains(userId)) { 767 mUiModeManagerCallbacks.put(userId, new RemoteCallbackList<>()); 768 } 769 mUiModeManagerCallbacks.get(userId).register(callback); 770 } 771 } 772 773 @Override enableCarMode(@iModeManager.EnableCarMode int flags, @IntRange(from = 0) int priority, String callingPackage)774 public void enableCarMode(@UiModeManager.EnableCarMode int flags, 775 @IntRange(from = 0) int priority, String callingPackage) { 776 if (isUiModeLocked()) { 777 Slog.e(TAG, "enableCarMode while UI mode is locked"); 778 return; 779 } 780 781 if (priority != UiModeManager.DEFAULT_PRIORITY 782 && getContext().checkCallingOrSelfPermission( 783 android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) 784 != PackageManager.PERMISSION_GRANTED) { 785 throw new SecurityException("Enabling car mode with a priority requires " 786 + "permission ENTER_CAR_MODE_PRIORITIZED"); 787 } 788 789 // Allow the user to enable car mode using the shell, 790 // e.g. 'adb shell cmd uimode car yes' 791 boolean isShellCaller = mInjector.getCallingUid() == Process.SHELL_UID; 792 if (!isShellCaller) { 793 assertLegit(callingPackage); 794 } 795 796 final long ident = Binder.clearCallingIdentity(); 797 try { 798 synchronized (mLock) { 799 setCarModeLocked(true, flags, priority, callingPackage); 800 if (mSystemReady) { 801 updateLocked(flags, 0); 802 } 803 } 804 } finally { 805 Binder.restoreCallingIdentity(ident); 806 } 807 } 808 809 /** 810 * This method is only kept around for the time being; the AIDL has an UnsupportedAppUsage 811 * tag which means this method is technically considered part of the greylist "API". 812 * @param flags 813 */ 814 @Override disableCarMode(@iModeManager.DisableCarMode int flags)815 public void disableCarMode(@UiModeManager.DisableCarMode int flags) { 816 disableCarModeByCallingPackage(flags, null /* callingPackage */); 817 } 818 819 /** 820 * Handles requests to disable car mode. 821 * @param flags Disable car mode flags 822 * @param callingPackage 823 */ 824 @Override disableCarModeByCallingPackage(@iModeManager.DisableCarMode int flags, String callingPackage)825 public void disableCarModeByCallingPackage(@UiModeManager.DisableCarMode int flags, 826 String callingPackage) { 827 if (isUiModeLocked()) { 828 Slog.e(TAG, "disableCarMode while UI mode is locked"); 829 return; 830 } 831 832 // If the caller is the system, we will allow the DISABLE_CAR_MODE_ALL_PRIORITIES car 833 // mode flag to be specified; this is so that the user can disable car mode at all 834 // priorities using the persistent notification. 835 // 836 // We also allow the user to disable car mode using the shell, 837 // e.g. 'adb shell cmd uimode car no' 838 int callingUid = mInjector.getCallingUid(); 839 boolean isSystemCaller = callingUid == Process.SYSTEM_UID; 840 boolean isShellCaller = callingUid == Process.SHELL_UID; 841 if (!isSystemCaller && !isShellCaller) { 842 assertLegit(callingPackage); 843 } 844 final int carModeFlags = 845 isSystemCaller ? flags : flags & ~UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES; 846 847 final long ident = Binder.clearCallingIdentity(); 848 try { 849 synchronized (mLock) { 850 // Determine if the caller has enabled car mode at a priority other than the 851 // default one. If they have, then attempt to disable at that priority. 852 int priority = mCarModePackagePriority.entrySet() 853 .stream() 854 .filter(e -> e.getValue().equals(callingPackage)) 855 .findFirst() 856 .map(Map.Entry::getKey) 857 .orElse(UiModeManager.DEFAULT_PRIORITY); 858 859 setCarModeLocked(false, carModeFlags, priority, callingPackage); 860 if (mSystemReady) { 861 updateLocked(0, flags); 862 } 863 } 864 } finally { 865 Binder.restoreCallingIdentity(ident); 866 } 867 } 868 869 @Override getCurrentModeType()870 public int getCurrentModeType() { 871 final long ident = Binder.clearCallingIdentity(); 872 try { 873 synchronized (mLock) { 874 return mCurUiMode.get() & Configuration.UI_MODE_TYPE_MASK; 875 } 876 } finally { 877 Binder.restoreCallingIdentity(ident); 878 } 879 } 880 881 @Override setNightMode(int mode)882 public void setNightMode(int mode) { 883 // MODE_NIGHT_CUSTOM_TYPE_SCHEDULE is the default for MODE_NIGHT_CUSTOM. 884 int customModeType = mode == MODE_NIGHT_CUSTOM 885 ? MODE_NIGHT_CUSTOM_TYPE_SCHEDULE 886 : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; 887 setNightModeInternal(mode, customModeType); 888 } 889 setNightModeInternal(int mode, int customModeType)890 private void setNightModeInternal(int mode, int customModeType) { 891 if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( 892 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 893 != PackageManager.PERMISSION_GRANTED)) { 894 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); 895 return; 896 } 897 switch (mode) { 898 case UiModeManager.MODE_NIGHT_NO: 899 case UiModeManager.MODE_NIGHT_YES: 900 case MODE_NIGHT_AUTO: 901 break; 902 case MODE_NIGHT_CUSTOM: 903 if (SUPPORTED_NIGHT_MODE_CUSTOM_TYPES.contains(customModeType)) { 904 break; 905 } 906 throw new IllegalArgumentException( 907 "Can't set the custom type to " + customModeType); 908 default: 909 throw new IllegalArgumentException("Unknown mode: " + mode); 910 } 911 912 enforceCurrentUserIfVisibleBackgroundEnabled(); 913 914 final int user = UserHandle.getCallingUserId(); 915 final long ident = Binder.clearCallingIdentity(); 916 try { 917 synchronized (mLock) { 918 if (mNightMode.get() != mode || mNightModeCustomType != customModeType) { 919 if (mNightMode.get() == MODE_NIGHT_AUTO 920 || mNightMode.get() == MODE_NIGHT_CUSTOM) { 921 unregisterDeviceInactiveListenerLocked(); 922 cancelCustomAlarm(); 923 } 924 mNightModeCustomType = mode == MODE_NIGHT_CUSTOM 925 ? customModeType 926 : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; 927 mNightMode.set(mode); 928 resetNightModeOverrideLocked(); 929 persistNightMode(user); 930 // on screen off will update configuration instead 931 if ((mNightMode.get() != MODE_NIGHT_AUTO 932 && mNightMode.get() != MODE_NIGHT_CUSTOM) 933 || shouldApplyAutomaticChangesImmediately()) { 934 unregisterDeviceInactiveListenerLocked(); 935 updateLocked(0, 0); 936 } else { 937 registerDeviceInactiveListenerLocked(); 938 } 939 } 940 } 941 } finally { 942 Binder.restoreCallingIdentity(ident); 943 } 944 } 945 946 @Override getNightMode()947 public int getNightMode() { 948 synchronized (mLock) { 949 return mNightMode.get(); 950 } 951 } 952 953 @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 954 @Override setNightModeCustomType(@ightModeCustomType int nightModeCustomType)955 public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) { 956 setNightModeCustomType_enforcePermission(); 957 setNightModeInternal(MODE_NIGHT_CUSTOM, nightModeCustomType); 958 } 959 960 @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 961 @Override getNightModeCustomType()962 public @NightModeCustomReturnType int getNightModeCustomType() { 963 getNightModeCustomType_enforcePermission(); 964 synchronized (mLock) { 965 return mNightModeCustomType; 966 } 967 } 968 969 @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 970 @Override setAttentionModeThemeOverlay( @ttentionModeThemeOverlayType int attentionModeThemeOverlayType)971 public void setAttentionModeThemeOverlay( 972 @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) { 973 setAttentionModeThemeOverlay_enforcePermission(); 974 975 enforceCurrentUserIfVisibleBackgroundEnabled(); 976 977 synchronized (mLock) { 978 if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) { 979 mAttentionModeThemeOverlay = attentionModeThemeOverlayType; 980 Binder.withCleanCallingIdentity(()-> updateLocked(0, 0)); 981 } 982 } 983 } 984 985 @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 986 @Override getAttentionModeThemeOverlay()987 public @AttentionModeThemeOverlayType int getAttentionModeThemeOverlay() { 988 getAttentionModeThemeOverlay_enforcePermission(); 989 synchronized (mLock) { 990 return mAttentionModeThemeOverlay; 991 } 992 } 993 994 @Override setApplicationNightMode(@iModeManager.NightMode int mode)995 public void setApplicationNightMode(@UiModeManager.NightMode int mode) { 996 switch (mode) { 997 case UiModeManager.MODE_NIGHT_NO: 998 case UiModeManager.MODE_NIGHT_YES: 999 case UiModeManager.MODE_NIGHT_AUTO: 1000 case UiModeManager.MODE_NIGHT_CUSTOM: 1001 break; 1002 default: 1003 throw new IllegalArgumentException("Unknown mode: " + mode); 1004 } 1005 final int configNightMode; 1006 switch (mode) { 1007 case MODE_NIGHT_YES: 1008 configNightMode = Configuration.UI_MODE_NIGHT_YES; 1009 break; 1010 case MODE_NIGHT_NO: 1011 configNightMode = Configuration.UI_MODE_NIGHT_NO; 1012 break; 1013 default: 1014 configNightMode = Configuration.UI_MODE_NIGHT_UNDEFINED; 1015 } 1016 final ActivityTaskManagerInternal.PackageConfigurationUpdater updater = 1017 mActivityTaskManager.createPackageConfigurationUpdater(); 1018 updater.setNightMode(configNightMode); 1019 updater.commit(); 1020 } 1021 1022 @Override isUiModeLocked()1023 public boolean isUiModeLocked() { 1024 synchronized (mLock) { 1025 return mUiModeLocked; 1026 } 1027 } 1028 1029 @Override isNightModeLocked()1030 public boolean isNightModeLocked() { 1031 synchronized (mLock) { 1032 return mNightModeLocked; 1033 } 1034 } 1035 1036 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1037 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 1038 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 1039 new Shell(mService).exec(mService, in, out, err, args, callback, resultReceiver); 1040 } 1041 1042 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1043 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1044 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; 1045 dumpImpl(pw); 1046 } 1047 1048 @Override setNightModeActivatedForCustomMode(int modeNightCustomType, boolean active)1049 public boolean setNightModeActivatedForCustomMode(int modeNightCustomType, boolean active) { 1050 return setNightModeActivatedForModeInternal(modeNightCustomType, active, false); 1051 } 1052 1053 @Override setNightModeActivated(boolean active)1054 public boolean setNightModeActivated(boolean active) { 1055 return setNightModeActivatedForModeInternal(mNightModeCustomType, active, true); 1056 } 1057 setNightModeActivatedForModeInternal(int modeCustomType, boolean active, boolean isUserInteraction)1058 private boolean setNightModeActivatedForModeInternal(int modeCustomType, 1059 boolean active, boolean isUserInteraction) { 1060 if (getContext().checkCallingOrSelfPermission( 1061 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 1062 != PackageManager.PERMISSION_GRANTED) { 1063 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); 1064 return false; 1065 } 1066 final int user = Binder.getCallingUserHandle().getIdentifier(); 1067 if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( 1068 android.Manifest.permission.INTERACT_ACROSS_USERS) 1069 != PackageManager.PERMISSION_GRANTED) { 1070 Slog.e(TAG, "Target user is not current user," 1071 + " INTERACT_ACROSS_USERS permission is required"); 1072 return false; 1073 } 1074 1075 enforceCurrentUserIfVisibleBackgroundEnabled(); 1076 1077 // Store the last requested bedtime night mode state so that we don't need to notify 1078 // anyone if the user decides to switch to the night mode to bedtime. 1079 if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { 1080 mLastBedtimeRequestedNightMode = active; 1081 } 1082 if (modeCustomType != mNightModeCustomType) { 1083 return false; 1084 } 1085 synchronized (mLock) { 1086 final long ident = Binder.clearCallingIdentity(); 1087 try { 1088 if (mNightMode.get() == MODE_NIGHT_AUTO 1089 || mNightMode.get() == MODE_NIGHT_CUSTOM) { 1090 unregisterDeviceInactiveListenerLocked(); 1091 mOverrideNightModeOff = !active; 1092 mOverrideNightModeOn = active; 1093 mOverrideNightModeUser = mCurrentUser; 1094 persistNightModeOverrides(mCurrentUser); 1095 } else if (mNightMode.get() == UiModeManager.MODE_NIGHT_NO && active) { 1096 mNightMode.set(UiModeManager.MODE_NIGHT_YES); 1097 } else if (mNightMode.get() == UiModeManager.MODE_NIGHT_YES && !active) { 1098 mNightMode.set(UiModeManager.MODE_NIGHT_NO); 1099 } 1100 1101 if (isUserInteraction) { 1102 // deactivates AttentionMode if user toggles DarkTheme 1103 mAttentionModeThemeOverlay = MODE_ATTENTION_THEME_OVERLAY_OFF; 1104 } 1105 updateConfigurationLocked(); 1106 applyConfigurationExternallyLocked(); 1107 persistNightMode(mCurrentUser); 1108 return true; 1109 } finally { 1110 Binder.restoreCallingIdentity(ident); 1111 } 1112 } 1113 } 1114 1115 @Override getCustomNightModeStart()1116 public long getCustomNightModeStart() { 1117 return mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000; 1118 } 1119 1120 @Override setCustomNightModeStart(long time)1121 public void setCustomNightModeStart(long time) { 1122 if (isNightModeLocked() && getContext().checkCallingOrSelfPermission( 1123 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 1124 != PackageManager.PERMISSION_GRANTED) { 1125 Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission"); 1126 return; 1127 } 1128 1129 enforceCurrentUserIfVisibleBackgroundEnabled(); 1130 1131 final int user = UserHandle.getCallingUserId(); 1132 final long ident = Binder.clearCallingIdentity(); 1133 try { 1134 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); 1135 if (newTime == null) return; 1136 mCustomAutoNightModeStartMilliseconds = newTime; 1137 persistNightMode(user); 1138 onCustomTimeUpdated(user); 1139 } catch (DateTimeException e) { 1140 unregisterDeviceInactiveListenerLocked(); 1141 } finally { 1142 Binder.restoreCallingIdentity(ident); 1143 } 1144 } 1145 1146 @Override getCustomNightModeEnd()1147 public long getCustomNightModeEnd() { 1148 return mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000; 1149 } 1150 1151 @Override setCustomNightModeEnd(long time)1152 public void setCustomNightModeEnd(long time) { 1153 if (isNightModeLocked() && getContext().checkCallingOrSelfPermission( 1154 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 1155 != PackageManager.PERMISSION_GRANTED) { 1156 Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission"); 1157 return; 1158 } 1159 1160 enforceCurrentUserIfVisibleBackgroundEnabled(); 1161 1162 final int user = UserHandle.getCallingUserId(); 1163 final long ident = Binder.clearCallingIdentity(); 1164 try { 1165 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); 1166 if (newTime == null) return; 1167 mCustomAutoNightModeEndMilliseconds = newTime; 1168 onCustomTimeUpdated(user); 1169 } catch (DateTimeException e) { 1170 unregisterDeviceInactiveListenerLocked(); 1171 } finally { 1172 Binder.restoreCallingIdentity(ident); 1173 } 1174 } 1175 1176 @Override requestProjection(IBinder binder, @UiModeManager.ProjectionType int projectionType, @NonNull String callingPackage)1177 public boolean requestProjection(IBinder binder, 1178 @UiModeManager.ProjectionType int projectionType, 1179 @NonNull String callingPackage) { 1180 assertLegit(callingPackage); 1181 assertSingleProjectionType(projectionType); 1182 enforceProjectionTypePermissions(projectionType); 1183 enforceCurrentUserIfVisibleBackgroundEnabled(); 1184 1185 synchronized (mLock) { 1186 if (mProjectionHolders == null) { 1187 mProjectionHolders = new SparseArray<>(1); 1188 } 1189 if (!mProjectionHolders.contains(projectionType)) { 1190 mProjectionHolders.put(projectionType, new ArrayList<>(1)); 1191 } 1192 List<ProjectionHolder> currentHolders = mProjectionHolders.get(projectionType); 1193 1194 // For all projection types, it's a noop if already held. 1195 for (int i = 0; i < currentHolders.size(); ++i) { 1196 if (callingPackage.equals(currentHolders.get(i).mPackageName)) { 1197 return true; 1198 } 1199 } 1200 1201 // Enforce projection type-specific restrictions here. 1202 1203 // Automotive projection can only be set if it is currently unset. The case where it 1204 // is already set by the calling package is taken care of above. 1205 if (projectionType == PROJECTION_TYPE_AUTOMOTIVE && !currentHolders.isEmpty()) { 1206 return false; 1207 } 1208 1209 ProjectionHolder projectionHolder = new ProjectionHolder(callingPackage, 1210 projectionType, binder, 1211 UiModeManagerService.this::releaseProjectionUnchecked); 1212 if (!projectionHolder.linkToDeath()) { 1213 return false; 1214 } 1215 currentHolders.add(projectionHolder); 1216 Slog.d(TAG, "Package " + callingPackage + " set projection type " 1217 + projectionType + "."); 1218 onProjectionStateChangedLocked(projectionType); 1219 } 1220 return true; 1221 } 1222 1223 @Override releaseProjection(@iModeManager.ProjectionType int projectionType, @NonNull String callingPackage)1224 public boolean releaseProjection(@UiModeManager.ProjectionType int projectionType, 1225 @NonNull String callingPackage) { 1226 assertLegit(callingPackage); 1227 assertSingleProjectionType(projectionType); 1228 enforceProjectionTypePermissions(projectionType); 1229 enforceCurrentUserIfVisibleBackgroundEnabled(); 1230 1231 return releaseProjectionUnchecked(projectionType, callingPackage); 1232 } 1233 1234 @android.annotation.EnforcePermission(android.Manifest.permission.READ_PROJECTION_STATE) 1235 @Override getActiveProjectionTypes()1236 public @UiModeManager.ProjectionType int getActiveProjectionTypes() { 1237 getActiveProjectionTypes_enforcePermission(); 1238 @UiModeManager.ProjectionType int projectionTypeFlag = PROJECTION_TYPE_NONE; 1239 synchronized (mLock) { 1240 if (mProjectionHolders != null) { 1241 for (int i = 0; i < mProjectionHolders.size(); ++i) { 1242 if (!mProjectionHolders.valueAt(i).isEmpty()) { 1243 projectionTypeFlag = projectionTypeFlag | mProjectionHolders.keyAt(i); 1244 } 1245 } 1246 } 1247 } 1248 return projectionTypeFlag; 1249 } 1250 1251 @android.annotation.EnforcePermission(android.Manifest.permission.READ_PROJECTION_STATE) 1252 @Override getProjectingPackages( @iModeManager.ProjectionType int projectionType)1253 public List<String> getProjectingPackages( 1254 @UiModeManager.ProjectionType int projectionType) { 1255 getProjectingPackages_enforcePermission(); 1256 synchronized (mLock) { 1257 List<String> packageNames = new ArrayList<>(); 1258 populateWithRelevantActivePackageNames(projectionType, packageNames); 1259 return packageNames; 1260 } 1261 } 1262 1263 @android.annotation.EnforcePermission(android.Manifest.permission.READ_PROJECTION_STATE) addOnProjectionStateChangedListener(IOnProjectionStateChangedListener listener, @UiModeManager.ProjectionType int projectionType)1264 public void addOnProjectionStateChangedListener(IOnProjectionStateChangedListener listener, 1265 @UiModeManager.ProjectionType int projectionType) { 1266 addOnProjectionStateChangedListener_enforcePermission(); 1267 if (projectionType == PROJECTION_TYPE_NONE) { 1268 return; 1269 } 1270 1271 enforceCurrentUserIfVisibleBackgroundEnabled(); 1272 1273 synchronized (mLock) { 1274 if (mProjectionListeners == null) { 1275 mProjectionListeners = new SparseArray<>(1); 1276 } 1277 if (!mProjectionListeners.contains(projectionType)) { 1278 mProjectionListeners.put(projectionType, new RemoteCallbackList<>()); 1279 } 1280 if (mProjectionListeners.get(projectionType).register(listener)) { 1281 // If any of those types are active, send a callback immediately. 1282 List<String> packageNames = new ArrayList<>(); 1283 @UiModeManager.ProjectionType int activeProjectionTypes = 1284 populateWithRelevantActivePackageNames(projectionType, packageNames); 1285 if (!packageNames.isEmpty()) { 1286 try { 1287 listener.onProjectionStateChanged(activeProjectionTypes, packageNames); 1288 } catch (RemoteException e) { 1289 Slog.w(TAG, 1290 "Failed a call to onProjectionStateChanged() during listener " 1291 + "registration."); 1292 } 1293 } 1294 } 1295 } 1296 } 1297 1298 1299 @android.annotation.EnforcePermission(android.Manifest.permission.READ_PROJECTION_STATE) removeOnProjectionStateChangedListener( IOnProjectionStateChangedListener listener)1300 public void removeOnProjectionStateChangedListener( 1301 IOnProjectionStateChangedListener listener) { 1302 removeOnProjectionStateChangedListener_enforcePermission(); 1303 synchronized (mLock) { 1304 if (mProjectionListeners != null) { 1305 for (int i = 0; i < mProjectionListeners.size(); ++i) { 1306 mProjectionListeners.valueAt(i).unregister(listener); 1307 } 1308 } 1309 } 1310 } 1311 1312 @Override getContrast()1313 public float getContrast() { 1314 synchronized (mLock) { 1315 return getContrastLocked(); 1316 } 1317 } 1318 1319 @Override 1320 @ForceInvertType getForceInvertState()1321 public int getForceInvertState() { 1322 synchronized (mLock) { 1323 return getForceInvertStateLocked(); 1324 } 1325 } 1326 }; 1327 enforceProjectionTypePermissions(@iModeManager.ProjectionType int p)1328 private void enforceProjectionTypePermissions(@UiModeManager.ProjectionType int p) { 1329 if ((p & PROJECTION_TYPE_AUTOMOTIVE) != 0) { 1330 getContext().enforceCallingPermission( 1331 android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, 1332 "toggleProjection"); 1333 } 1334 } 1335 assertSingleProjectionType(@iModeManager.ProjectionType int p)1336 private static void assertSingleProjectionType(@UiModeManager.ProjectionType int p) { 1337 // To be a single projection type it must be non-zero and an exact power of two. 1338 boolean projectionTypeIsPowerOfTwoOrZero = (p & p - 1) == 0; 1339 if (p == 0 || !projectionTypeIsPowerOfTwoOrZero) { 1340 throw new IllegalArgumentException("Must specify exactly one projection type."); 1341 } 1342 } 1343 toPackageNameList(Collection<ProjectionHolder> c)1344 private static List<String> toPackageNameList(Collection<ProjectionHolder> c) { 1345 List<String> packageNames = new ArrayList<>(); 1346 for (ProjectionHolder p : c) { 1347 packageNames.add(p.mPackageName); 1348 } 1349 return packageNames; 1350 } 1351 1352 /** 1353 * Populates a list with the package names that have set any of the given projection types. 1354 * @param projectionType the projection types to include 1355 * @param packageNames the list to populate with package names 1356 * @return the active projection types 1357 */ 1358 @GuardedBy("mLock") 1359 @UiModeManager.ProjectionType populateWithRelevantActivePackageNames( @iModeManager.ProjectionType int projectionType, List<String> packageNames)1360 private int populateWithRelevantActivePackageNames( 1361 @UiModeManager.ProjectionType int projectionType, List<String> packageNames) { 1362 packageNames.clear(); 1363 @UiModeManager.ProjectionType int projectionTypeFlag = PROJECTION_TYPE_NONE; 1364 if (mProjectionHolders != null) { 1365 for (int i = 0; i < mProjectionHolders.size(); ++i) { 1366 int key = mProjectionHolders.keyAt(i); 1367 List<ProjectionHolder> holders = mProjectionHolders.valueAt(i); 1368 if ((projectionType & key) != 0) { 1369 if (packageNames.addAll(toPackageNameList(holders))) { 1370 projectionTypeFlag = projectionTypeFlag | key; 1371 } 1372 } 1373 } 1374 } 1375 return projectionTypeFlag; 1376 } 1377 releaseProjectionUnchecked(@iModeManager.ProjectionType int projectionType, @NonNull String pkg)1378 private boolean releaseProjectionUnchecked(@UiModeManager.ProjectionType int projectionType, 1379 @NonNull String pkg) { 1380 synchronized (mLock) { 1381 boolean removed = false; 1382 if (mProjectionHolders != null) { 1383 List<ProjectionHolder> holders = mProjectionHolders.get(projectionType); 1384 if (holders != null) { 1385 // Iterate backward so we can safely remove while iterating. 1386 for (int i = holders.size() - 1; i >= 0; --i) { 1387 ProjectionHolder holder = holders.get(i); 1388 if (pkg.equals(holder.mPackageName)) { 1389 holder.unlinkToDeath(); 1390 Slog.d(TAG, "Projection type " + projectionType + " released by " 1391 + pkg + "."); 1392 holders.remove(i); 1393 removed = true; 1394 } 1395 } 1396 } 1397 } 1398 if (removed) { 1399 onProjectionStateChangedLocked(projectionType); 1400 } else { 1401 Slog.w(TAG, pkg + " tried to release projection type " + projectionType 1402 + " but was not set by that package."); 1403 } 1404 return removed; 1405 } 1406 } 1407 1408 /** 1409 * Return the force invert for the current user. If not cached, fetch it from the settings. 1410 */ 1411 @GuardedBy("mLock") 1412 @ForceInvertType getForceInvertStateLocked()1413 private int getForceInvertStateLocked() { 1414 if (mForceInvertStates.indexOfKey(mCurrentUser) < 0 && mSystemReady) { 1415 updateForceInvertStateLocked(); 1416 } 1417 return mForceInvertStates.get(mCurrentUser, FORCE_INVERT_TYPE_OFF); 1418 } 1419 1420 /** 1421 * Read the force invert setting for the current user and update {@link #mForceInvertStates} 1422 * if the contrast changed. Returns true if {@link #mForceInvertStates} was updated. 1423 */ 1424 @GuardedBy("mLock") updateForceInvertStateLocked()1425 private boolean updateForceInvertStateLocked() { 1426 int forceInvertState = getForceInvertStateInternal(); 1427 if (mForceInvertStates.get(mCurrentUser, Integer.MIN_VALUE) != forceInvertState) { 1428 mForceInvertStates.put(mCurrentUser, forceInvertState); 1429 return true; 1430 } 1431 return false; 1432 } 1433 1434 /** 1435 * Returns the current state of force invert, which modifies the display colors to 1436 * enhance visibility based on the system's dark theme settings and app-specific configurations. 1437 * 1438 * <p>This method is for informational purposes only. The application does not need to 1439 * implement any special handling for force invert; the system applies it automatically. 1440 * If you want to prevent force invert from affecting your app, ensure you have defined 1441 * both light and dark themes. Force invert is not applied to apps that already adapt 1442 * to the user's system theme preference.</p> 1443 * 1444 * @return The current force invert state, represented by a {@code ForceDarkType} constant. 1445 * 1446 * @hide 1447 */ getForceInvertStateInternal()1448 private int getForceInvertStateInternal() { 1449 if (!android.view.accessibility.Flags.forceInvertColor()) { 1450 return FORCE_INVERT_TYPE_OFF; 1451 } 1452 1453 if (!isSystemInDarkTheme()) { 1454 return FORCE_INVERT_TYPE_OFF; 1455 } 1456 1457 if (!isForceInvert()) { 1458 return FORCE_INVERT_TYPE_OFF; 1459 } 1460 1461 return FORCE_INVERT_TYPE_DARK; 1462 } 1463 isSystemInDarkTheme()1464 private boolean isSystemInDarkTheme() { 1465 Context sysUiContext = ActivityThread.currentActivityThread().getSystemUiContext(); 1466 int sysUiNightMode = sysUiContext.getResources().getConfiguration().uiMode 1467 & Configuration.UI_MODE_NIGHT_MASK; 1468 return sysUiNightMode == Configuration.UI_MODE_NIGHT_YES; 1469 } 1470 isForceInvert()1471 private boolean isForceInvert() { 1472 return Settings.Secure.getIntForUser( 1473 getContext().getContentResolver(), 1474 Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, 1475 /* def= */ 0, mCurrentUser) == 1; 1476 } 1477 1478 /** 1479 * Return the contrast for the current user. If not cached, fetch it from the settings. 1480 */ 1481 @GuardedBy("mLock") getContrastLocked()1482 private float getContrastLocked() { 1483 if (!mContrasts.contains(mCurrentUser)) updateContrastLocked(); 1484 return mContrasts.get(mCurrentUser); 1485 } 1486 1487 /** 1488 * Read the contrast setting for the current user and update {@link #mContrasts} 1489 * if the contrast changed. Returns true if {@link #mContrasts} was updated. 1490 */ 1491 @GuardedBy("mLock") updateContrastLocked()1492 private boolean updateContrastLocked() { 1493 float contrast = Settings.Secure.getFloatForUser(getContext().getContentResolver(), 1494 CONTRAST_LEVEL, CONTRAST_DEFAULT_VALUE, mCurrentUser); 1495 if (Math.abs(mContrasts.get(mCurrentUser, Float.MAX_VALUE) - contrast) >= 1e-10) { 1496 mContrasts.put(mCurrentUser, contrast); 1497 return true; 1498 } 1499 return false; 1500 } 1501 1502 private static class ProjectionHolder implements IBinder.DeathRecipient { 1503 private final String mPackageName; 1504 private final @UiModeManager.ProjectionType int mProjectionType; 1505 private final IBinder mBinder; 1506 private final ProjectionReleaser mProjectionReleaser; 1507 ProjectionHolder(String packageName, @UiModeManager.ProjectionType int projectionType, IBinder binder, ProjectionReleaser projectionReleaser)1508 private ProjectionHolder(String packageName, 1509 @UiModeManager.ProjectionType int projectionType, IBinder binder, 1510 ProjectionReleaser projectionReleaser) { 1511 mPackageName = packageName; 1512 mProjectionType = projectionType; 1513 mBinder = binder; 1514 mProjectionReleaser = projectionReleaser; 1515 } 1516 linkToDeath()1517 private boolean linkToDeath() { 1518 try { 1519 mBinder.linkToDeath(this, 0); 1520 } catch (RemoteException e) { 1521 Slog.e(TAG, "linkToDeath failed for projection requester: " + mPackageName + ".", 1522 e); 1523 return false; 1524 } 1525 return true; 1526 } 1527 unlinkToDeath()1528 private void unlinkToDeath() { 1529 mBinder.unlinkToDeath(this, 0); 1530 } 1531 1532 @Override binderDied()1533 public void binderDied() { 1534 Slog.w(TAG, "Projection holder " + mPackageName 1535 + " died. Releasing projection type " + mProjectionType + "."); 1536 mProjectionReleaser.release(mProjectionType, mPackageName); 1537 } 1538 1539 private interface ProjectionReleaser { release(@iModeManager.ProjectionType int projectionType, @NonNull String packageName)1540 boolean release(@UiModeManager.ProjectionType int projectionType, 1541 @NonNull String packageName); 1542 } 1543 } 1544 assertLegit(@onNull String packageName)1545 private void assertLegit(@NonNull String packageName) { 1546 if (!doesPackageHaveCallingUid(packageName)) { 1547 throw new SecurityException("Caller claimed bogus packageName: " + packageName + "."); 1548 } 1549 } 1550 doesPackageHaveCallingUid(@onNull String packageName)1551 private boolean doesPackageHaveCallingUid(@NonNull String packageName) { 1552 int callingUid = mInjector.getCallingUid(); 1553 int callingUserId = UserHandle.getUserId(callingUid); 1554 final long ident = Binder.clearCallingIdentity(); 1555 try { 1556 return getContext().getPackageManager().getPackageUidAsUser(packageName, 1557 callingUserId) == callingUid; 1558 } catch (PackageManager.NameNotFoundException e) { 1559 return false; 1560 } finally { 1561 Binder.restoreCallingIdentity(ident); 1562 } 1563 } 1564 1565 @GuardedBy("mLock") onProjectionStateChangedLocked( @iModeManager.ProjectionType int changedProjectionType)1566 private void onProjectionStateChangedLocked( 1567 @UiModeManager.ProjectionType int changedProjectionType) { 1568 if (mProjectionListeners == null) { 1569 return; 1570 } 1571 for (int i = 0; i < mProjectionListeners.size(); ++i) { 1572 int listenerProjectionType = mProjectionListeners.keyAt(i); 1573 // Every listener that is affected must be called back with all the state they are 1574 // listening for. 1575 if ((changedProjectionType & listenerProjectionType) != 0) { 1576 RemoteCallbackList<IOnProjectionStateChangedListener> listeners = 1577 mProjectionListeners.valueAt(i); 1578 List<String> packageNames = new ArrayList<>(); 1579 @UiModeManager.ProjectionType int activeProjectionTypes = 1580 populateWithRelevantActivePackageNames(listenerProjectionType, 1581 packageNames); 1582 int listenerCount = listeners.beginBroadcast(); 1583 for (int j = 0; j < listenerCount; ++j) { 1584 try { 1585 listeners.getBroadcastItem(j).onProjectionStateChanged( 1586 activeProjectionTypes, packageNames); 1587 } catch (RemoteException e) { 1588 Slog.w(TAG, "Failed a call to onProjectionStateChanged()."); 1589 } 1590 } 1591 listeners.finishBroadcast(); 1592 } 1593 } 1594 } 1595 onCustomTimeUpdated(int user)1596 private void onCustomTimeUpdated(int user) { 1597 persistNightMode(user); 1598 if (mNightMode.get() != MODE_NIGHT_CUSTOM) return; 1599 if (shouldApplyAutomaticChangesImmediately()) { 1600 unregisterDeviceInactiveListenerLocked(); 1601 updateLocked(0, 0); 1602 } else { 1603 registerDeviceInactiveListenerLocked(); 1604 } 1605 } 1606 dumpImpl(PrintWriter pw)1607 void dumpImpl(PrintWriter pw) { 1608 synchronized (mLock) { 1609 pw.println("Current UI Mode Service state:"); 1610 pw.print(" mDockState="); pw.print(mDockState); 1611 pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); 1612 1613 pw.print(" mStartDreamImmediatelyOnDock="); pw.print(mStartDreamImmediatelyOnDock); 1614 1615 pw.print(" mNightMode="); pw.print(mNightMode.get()); pw.print(" ("); 1616 pw.print(Shell.nightModeToStr(mNightMode.get(), mNightModeCustomType)); pw.print(") "); 1617 pw.print(" mOverrideOn/Off="); pw.print(mOverrideNightModeOn); 1618 pw.print("/"); pw.print(mOverrideNightModeOff); 1619 pw.print(" mAttentionModeThemeOverlay="); pw.print(mAttentionModeThemeOverlay); 1620 pw.print(" mNightModeLocked="); pw.println(mNightModeLocked); 1621 1622 pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); 1623 pw.print(" (carModeApps="); 1624 for (Map.Entry<Integer, String> entry : mCarModePackagePriority.entrySet()) { 1625 pw.print(entry.getKey()); 1626 pw.print(":"); 1627 pw.print(entry.getValue()); 1628 pw.print(" "); 1629 } 1630 pw.println(""); 1631 pw.print(" mWaitForDeviceInactive="); pw.print(mWaitForDeviceInactive); 1632 pw.print(" mComputedNightMode="); pw.print(mComputedNightMode); 1633 pw.print(" customStart="); pw.print(mCustomAutoNightModeStartMilliseconds); 1634 pw.print(" customEnd"); pw.print(mCustomAutoNightModeEndMilliseconds); 1635 pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags); 1636 pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch); 1637 1638 pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode.get())); 1639 pw.print(" mUiModeLocked="); pw.print(mUiModeLocked); 1640 pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); 1641 1642 pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); 1643 pw.print(" mSystemReady="); pw.println(mSystemReady); 1644 1645 if (mTwilightManager != null) { 1646 // We may not have a TwilightManager. 1647 pw.print(" mTwilightService.getLastTwilightState()="); 1648 pw.println(mTwilightManager.getLastTwilightState()); 1649 } 1650 } 1651 } 1652 1653 /** 1654 * Updates the global car mode state. 1655 * The device is considered to be in car mode if there exists an app at any priority level which 1656 * has entered car mode. 1657 * 1658 * @param enabled {@code true} if the caller wishes to enable car mode, {@code false} otherwise. 1659 * @param flags Flags used when enabling/disabling car mode. 1660 * @param priority The priority level for entering or exiting car mode; defaults to 1661 * {@link UiModeManager#DEFAULT_PRIORITY} for callers using 1662 * {@link UiModeManager#enableCarMode(int)}. Callers using 1663 * {@link UiModeManager#enableCarMode(int, int)} may specify a priority. 1664 * @param packageName The package name of the app which initiated the request to enable or 1665 * disable car mode. 1666 */ setCarModeLocked(boolean enabled, int flags, int priority, String packageName)1667 void setCarModeLocked(boolean enabled, int flags, int priority, String packageName) { 1668 if (enabled) { 1669 enableCarMode(priority, packageName); 1670 } else { 1671 disableCarMode(flags, priority, packageName); 1672 } 1673 boolean isCarModeNowEnabled = isCarModeEnabled(); 1674 1675 if (mCarModeEnabled != isCarModeNowEnabled) { 1676 mCarModeEnabled = isCarModeNowEnabled; 1677 // When exiting car mode, restore night mode from settings 1678 if (!isCarModeNowEnabled) { 1679 Context context = getContext(); 1680 updateNightModeFromSettingsLocked(context, 1681 context.getResources(), 1682 UserHandle.getCallingUserId()); 1683 } 1684 } 1685 mCarModeEnableFlags = flags; 1686 } 1687 1688 /** 1689 * Handles disabling car mode. 1690 * <p> 1691 * Car mode can be disabled at a priority level if any of the following is true: 1692 * 1. The priority being disabled is the {@link UiModeManager#DEFAULT_PRIORITY}. 1693 * 2. The priority level is enabled and the caller is the app who originally enabled it. 1694 * 3. The {@link UiModeManager#DISABLE_CAR_MODE_ALL_PRIORITIES} flag was specified, meaning all 1695 * car mode priorities are disabled. 1696 * 1697 * @param flags Car mode flags. 1698 * @param priority The priority level at which to disable car mode. 1699 * @param packageName The calling package which initiated the request. 1700 */ disableCarMode(int flags, int priority, String packageName)1701 private void disableCarMode(int flags, int priority, String packageName) { 1702 boolean isDisableAll = (flags & UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES) != 0; 1703 boolean isPriorityTracked = mCarModePackagePriority.keySet().contains(priority); 1704 boolean isDefaultPriority = priority == UiModeManager.DEFAULT_PRIORITY; 1705 boolean isChangeAllowed = 1706 // Anyone can disable the default priority. 1707 isDefaultPriority 1708 // If priority was enabled, only enabling package can disable it. 1709 || isPriorityTracked && mCarModePackagePriority.get(priority).equals( 1710 packageName) 1711 // Disable all priorities flag can disable all regardless. 1712 || isDisableAll; 1713 if (isChangeAllowed) { 1714 Slog.d(TAG, "disableCarMode: disabling, priority=" + priority 1715 + ", packageName=" + packageName); 1716 if (isDisableAll) { 1717 Set<Map.Entry<Integer, String>> entries = 1718 new ArraySet<>(mCarModePackagePriority.entrySet()); 1719 mCarModePackagePriority.clear(); 1720 1721 for (Map.Entry<Integer, String> entry : entries) { 1722 notifyCarModeDisabled(entry.getKey(), entry.getValue()); 1723 } 1724 } else { 1725 mCarModePackagePriority.remove(priority); 1726 notifyCarModeDisabled(priority, packageName); 1727 } 1728 } 1729 } 1730 1731 /** 1732 * Handles enabling car mode. 1733 * <p> 1734 * Car mode can be enabled at any priority if it has not already been enabled at that priority. 1735 * The calling package is tracked for the first app which enters priority at the 1736 * {@link UiModeManager#DEFAULT_PRIORITY}, though any app can disable it at that priority. 1737 * 1738 * @param priority The priority for enabling car mode. 1739 * @param packageName The calling package which initiated the request. 1740 */ enableCarMode(int priority, String packageName)1741 private void enableCarMode(int priority, String packageName) { 1742 boolean isPriorityTracked = mCarModePackagePriority.containsKey(priority); 1743 boolean isPackagePresent = mCarModePackagePriority.containsValue(packageName); 1744 if (!isPriorityTracked && !isPackagePresent) { 1745 Slog.d(TAG, "enableCarMode: enabled at priority=" + priority + ", packageName=" 1746 + packageName); 1747 mCarModePackagePriority.put(priority, packageName); 1748 notifyCarModeEnabled(priority, packageName); 1749 } else { 1750 Slog.d(TAG, "enableCarMode: car mode at priority " + priority + " already enabled."); 1751 } 1752 1753 } 1754 notifyCarModeEnabled(int priority, String packageName)1755 private void notifyCarModeEnabled(int priority, String packageName) { 1756 Intent intent = new Intent(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED); 1757 intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName); 1758 intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority); 1759 getContext().sendBroadcastAsUser(intent, UserHandle.ALL, 1760 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES); 1761 } 1762 notifyCarModeDisabled(int priority, String packageName)1763 private void notifyCarModeDisabled(int priority, String packageName) { 1764 Intent intent = new Intent(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED); 1765 intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName); 1766 intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority); 1767 getContext().sendBroadcastAsUser(intent, UserHandle.ALL, 1768 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES); 1769 } 1770 1771 /** 1772 * Determines if car mode is enabled at any priority level. 1773 * @return {@code true} if car mode is enabled, {@code false} otherwise. 1774 */ isCarModeEnabled()1775 private boolean isCarModeEnabled() { 1776 return mCarModePackagePriority.size() > 0; 1777 } 1778 updateDockState(int newState)1779 private void updateDockState(int newState) { 1780 synchronized (mLock) { 1781 if (newState != mDockState) { 1782 mDockState = newState; 1783 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0, 1784 UiModeManager.DEFAULT_PRIORITY, "" /* packageName */); 1785 if (mSystemReady) { 1786 updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0); 1787 } 1788 } 1789 } 1790 } 1791 isDeskDockState(int state)1792 private static boolean isDeskDockState(int state) { 1793 switch (state) { 1794 case Intent.EXTRA_DOCK_STATE_DESK: 1795 case Intent.EXTRA_DOCK_STATE_LE_DESK: 1796 case Intent.EXTRA_DOCK_STATE_HE_DESK: 1797 return true; 1798 default: 1799 return false; 1800 } 1801 } 1802 persistNightMode(int user)1803 private void persistNightMode(int user) { 1804 // Only persist setting if not in car mode 1805 if (mCarModeEnabled || mCar) return; 1806 Secure.putIntForUser(getContext().getContentResolver(), 1807 Secure.UI_NIGHT_MODE, mNightMode.get(), user); 1808 Secure.putLongForUser(getContext().getContentResolver(), 1809 Secure.UI_NIGHT_MODE_CUSTOM_TYPE, mNightModeCustomType, user); 1810 Secure.putLongForUser(getContext().getContentResolver(), 1811 Secure.DARK_THEME_CUSTOM_START_TIME, 1812 mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user); 1813 Secure.putLongForUser(getContext().getContentResolver(), 1814 Secure.DARK_THEME_CUSTOM_END_TIME, 1815 mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000, user); 1816 } 1817 persistNightModeOverrides(int user)1818 private void persistNightModeOverrides(int user) { 1819 // Only persist setting if not in car mode 1820 if (mCarModeEnabled || mCar) return; 1821 Secure.putIntForUser(getContext().getContentResolver(), 1822 Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user); 1823 Secure.putIntForUser(getContext().getContentResolver(), 1824 Secure.UI_NIGHT_MODE_OVERRIDE_OFF, mOverrideNightModeOff ? 1 : 0, user); 1825 } 1826 updateConfigurationLocked()1827 private void updateConfigurationLocked() { 1828 int uiMode = mDefaultUiModeType; 1829 if (mUiModeLocked) { 1830 // no-op, keeps default one 1831 } else if (mTelevision) { 1832 uiMode = Configuration.UI_MODE_TYPE_TELEVISION; 1833 } else if (mWatch) { 1834 uiMode = Configuration.UI_MODE_TYPE_WATCH; 1835 } else if (mCarModeEnabled) { 1836 uiMode = Configuration.UI_MODE_TYPE_CAR; 1837 } else if (isDeskDockState(mDockState)) { 1838 uiMode = Configuration.UI_MODE_TYPE_DESK; 1839 } else if (mVrHeadset) { 1840 uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET; 1841 } 1842 1843 if (mNightMode.get() == MODE_NIGHT_YES || mNightMode.get() == UiModeManager.MODE_NIGHT_NO) { 1844 updateComputedNightModeLocked(mNightMode.get() == MODE_NIGHT_YES); 1845 } 1846 1847 if (mNightMode.get() == MODE_NIGHT_AUTO) { 1848 boolean activateNightMode = mComputedNightMode; 1849 if (mTwilightManager != null) { 1850 mTwilightManager.registerListener(mTwilightListener, mHandler); 1851 final TwilightState lastState = mTwilightManager.getLastTwilightState(); 1852 activateNightMode = lastState == null ? mComputedNightMode : lastState.isNight(); 1853 } 1854 updateComputedNightModeLocked(activateNightMode); 1855 } else { 1856 if (mTwilightManager != null) { 1857 mTwilightManager.unregisterListener(mTwilightListener); 1858 } 1859 } 1860 1861 if (mNightMode.get() == MODE_NIGHT_CUSTOM) { 1862 if (mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { 1863 updateComputedNightModeLocked(mLastBedtimeRequestedNightMode); 1864 } else { 1865 registerTimeChangeEvent(); 1866 final boolean activate = computeCustomNightMode(); 1867 updateComputedNightModeLocked(activate); 1868 scheduleNextCustomTimeListener(); 1869 } 1870 } else { 1871 unregisterTimeChangeEvent(); 1872 } 1873 1874 // Override night mode in power save mode if not in car mode 1875 if (mPowerSave && !mCarModeEnabled && !mCar) { 1876 uiMode &= ~Configuration.UI_MODE_NIGHT_NO; 1877 uiMode |= Configuration.UI_MODE_NIGHT_YES; 1878 } else { 1879 uiMode = getComputedUiModeConfiguration(uiMode); 1880 } 1881 1882 if (LOG) { 1883 Slog.d(TAG, 1884 "updateConfigurationLocked: mDockState=" + mDockState 1885 + "; mCarMode=" + mCarModeEnabled 1886 + "; mNightMode=" + mNightMode 1887 + "; mNightModeCustomType=" + mNightModeCustomType 1888 + "; uiMode=" + uiMode); 1889 } 1890 1891 mCurUiMode.set(uiMode); 1892 if (!mHoldingConfiguration && (!mWaitForDeviceInactive || mPowerSave)) { 1893 mConfiguration.uiMode = uiMode; 1894 } 1895 } 1896 1897 @UiModeManager.NightMode getComputedUiModeConfiguration(int uiMode)1898 private int getComputedUiModeConfiguration(int uiMode) { 1899 uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES 1900 : Configuration.UI_MODE_NIGHT_NO; 1901 uiMode &= mComputedNightMode ? ~Configuration.UI_MODE_NIGHT_NO 1902 : ~Configuration.UI_MODE_NIGHT_YES; 1903 return uiMode; 1904 } 1905 computeCustomNightMode()1906 private boolean computeCustomNightMode() { 1907 return isTimeBetween(LocalTime.now(), 1908 mCustomAutoNightModeStartMilliseconds, 1909 mCustomAutoNightModeEndMilliseconds); 1910 } 1911 applyConfigurationExternallyLocked()1912 private void applyConfigurationExternallyLocked() { 1913 if (mSetUiMode != mConfiguration.uiMode) { 1914 mSetUiMode = mConfiguration.uiMode; 1915 // load splash screen instead of screenshot 1916 mWindowManager.clearSnapshotCache(); 1917 try { 1918 ActivityTaskManager.getService().updateConfiguration(mConfiguration); 1919 } catch (RemoteException e) { 1920 Slog.w(TAG, "Failure communicating with activity manager", e); 1921 } catch (SecurityException e) { 1922 Slog.e(TAG, "Activity does not have the ", e); 1923 } 1924 } 1925 } 1926 shouldApplyAutomaticChangesImmediately()1927 private boolean shouldApplyAutomaticChangesImmediately() { 1928 return mCar || !mPowerManager.isInteractive() 1929 || mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME 1930 || mDreamManagerInternal.isDreaming(); 1931 } 1932 scheduleNextCustomTimeListener()1933 private void scheduleNextCustomTimeListener() { 1934 cancelCustomAlarm(); 1935 LocalDateTime now = LocalDateTime.now(); 1936 final boolean active = computeCustomNightMode(); 1937 final LocalDateTime next = active 1938 ? getDateTimeAfter(mCustomAutoNightModeEndMilliseconds, now) 1939 : getDateTimeAfter(mCustomAutoNightModeStartMilliseconds, now); 1940 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); 1941 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, mCustomTimeListener, null); 1942 } 1943 getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime)1944 private LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) { 1945 final LocalDateTime ldt = LocalDateTime.of(compareTime.toLocalDate(), localTime); 1946 1947 // Check if the local time has passed, if so return the same time tomorrow. 1948 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; 1949 } 1950 updateLocked(int enableFlags, int disableFlags)1951 void updateLocked(int enableFlags, int disableFlags) { 1952 String action = null; 1953 String oldAction = null; 1954 if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { 1955 adjustStatusBarCarModeLocked(); 1956 oldAction = UiModeManager.ACTION_EXIT_CAR_MODE; 1957 } else if (isDeskDockState(mLastBroadcastState)) { 1958 oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; 1959 } 1960 1961 if (mCarModeEnabled) { 1962 if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) { 1963 adjustStatusBarCarModeLocked(); 1964 if (oldAction != null) { 1965 sendForegroundBroadcastToAllUsers(oldAction); 1966 } 1967 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; 1968 action = UiModeManager.ACTION_ENTER_CAR_MODE; 1969 } 1970 } else if (isDeskDockState(mDockState)) { 1971 if (!isDeskDockState(mLastBroadcastState)) { 1972 if (oldAction != null) { 1973 sendForegroundBroadcastToAllUsers(oldAction); 1974 } 1975 mLastBroadcastState = mDockState; 1976 action = UiModeManager.ACTION_ENTER_DESK_MODE; 1977 } 1978 } else { 1979 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 1980 action = oldAction; 1981 } 1982 1983 if (action != null) { 1984 if (LOG) { 1985 Slog.v(TAG, String.format( 1986 "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x", 1987 action, enableFlags, disableFlags)); 1988 } 1989 1990 // Send the ordered broadcast; the result receiver will receive after all 1991 // broadcasts have been sent. If any broadcast receiver changes the result 1992 // code from the initial value of RESULT_OK, then the result receiver will 1993 // not launch the corresponding dock application. This gives apps a chance 1994 // to override the behavior and stay in their app even when the device is 1995 // placed into a dock. 1996 Intent intent = new Intent(action); 1997 intent.putExtra("enableFlags", enableFlags); 1998 intent.putExtra("disableFlags", disableFlags); 1999 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 2000 getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 2001 mResultReceiver, null, Activity.RESULT_OK, null, null); 2002 2003 // Attempting to make this transition a little more clean, we are going 2004 // to hold off on doing a configuration change until we have finished 2005 // the broadcast and started the home activity. 2006 mHoldingConfiguration = true; 2007 updateConfigurationLocked(); 2008 } else { 2009 String category = null; 2010 if (mCarModeEnabled) { 2011 if (mEnableCarDockLaunch 2012 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 2013 category = Intent.CATEGORY_CAR_DOCK; 2014 } 2015 } else if (isDeskDockState(mDockState)) { 2016 if (ENABLE_LAUNCH_DESK_DOCK_APP 2017 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 2018 category = Intent.CATEGORY_DESK_DOCK; 2019 } 2020 } else { 2021 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { 2022 category = Intent.CATEGORY_HOME; 2023 } 2024 } 2025 2026 if (LOG) { 2027 Slog.v(TAG, "updateLocked: null action, mDockState=" 2028 + mDockState +", category=" + category); 2029 } 2030 2031 sendConfigurationAndStartDreamOrDockAppLocked(category); 2032 } 2033 2034 // keep screen on when charging and in car mode 2035 boolean keepScreenOn = mCharging && 2036 ((mCarModeEnabled && mCarModeKeepsScreenOn && 2037 (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) || 2038 (mCurUiMode.get() == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn)); 2039 if (keepScreenOn != mWakeLock.isHeld()) { 2040 if (keepScreenOn) { 2041 mWakeLock.acquire(); 2042 } else { 2043 mWakeLock.release(); 2044 } 2045 } 2046 } 2047 sendForegroundBroadcastToAllUsers(String action)2048 private void sendForegroundBroadcastToAllUsers(String action) { 2049 getContext().sendBroadcastAsUser(new Intent(action) 2050 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL); 2051 } 2052 updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags)2053 private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) { 2054 // Launch a dock activity 2055 String category = null; 2056 if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) { 2057 // Only launch car home when car mode is enabled and the caller 2058 // has asked us to switch to it. 2059 if (mEnableCarDockLaunch 2060 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 2061 category = Intent.CATEGORY_CAR_DOCK; 2062 } 2063 } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) { 2064 // Only launch car home when desk mode is enabled and the caller 2065 // has asked us to switch to it. Currently re-using the car 2066 // mode flag since we don't have a formal API for "desk mode". 2067 if (ENABLE_LAUNCH_DESK_DOCK_APP 2068 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 2069 category = Intent.CATEGORY_DESK_DOCK; 2070 } 2071 } else { 2072 // Launch the standard home app if requested. 2073 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { 2074 category = Intent.CATEGORY_HOME; 2075 } 2076 } 2077 2078 if (LOG) { 2079 Slog.v(TAG, String.format( 2080 "Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, " 2081 + "category=%s", 2082 action, enableFlags, disableFlags, category)); 2083 } 2084 2085 sendConfigurationAndStartDreamOrDockAppLocked(category); 2086 } 2087 shouldStartDockApp(Context context, Intent homeIntent)2088 private boolean shouldStartDockApp(Context context, Intent homeIntent) { 2089 if (mWatch && !mSetupWizardComplete) { 2090 // Do not ever start dock app when setup is not complete on a watch. 2091 return false; 2092 } 2093 return Sandman.shouldStartDockApp(context, homeIntent); 2094 } 2095 sendConfigurationAndStartDreamOrDockAppLocked(String category)2096 private void sendConfigurationAndStartDreamOrDockAppLocked(String category) { 2097 // Update the configuration but don't send it yet. 2098 mHoldingConfiguration = false; 2099 updateConfigurationLocked(); 2100 2101 // Start the dock app, if there is one. 2102 boolean dockAppStarted = false; 2103 if (category != null) { 2104 // Now we are going to be careful about switching the 2105 // configuration and starting the activity -- we need to 2106 // do this in a specific order under control of the 2107 // activity manager, to do it cleanly. So compute the 2108 // new config, but don't set it yet, and let the 2109 // activity manager take care of both the start and config 2110 // change. 2111 Intent homeIntent = buildHomeIntent(category); 2112 if (shouldStartDockApp(getContext(), homeIntent)) { 2113 try { 2114 int result = ActivityTaskManager.getService().startActivityWithConfig( 2115 null, getContext().getBasePackageName(), 2116 getContext().getAttributionTag(), homeIntent, null, null, null, 0, 0, 2117 mConfiguration, null, UserHandle.USER_CURRENT); 2118 if (ActivityManager.isStartResultSuccessful(result)) { 2119 dockAppStarted = true; 2120 } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { 2121 Slog.e(TAG, "Could not start dock app: " + homeIntent 2122 + ", startActivityWithConfig result " + result); 2123 } 2124 } catch (RemoteException ex) { 2125 Slog.e(TAG, "Could not start dock app: " + homeIntent, ex); 2126 } 2127 } 2128 } 2129 2130 // Send the new configuration. 2131 applyConfigurationExternallyLocked(); 2132 2133 final boolean dreamsSuppressed = mDreamsDisabledByAmbientModeSuppression 2134 && mLocalPowerManager.isAmbientDisplaySuppressed(); 2135 2136 // If we did not start a dock app, then start dreaming if appropriate. 2137 if (category != null && !dockAppStarted && !dreamsSuppressed && ( 2138 mStartDreamImmediatelyOnDock 2139 || mWindowManager.isKeyguardShowingAndNotOccluded() 2140 || !mPowerManager.isInteractive())) { 2141 mInjector.startDreamWhenDockedIfAppropriate(getContext()); 2142 } 2143 } 2144 adjustStatusBarCarModeLocked()2145 private void adjustStatusBarCarModeLocked() { 2146 final Context context = getContext(); 2147 if (mStatusBarManager == null) { 2148 mStatusBarManager = (StatusBarManager) 2149 context.getSystemService(Context.STATUS_BAR_SERVICE); 2150 } 2151 2152 // Fear not: StatusBarManagerService manages a list of requests to disable 2153 // features of the status bar; these are ORed together to form the 2154 // active disabled list. So if (for example) the device is locked and 2155 // the status bar should be totally disabled, the calls below will 2156 // have no effect until the device is unlocked. 2157 if (mStatusBarManager != null) { 2158 mStatusBarManager.disable(mCarModeEnabled 2159 ? StatusBarManager.DISABLE_NOTIFICATION_TICKER 2160 : StatusBarManager.DISABLE_NONE); 2161 } 2162 2163 if (mNotificationManager == null) { 2164 mNotificationManager = (NotificationManager) 2165 context.getSystemService(Context.NOTIFICATION_SERVICE); 2166 } 2167 2168 if (mNotificationManager != null) { 2169 if (mCarModeEnabled) { 2170 Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class); 2171 2172 Notification.Builder n = 2173 new Notification.Builder(context, SystemNotificationChannels.CAR_MODE) 2174 .setSmallIcon(R.drawable.stat_notify_car_mode) 2175 .setDefaults(Notification.DEFAULT_LIGHTS) 2176 .setOngoing(true) 2177 .setWhen(0) 2178 .setColor(context.getColor( 2179 com.android.internal.R.color.system_notification_accent_color)) 2180 .setContentTitle( 2181 context.getString(R.string.car_mode_disable_notification_title)) 2182 .setContentText( 2183 context.getString(R.string.car_mode_disable_notification_message)) 2184 2185 .setContentIntent( 2186 // TODO(b/173744200) Please replace FLAG_MUTABLE_UNAUDITED below 2187 // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE. 2188 PendingIntent.getActivityAsUser(context, 0, 2189 carModeOffIntent, PendingIntent.FLAG_MUTABLE, 2190 null, UserHandle.CURRENT)); 2191 mNotificationManager.notifyAsUser(null, 2192 SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL); 2193 } else { 2194 mNotificationManager.cancelAsUser(null, 2195 SystemMessage.NOTE_CAR_MODE_DISABLE, UserHandle.ALL); 2196 } 2197 } 2198 } 2199 updateComputedNightModeLocked(boolean activate)2200 private void updateComputedNightModeLocked(boolean activate) { 2201 boolean newComputedValue = activate; 2202 boolean appliedOverrides = false; 2203 if (mNightMode.get() != MODE_NIGHT_YES && mNightMode.get() != UiModeManager.MODE_NIGHT_NO) { 2204 if (mOverrideNightModeOn && !newComputedValue) { 2205 newComputedValue = true; 2206 } else if (mOverrideNightModeOff && newComputedValue) { 2207 newComputedValue = false; 2208 } 2209 appliedOverrides = true; 2210 } 2211 2212 // Computes final night mode values based on Attention Mode. 2213 mComputedNightMode = switch (mAttentionModeThemeOverlay) { 2214 case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT) -> true; 2215 case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_DAY) -> false; 2216 default -> newComputedValue; // case OFF 2217 }; 2218 2219 if (appliedOverrides) { 2220 return; 2221 } 2222 2223 if (mNightMode.get() != MODE_NIGHT_AUTO || (mTwilightManager != null 2224 && mTwilightManager.getLastTwilightState() != null)) { 2225 resetNightModeOverrideLocked(); 2226 } 2227 } 2228 resetNightModeOverrideLocked()2229 private boolean resetNightModeOverrideLocked() { 2230 if (mOverrideNightModeOff || mOverrideNightModeOn) { 2231 mOverrideNightModeOff = false; 2232 mOverrideNightModeOn = false; 2233 persistNightModeOverrides(mOverrideNightModeUser); 2234 mOverrideNightModeUser = USER_SYSTEM; 2235 return true; 2236 } 2237 return false; 2238 } 2239 registerVrStateListener()2240 private void registerVrStateListener() { 2241 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService( 2242 Context.VR_SERVICE)); 2243 try { 2244 if (vrManager != null) { 2245 vrManager.registerListener(mVrStateCallbacks); 2246 } 2247 } catch (RemoteException e) { 2248 Slog.e(TAG, "Failed to register VR mode state listener: " + e); 2249 } 2250 } 2251 2252 /** 2253 * Handles "adb shell" commands. 2254 */ 2255 private static class Shell extends ShellCommand { 2256 public static final String NIGHT_MODE_STR_YES = "yes"; 2257 public static final String NIGHT_MODE_STR_NO = "no"; 2258 public static final String NIGHT_MODE_STR_AUTO = "auto"; 2259 public static final String NIGHT_MODE_STR_CUSTOM_SCHEDULE = "custom_schedule"; 2260 public static final String NIGHT_MODE_STR_CUSTOM_BEDTIME = "custom_bedtime"; 2261 public static final String NIGHT_MODE_STR_UNKNOWN = "unknown"; 2262 private final IUiModeManager mInterface; 2263 Shell(IUiModeManager iface)2264 Shell(IUiModeManager iface) { 2265 mInterface = iface; 2266 } 2267 2268 @Override onHelp()2269 public void onHelp() { 2270 final PrintWriter pw = getOutPrintWriter(); 2271 pw.println("UiModeManager service (uimode) commands:"); 2272 pw.println(" help"); 2273 pw.println(" Print this help text."); 2274 pw.println(" night [yes|no|auto|custom_schedule|custom_bedtime]"); 2275 pw.println(" Set or read night mode."); 2276 pw.println(" car [yes|no]"); 2277 pw.println(" Set or read car mode."); 2278 pw.println(" time [start|end] <ISO time>"); 2279 pw.println(" Set custom start/end schedule time" 2280 + " (night mode must be set to custom to apply)."); 2281 } 2282 2283 @Override onCommand(String cmd)2284 public int onCommand(String cmd) { 2285 if (cmd == null) { 2286 return handleDefaultCommands(cmd); 2287 } 2288 2289 try { 2290 switch (cmd) { 2291 case "night": 2292 return handleNightMode(); 2293 case "car": 2294 return handleCarMode(); 2295 case "time": 2296 return handleCustomTime(); 2297 default: 2298 return handleDefaultCommands(cmd); 2299 } 2300 } catch (RemoteException e) { 2301 final PrintWriter err = getErrPrintWriter(); 2302 err.println("Remote exception: " + e); 2303 } 2304 return -1; 2305 } 2306 handleCustomTime()2307 private int handleCustomTime() throws RemoteException { 2308 final String modeStr = getNextArg(); 2309 if (modeStr == null) { 2310 printCustomTime(); 2311 return 0; 2312 } 2313 switch (modeStr) { 2314 case "start": 2315 final String start = getNextArg(); 2316 mInterface.setCustomNightModeStart(toMilliSeconds(LocalTime.parse(start))); 2317 return 0; 2318 case "end": 2319 final String end = getNextArg(); 2320 mInterface.setCustomNightModeEnd(toMilliSeconds(LocalTime.parse(end))); 2321 return 0; 2322 default: 2323 getErrPrintWriter().println("command must be in [start|end]"); 2324 return -1; 2325 } 2326 } 2327 printCustomTime()2328 private void printCustomTime() throws RemoteException { 2329 getOutPrintWriter().println("start " + fromMilliseconds( 2330 mInterface.getCustomNightModeStart()).toString()); 2331 getOutPrintWriter().println("end " + fromMilliseconds( 2332 mInterface.getCustomNightModeEnd()).toString()); 2333 } 2334 handleNightMode()2335 private int handleNightMode() throws RemoteException { 2336 final PrintWriter err = getErrPrintWriter(); 2337 final String modeStr = getNextArg(); 2338 if (modeStr == null) { 2339 printCurrentNightMode(); 2340 return 0; 2341 } 2342 2343 final int mode = strToNightMode(modeStr); 2344 final int customType = strToNightModeCustomType(modeStr); 2345 if (mode >= 0) { 2346 mInterface.setNightMode(mode); 2347 if (mode == UiModeManager.MODE_NIGHT_CUSTOM) { 2348 mInterface.setNightModeCustomType(customType); 2349 } 2350 printCurrentNightMode(); 2351 return 0; 2352 } else { 2353 err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '" 2354 + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO 2355 + "', or '" + NIGHT_MODE_STR_CUSTOM_SCHEDULE + "', or '" 2356 + NIGHT_MODE_STR_CUSTOM_BEDTIME + "'"); 2357 return -1; 2358 } 2359 } 2360 printCurrentNightMode()2361 private void printCurrentNightMode() throws RemoteException { 2362 final PrintWriter pw = getOutPrintWriter(); 2363 final int currMode = mInterface.getNightMode(); 2364 final int customType = mInterface.getNightModeCustomType(); 2365 final String currModeStr = nightModeToStr(currMode, customType); 2366 pw.println("Night mode: " + currModeStr); 2367 } 2368 nightModeToStr(int mode, int customType)2369 private static String nightModeToStr(int mode, int customType) { 2370 switch (mode) { 2371 case UiModeManager.MODE_NIGHT_YES: 2372 return NIGHT_MODE_STR_YES; 2373 case UiModeManager.MODE_NIGHT_NO: 2374 return NIGHT_MODE_STR_NO; 2375 case UiModeManager.MODE_NIGHT_AUTO: 2376 return NIGHT_MODE_STR_AUTO; 2377 case MODE_NIGHT_CUSTOM: 2378 if (customType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE) { 2379 return NIGHT_MODE_STR_CUSTOM_SCHEDULE; 2380 } 2381 if (customType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { 2382 return NIGHT_MODE_STR_CUSTOM_BEDTIME; 2383 } 2384 default: 2385 return NIGHT_MODE_STR_UNKNOWN; 2386 } 2387 } 2388 strToNightMode(String modeStr)2389 private static int strToNightMode(String modeStr) { 2390 switch (modeStr) { 2391 case NIGHT_MODE_STR_YES: 2392 return UiModeManager.MODE_NIGHT_YES; 2393 case NIGHT_MODE_STR_NO: 2394 return UiModeManager.MODE_NIGHT_NO; 2395 case NIGHT_MODE_STR_AUTO: 2396 return UiModeManager.MODE_NIGHT_AUTO; 2397 case NIGHT_MODE_STR_CUSTOM_SCHEDULE: 2398 case NIGHT_MODE_STR_CUSTOM_BEDTIME: 2399 return UiModeManager.MODE_NIGHT_CUSTOM; 2400 default: 2401 return -1; 2402 } 2403 } 2404 strToNightModeCustomType(String customTypeStr)2405 private static int strToNightModeCustomType(String customTypeStr) { 2406 switch (customTypeStr) { 2407 case NIGHT_MODE_STR_CUSTOM_BEDTIME: 2408 return UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; 2409 case NIGHT_MODE_STR_CUSTOM_SCHEDULE: 2410 return UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE; 2411 default: 2412 return -1; 2413 } 2414 } 2415 handleCarMode()2416 private int handleCarMode() throws RemoteException { 2417 final PrintWriter err = getErrPrintWriter(); 2418 final String modeStr = getNextArg(); 2419 if (modeStr == null) { 2420 printCurrentCarMode(); 2421 return 0; 2422 } 2423 2424 if (modeStr.equals("yes")) { 2425 mInterface.enableCarMode(0 /* flags */, DEFAULT_PRIORITY, "" /* package */); 2426 printCurrentCarMode(); 2427 return 0; 2428 } else if (modeStr.equals("no")) { 2429 mInterface.disableCarMode(0 /* flags */); 2430 printCurrentCarMode(); 2431 return 0; 2432 } else { 2433 err.println("Error: mode must be 'yes', or 'no'"); 2434 return -1; 2435 } 2436 } 2437 printCurrentCarMode()2438 private void printCurrentCarMode() throws RemoteException { 2439 final PrintWriter pw = getOutPrintWriter(); 2440 final int currMode = mInterface.getCurrentModeType(); 2441 pw.println("Car mode: " + (currMode == Configuration.UI_MODE_TYPE_CAR ? "yes" : "no")); 2442 } 2443 } 2444 2445 public final class LocalService extends UiModeManagerInternal { 2446 2447 @Override isNightMode()2448 public boolean isNightMode() { 2449 synchronized (mLock) { 2450 final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0; 2451 if (LOG) { 2452 Slog.d(TAG, 2453 "LocalService.isNightMode(): mNightMode=" + mNightMode 2454 + "; mComputedNightMode=" + mComputedNightMode 2455 + "; uiMode=" + mConfiguration.uiMode 2456 + "; isIt=" + isIt); 2457 } 2458 return isIt; 2459 } 2460 } 2461 } 2462 2463 @VisibleForTesting 2464 public static class Injector { getCallingUid()2465 public int getCallingUid() { 2466 return Binder.getCallingUid(); 2467 } 2468 startDreamWhenDockedIfAppropriate(Context context)2469 public void startDreamWhenDockedIfAppropriate(Context context) { 2470 Sandman.startDreamWhenDockedIfAppropriate(context); 2471 } 2472 } 2473 2474 /** 2475 * Interface to contain the value for an integral property. We make the property 2476 * accessible through this class to ensure that the reassignment of this value invalidates the 2477 * cache. 2478 */ 2479 private interface IntProperty { get()2480 int get(); set(int value)2481 void set(int value); 2482 } 2483 } 2484