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