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