1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import android.annotation.Nullable; 20 import android.app.Activity; 21 import android.app.ActivityManager; 22 import android.app.ActivityTaskManager; 23 import android.app.IUiModeManager; 24 import android.app.Notification; 25 import android.app.NotificationManager; 26 import android.app.PendingIntent; 27 import android.app.StatusBarManager; 28 import android.app.UiModeManager; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.PackageManager; 34 import android.content.res.Configuration; 35 import android.content.res.Resources; 36 import android.database.ContentObserver; 37 import android.net.Uri; 38 import android.os.BatteryManager; 39 import android.os.Binder; 40 import android.os.Handler; 41 import android.os.PowerManager; 42 import android.os.PowerManager.ServiceType; 43 import android.os.PowerManagerInternal; 44 import android.os.RemoteException; 45 import android.os.ResultReceiver; 46 import android.os.ServiceManager; 47 import android.os.ShellCallback; 48 import android.os.ShellCommand; 49 import android.os.SystemProperties; 50 import android.os.UserHandle; 51 import android.os.UserManager; 52 import android.provider.Settings.Secure; 53 import android.service.dreams.Sandman; 54 import android.service.vr.IVrManager; 55 import android.service.vr.IVrStateCallbacks; 56 import android.util.Slog; 57 58 import com.android.internal.R; 59 import com.android.internal.app.DisableCarModeActivity; 60 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 61 import com.android.internal.notification.SystemNotificationChannels; 62 import com.android.internal.util.DumpUtils; 63 import com.android.server.twilight.TwilightListener; 64 import com.android.server.twilight.TwilightManager; 65 import com.android.server.twilight.TwilightState; 66 67 import java.io.FileDescriptor; 68 import java.io.PrintWriter; 69 70 final class UiModeManagerService extends SystemService { 71 private static final String TAG = UiModeManager.class.getSimpleName(); 72 private static final boolean LOG = false; 73 74 // Enable launching of applications when entering the dock. 75 private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; 76 private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme"; 77 78 final Object mLock = new Object(); 79 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 80 81 private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 82 private int mNightMode = UiModeManager.MODE_NIGHT_NO; 83 84 private boolean mCarModeEnabled = false; 85 private boolean mCharging = false; 86 private boolean mPowerSave = false; 87 private int mDefaultUiModeType; 88 private boolean mCarModeKeepsScreenOn; 89 private boolean mDeskModeKeepsScreenOn; 90 private boolean mTelevision; 91 private boolean mWatch; 92 private boolean mVrHeadset; 93 private boolean mComputedNightMode; 94 private int mCarModeEnableFlags; 95 private boolean mSetupWizardComplete; 96 97 // flag set by resource, whether to enable Car dock launch when starting car mode. 98 private boolean mEnableCarDockLaunch = true; 99 // flag set by resource, whether to lock UI mode to the default one or not. 100 private boolean mUiModeLocked = false; 101 // flag set by resource, whether to night mode change for normal all or not. 102 private boolean mNightModeLocked = false; 103 104 int mCurUiMode = 0; 105 private int mSetUiMode = 0; 106 private boolean mHoldingConfiguration = false; 107 108 private Configuration mConfiguration = new Configuration(); 109 boolean mSystemReady; 110 111 private final Handler mHandler = new Handler(); 112 113 private TwilightManager mTwilightManager; 114 private NotificationManager mNotificationManager; 115 private StatusBarManager mStatusBarManager; 116 117 private PowerManager.WakeLock mWakeLock; 118 119 private final LocalService mLocalService = new LocalService(); 120 121 public UiModeManagerService(Context context) { 122 super(context); 123 } 124 125 private static Intent buildHomeIntent(String category) { 126 Intent intent = new Intent(Intent.ACTION_MAIN); 127 intent.addCategory(category); 128 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 129 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 130 return intent; 131 } 132 133 // The broadcast receiver which receives the result of the ordered broadcast sent when 134 // the dock state changes. The original ordered broadcast is sent with an initial result 135 // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., 136 // to RESULT_CANCELED, then the intent to start a dock app will not be sent. 137 private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 138 @Override 139 public void onReceive(Context context, Intent intent) { 140 if (getResultCode() != Activity.RESULT_OK) { 141 if (LOG) { 142 Slog.v(TAG, "Handling broadcast result for action " + intent.getAction() 143 + ": canceled: " + getResultCode()); 144 } 145 return; 146 } 147 148 final int enableFlags = intent.getIntExtra("enableFlags", 0); 149 final int disableFlags = intent.getIntExtra("disableFlags", 0); 150 synchronized (mLock) { 151 updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags); 152 } 153 } 154 }; 155 156 private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() { 157 @Override 158 public void onReceive(Context context, Intent intent) { 159 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 160 Intent.EXTRA_DOCK_STATE_UNDOCKED); 161 updateDockState(state); 162 } 163 }; 164 165 private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { 166 @Override 167 public void onReceive(Context context, Intent intent) { 168 switch (intent.getAction()) { 169 case Intent.ACTION_BATTERY_CHANGED: 170 mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0; 171 break; 172 } 173 synchronized (mLock) { 174 if (mSystemReady) { 175 updateLocked(0, 0); 176 } 177 } 178 } 179 }; 180 181 private final TwilightListener mTwilightListener = new TwilightListener() { 182 @Override 183 public void onTwilightStateChanged(@Nullable TwilightState state) { 184 synchronized (mLock) { 185 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) { 186 updateComputedNightModeLocked(); 187 updateLocked(0, 0); 188 } 189 } 190 } 191 }; 192 193 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 194 @Override 195 public void onVrStateChanged(boolean enabled) { 196 synchronized (mLock) { 197 mVrHeadset = enabled; 198 if (mSystemReady) { 199 updateLocked(0, 0); 200 } 201 } 202 } 203 }; 204 205 private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) { 206 @Override 207 public void onChange(boolean selfChange, Uri uri) { 208 // setup wizard is done now so we can unblock 209 if (setupWizardCompleteForCurrentUser()) { 210 mSetupWizardComplete = true; 211 getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); 212 // update night mode 213 Context context = getContext(); 214 updateNightModeFromSettings(context, context.getResources(), 215 UserHandle.getCallingUserId()); 216 updateLocked(0, 0); 217 } 218 } 219 }; 220 221 @Override 222 public void onSwitchUser(int userHandle) { 223 super.onSwitchUser(userHandle); 224 getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); 225 verifySetupWizardCompleted(); 226 } 227 228 @Override 229 public void onStart() { 230 final Context context = getContext(); 231 232 final PowerManager powerManager = 233 (PowerManager) context.getSystemService(Context.POWER_SERVICE); 234 mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); 235 236 // If setup isn't complete for this user listen for completion so we can unblock 237 // being able to send a night mode configuration change event 238 verifySetupWizardCompleted(); 239 240 context.registerReceiver(mDockModeReceiver, 241 new IntentFilter(Intent.ACTION_DOCK_EVENT)); 242 IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 243 context.registerReceiver(mBatteryReceiver, batteryFilter); 244 245 PowerManagerInternal localPowerManager = 246 LocalServices.getService(PowerManagerInternal.class); 247 mPowerSave = localPowerManager.getLowPowerState(ServiceType.NIGHT_MODE).batterySaverEnabled; 248 localPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE, 249 state -> { 250 synchronized (mLock) { 251 if (mPowerSave == state.batterySaverEnabled) { 252 return; 253 } 254 mPowerSave = state.batterySaverEnabled; 255 if (mSystemReady) { 256 updateLocked(0, 0); 257 } 258 } 259 }); 260 261 mConfiguration.setToDefaults(); 262 263 final Resources res = context.getResources(); 264 mDefaultUiModeType = res.getInteger( 265 com.android.internal.R.integer.config_defaultUiModeType); 266 mCarModeKeepsScreenOn = (res.getInteger( 267 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1); 268 mDeskModeKeepsScreenOn = (res.getInteger( 269 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1); 270 mEnableCarDockLaunch = res.getBoolean( 271 com.android.internal.R.bool.config_enableCarDockHomeLaunch); 272 mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode); 273 mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode); 274 275 final PackageManager pm = context.getPackageManager(); 276 mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION) 277 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK); 278 mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH); 279 280 updateNightModeFromSettings(context, res, UserHandle.getCallingUserId()); 281 282 // Update the initial, static configurations. 283 SystemServerInitThreadPool.get().submit(() -> { 284 synchronized (mLock) { 285 updateConfigurationLocked(); 286 sendConfigurationLocked(); 287 } 288 289 }, TAG + ".onStart"); 290 publishBinderService(Context.UI_MODE_SERVICE, mService); 291 publishLocalService(UiModeManagerInternal.class, mLocalService); 292 293 IntentFilter filter = new IntentFilter(); 294 filter.addAction(Intent.ACTION_USER_SWITCHED); 295 context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler); 296 } 297 298 // Records whether setup wizard has happened or not and adds an observer for this user if not. 299 private void verifySetupWizardCompleted() { 300 final Context context = getContext(); 301 final int userId = UserHandle.getCallingUserId(); 302 if (!setupWizardCompleteForCurrentUser()) { 303 mSetupWizardComplete = false; 304 context.getContentResolver().registerContentObserver( 305 Secure.getUriFor( 306 Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId); 307 } else { 308 mSetupWizardComplete = true; 309 } 310 } 311 312 private boolean setupWizardCompleteForCurrentUser() { 313 return Secure.getIntForUser(getContext().getContentResolver(), 314 Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1; 315 } 316 317 /** 318 * Updates the night mode setting in Settings.Global and returns if the value was successfully 319 * changed. 320 * @param context A valid context 321 * @param res A valid resource object 322 * @param userId The user to update the setting for 323 * @return True if the new value is different from the old value. False otherwise. 324 */ 325 private boolean updateNightModeFromSettings(Context context, Resources res, int userId) { 326 final int defaultNightMode = res.getInteger( 327 com.android.internal.R.integer.config_defaultNightMode); 328 int oldNightMode = mNightMode; 329 if (mSetupWizardComplete) { 330 mNightMode = Secure.getIntForUser(context.getContentResolver(), 331 Secure.UI_NIGHT_MODE, defaultNightMode, userId); 332 } else { 333 mNightMode = defaultNightMode; 334 } 335 336 return oldNightMode != mNightMode; 337 } 338 339 private final IUiModeManager.Stub mService = new IUiModeManager.Stub() { 340 @Override 341 public void enableCarMode(int flags) { 342 if (isUiModeLocked()) { 343 Slog.e(TAG, "enableCarMode while UI mode is locked"); 344 return; 345 } 346 final long ident = Binder.clearCallingIdentity(); 347 try { 348 synchronized (mLock) { 349 setCarModeLocked(true, flags); 350 if (mSystemReady) { 351 updateLocked(flags, 0); 352 } 353 } 354 } finally { 355 Binder.restoreCallingIdentity(ident); 356 } 357 } 358 359 @Override 360 public void disableCarMode(int flags) { 361 if (isUiModeLocked()) { 362 Slog.e(TAG, "disableCarMode while UI mode is locked"); 363 return; 364 } 365 final long ident = Binder.clearCallingIdentity(); 366 try { 367 synchronized (mLock) { 368 setCarModeLocked(false, 0); 369 if (mSystemReady) { 370 updateLocked(0, flags); 371 } 372 } 373 } finally { 374 Binder.restoreCallingIdentity(ident); 375 } 376 } 377 378 @Override 379 public int getCurrentModeType() { 380 final long ident = Binder.clearCallingIdentity(); 381 try { 382 synchronized (mLock) { 383 return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; 384 } 385 } finally { 386 Binder.restoreCallingIdentity(ident); 387 } 388 } 389 390 @Override 391 public void setNightMode(int mode) { 392 if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( 393 android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) 394 != PackageManager.PERMISSION_GRANTED)) { 395 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); 396 return; 397 } 398 if (!mSetupWizardComplete) { 399 Slog.d(TAG, "Night mode cannot be changed before setup wizard completes."); 400 return; 401 } 402 switch (mode) { 403 case UiModeManager.MODE_NIGHT_NO: 404 case UiModeManager.MODE_NIGHT_YES: 405 case UiModeManager.MODE_NIGHT_AUTO: 406 break; 407 default: 408 throw new IllegalArgumentException("Unknown mode: " + mode); 409 } 410 411 final int user = UserHandle.getCallingUserId(); 412 final long ident = Binder.clearCallingIdentity(); 413 try { 414 synchronized (mLock) { 415 if (mNightMode != mode) { 416 // Only persist setting if not in car mode 417 if (!mCarModeEnabled) { 418 Secure.putIntForUser(getContext().getContentResolver(), 419 Secure.UI_NIGHT_MODE, mode, user); 420 421 if (UserManager.get(getContext()).isPrimaryUser()) { 422 SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, 423 Integer.toString(mode)); 424 } 425 } 426 427 mNightMode = mode; 428 updateLocked(0, 0); 429 } 430 } 431 } finally { 432 Binder.restoreCallingIdentity(ident); 433 } 434 } 435 436 @Override 437 public int getNightMode() { 438 synchronized (mLock) { 439 return mNightMode; 440 } 441 } 442 443 @Override 444 public boolean isUiModeLocked() { 445 synchronized (mLock) { 446 return mUiModeLocked; 447 } 448 } 449 450 @Override 451 public boolean isNightModeLocked() { 452 synchronized (mLock) { 453 return mNightModeLocked; 454 } 455 } 456 457 @Override 458 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 459 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 460 new Shell(mService).exec(mService, in, out, err, args, callback, resultReceiver); 461 } 462 463 @Override 464 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 465 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; 466 dumpImpl(pw); 467 } 468 }; 469 470 void dumpImpl(PrintWriter pw) { 471 synchronized (mLock) { 472 pw.println("Current UI Mode Service state:"); 473 pw.print(" mDockState="); pw.print(mDockState); 474 pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); 475 pw.print(" mNightMode="); pw.print(mNightMode); pw.print(" ("); 476 pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") "); 477 pw.print(" mNightModeLocked="); pw.print(mNightModeLocked); 478 pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); 479 pw.print(" mComputedNightMode="); pw.print(mComputedNightMode); 480 pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags); 481 pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch); 482 pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode)); 483 pw.print(" mUiModeLocked="); pw.print(mUiModeLocked); 484 pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); 485 pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); 486 pw.print(" mSystemReady="); pw.println(mSystemReady); 487 if (mTwilightManager != null) { 488 // We may not have a TwilightManager. 489 pw.print(" mTwilightService.getLastTwilightState()="); 490 pw.println(mTwilightManager.getLastTwilightState()); 491 } 492 } 493 } 494 495 @Override 496 public void onBootPhase(int phase) { 497 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 498 synchronized (mLock) { 499 mTwilightManager = getLocalService(TwilightManager.class); 500 mSystemReady = true; 501 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR; 502 updateComputedNightModeLocked(); 503 registerVrStateListener(); 504 updateLocked(0, 0); 505 } 506 } 507 } 508 509 void setCarModeLocked(boolean enabled, int flags) { 510 if (mCarModeEnabled != enabled) { 511 mCarModeEnabled = enabled; 512 513 // When exiting car mode, restore night mode from settings 514 if (!mCarModeEnabled) { 515 Context context = getContext(); 516 updateNightModeFromSettings(context, 517 context.getResources(), 518 UserHandle.getCallingUserId()); 519 } 520 } 521 mCarModeEnableFlags = flags; 522 } 523 524 private void updateDockState(int newState) { 525 synchronized (mLock) { 526 if (newState != mDockState) { 527 mDockState = newState; 528 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0); 529 if (mSystemReady) { 530 updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0); 531 } 532 } 533 } 534 } 535 536 private static boolean isDeskDockState(int state) { 537 switch (state) { 538 case Intent.EXTRA_DOCK_STATE_DESK: 539 case Intent.EXTRA_DOCK_STATE_LE_DESK: 540 case Intent.EXTRA_DOCK_STATE_HE_DESK: 541 return true; 542 default: 543 return false; 544 } 545 } 546 547 private void updateConfigurationLocked() { 548 int uiMode = mDefaultUiModeType; 549 if (mUiModeLocked) { 550 // no-op, keeps default one 551 } else if (mTelevision) { 552 uiMode = Configuration.UI_MODE_TYPE_TELEVISION; 553 } else if (mWatch) { 554 uiMode = Configuration.UI_MODE_TYPE_WATCH; 555 } else if (mCarModeEnabled) { 556 uiMode = Configuration.UI_MODE_TYPE_CAR; 557 } else if (isDeskDockState(mDockState)) { 558 uiMode = Configuration.UI_MODE_TYPE_DESK; 559 } else if (mVrHeadset) { 560 uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET; 561 } 562 563 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) { 564 if (mTwilightManager != null) { 565 mTwilightManager.registerListener(mTwilightListener, mHandler); 566 } 567 updateComputedNightModeLocked(); 568 uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES 569 : Configuration.UI_MODE_NIGHT_NO; 570 } else { 571 if (mTwilightManager != null) { 572 mTwilightManager.unregisterListener(mTwilightListener); 573 } 574 uiMode |= mNightMode << 4; 575 } 576 577 // Override night mode in power save mode if not in car mode 578 if (mPowerSave && !mCarModeEnabled) { 579 uiMode &= ~Configuration.UI_MODE_NIGHT_NO; 580 uiMode |= Configuration.UI_MODE_NIGHT_YES; 581 } 582 583 if (LOG) { 584 Slog.d(TAG, 585 "updateConfigurationLocked: mDockState=" + mDockState 586 + "; mCarMode=" + mCarModeEnabled 587 + "; mNightMode=" + mNightMode 588 + "; uiMode=" + uiMode); 589 } 590 591 mCurUiMode = uiMode; 592 if (!mHoldingConfiguration) { 593 mConfiguration.uiMode = uiMode; 594 } 595 } 596 597 private void sendConfigurationLocked() { 598 if (mSetUiMode != mConfiguration.uiMode) { 599 mSetUiMode = mConfiguration.uiMode; 600 601 try { 602 ActivityTaskManager.getService().updateConfiguration(mConfiguration); 603 } catch (RemoteException e) { 604 Slog.w(TAG, "Failure communicating with activity manager", e); 605 } 606 } 607 } 608 609 void updateLocked(int enableFlags, int disableFlags) { 610 String action = null; 611 String oldAction = null; 612 if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { 613 adjustStatusBarCarModeLocked(); 614 oldAction = UiModeManager.ACTION_EXIT_CAR_MODE; 615 } else if (isDeskDockState(mLastBroadcastState)) { 616 oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; 617 } 618 619 if (mCarModeEnabled) { 620 if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) { 621 adjustStatusBarCarModeLocked(); 622 if (oldAction != null) { 623 sendForegroundBroadcastToAllUsers(oldAction); 624 } 625 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; 626 action = UiModeManager.ACTION_ENTER_CAR_MODE; 627 } 628 } else if (isDeskDockState(mDockState)) { 629 if (!isDeskDockState(mLastBroadcastState)) { 630 if (oldAction != null) { 631 sendForegroundBroadcastToAllUsers(oldAction); 632 } 633 mLastBroadcastState = mDockState; 634 action = UiModeManager.ACTION_ENTER_DESK_MODE; 635 } 636 } else { 637 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 638 action = oldAction; 639 } 640 641 if (action != null) { 642 if (LOG) { 643 Slog.v(TAG, String.format( 644 "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x", 645 action, enableFlags, disableFlags)); 646 } 647 648 // Send the ordered broadcast; the result receiver will receive after all 649 // broadcasts have been sent. If any broadcast receiver changes the result 650 // code from the initial value of RESULT_OK, then the result receiver will 651 // not launch the corresponding dock application. This gives apps a chance 652 // to override the behavior and stay in their app even when the device is 653 // placed into a dock. 654 Intent intent = new Intent(action); 655 intent.putExtra("enableFlags", enableFlags); 656 intent.putExtra("disableFlags", disableFlags); 657 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 658 getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 659 mResultReceiver, null, Activity.RESULT_OK, null, null); 660 661 // Attempting to make this transition a little more clean, we are going 662 // to hold off on doing a configuration change until we have finished 663 // the broadcast and started the home activity. 664 mHoldingConfiguration = true; 665 updateConfigurationLocked(); 666 } else { 667 String category = null; 668 if (mCarModeEnabled) { 669 if (mEnableCarDockLaunch 670 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 671 category = Intent.CATEGORY_CAR_DOCK; 672 } 673 } else if (isDeskDockState(mDockState)) { 674 if (ENABLE_LAUNCH_DESK_DOCK_APP 675 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 676 category = Intent.CATEGORY_DESK_DOCK; 677 } 678 } else { 679 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { 680 category = Intent.CATEGORY_HOME; 681 } 682 } 683 684 if (LOG) { 685 Slog.v(TAG, "updateLocked: null action, mDockState=" 686 + mDockState +", category=" + category); 687 } 688 689 sendConfigurationAndStartDreamOrDockAppLocked(category); 690 } 691 692 // keep screen on when charging and in car mode 693 boolean keepScreenOn = mCharging && 694 ((mCarModeEnabled && mCarModeKeepsScreenOn && 695 (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) || 696 (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn)); 697 if (keepScreenOn != mWakeLock.isHeld()) { 698 if (keepScreenOn) { 699 mWakeLock.acquire(); 700 } else { 701 mWakeLock.release(); 702 } 703 } 704 } 705 706 private void sendForegroundBroadcastToAllUsers(String action) { 707 getContext().sendBroadcastAsUser(new Intent(action) 708 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL); 709 } 710 711 private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) { 712 // Launch a dock activity 713 String category = null; 714 if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) { 715 // Only launch car home when car mode is enabled and the caller 716 // has asked us to switch to it. 717 if (mEnableCarDockLaunch 718 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 719 category = Intent.CATEGORY_CAR_DOCK; 720 } 721 } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) { 722 // Only launch car home when desk mode is enabled and the caller 723 // has asked us to switch to it. Currently re-using the car 724 // mode flag since we don't have a formal API for "desk mode". 725 if (ENABLE_LAUNCH_DESK_DOCK_APP 726 && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { 727 category = Intent.CATEGORY_DESK_DOCK; 728 } 729 } else { 730 // Launch the standard home app if requested. 731 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { 732 category = Intent.CATEGORY_HOME; 733 } 734 } 735 736 if (LOG) { 737 Slog.v(TAG, String.format( 738 "Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, " 739 + "category=%s", 740 action, enableFlags, disableFlags, category)); 741 } 742 743 sendConfigurationAndStartDreamOrDockAppLocked(category); 744 } 745 746 private void sendConfigurationAndStartDreamOrDockAppLocked(String category) { 747 // Update the configuration but don't send it yet. 748 mHoldingConfiguration = false; 749 updateConfigurationLocked(); 750 751 // Start the dock app, if there is one. 752 boolean dockAppStarted = false; 753 if (category != null) { 754 // Now we are going to be careful about switching the 755 // configuration and starting the activity -- we need to 756 // do this in a specific order under control of the 757 // activity manager, to do it cleanly. So compute the 758 // new config, but don't set it yet, and let the 759 // activity manager take care of both the start and config 760 // change. 761 Intent homeIntent = buildHomeIntent(category); 762 if (Sandman.shouldStartDockApp(getContext(), homeIntent)) { 763 try { 764 int result = ActivityTaskManager.getService().startActivityWithConfig( 765 null, null, homeIntent, null, null, null, 0, 0, 766 mConfiguration, null, UserHandle.USER_CURRENT); 767 if (ActivityManager.isStartResultSuccessful(result)) { 768 dockAppStarted = true; 769 } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { 770 Slog.e(TAG, "Could not start dock app: " + homeIntent 771 + ", startActivityWithConfig result " + result); 772 } 773 } catch (RemoteException ex) { 774 Slog.e(TAG, "Could not start dock app: " + homeIntent, ex); 775 } 776 } 777 } 778 779 // Send the new configuration. 780 sendConfigurationLocked(); 781 782 // If we did not start a dock app, then start dreaming if supported. 783 if (category != null && !dockAppStarted) { 784 Sandman.startDreamWhenDockedIfAppropriate(getContext()); 785 } 786 } 787 788 private void adjustStatusBarCarModeLocked() { 789 final Context context = getContext(); 790 if (mStatusBarManager == null) { 791 mStatusBarManager = (StatusBarManager) 792 context.getSystemService(Context.STATUS_BAR_SERVICE); 793 } 794 795 // Fear not: StatusBarManagerService manages a list of requests to disable 796 // features of the status bar; these are ORed together to form the 797 // active disabled list. So if (for example) the device is locked and 798 // the status bar should be totally disabled, the calls below will 799 // have no effect until the device is unlocked. 800 if (mStatusBarManager != null) { 801 mStatusBarManager.disable(mCarModeEnabled 802 ? StatusBarManager.DISABLE_NOTIFICATION_TICKER 803 : StatusBarManager.DISABLE_NONE); 804 } 805 806 if (mNotificationManager == null) { 807 mNotificationManager = (NotificationManager) 808 context.getSystemService(Context.NOTIFICATION_SERVICE); 809 } 810 811 if (mNotificationManager != null) { 812 if (mCarModeEnabled) { 813 Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class); 814 815 Notification.Builder n = 816 new Notification.Builder(context, SystemNotificationChannels.CAR_MODE) 817 .setSmallIcon(R.drawable.stat_notify_car_mode) 818 .setDefaults(Notification.DEFAULT_LIGHTS) 819 .setOngoing(true) 820 .setWhen(0) 821 .setColor(context.getColor( 822 com.android.internal.R.color.system_notification_accent_color)) 823 .setContentTitle( 824 context.getString(R.string.car_mode_disable_notification_title)) 825 .setContentText( 826 context.getString(R.string.car_mode_disable_notification_message)) 827 .setContentIntent( 828 PendingIntent.getActivityAsUser(context, 0, carModeOffIntent, 0, 829 null, UserHandle.CURRENT)); 830 mNotificationManager.notifyAsUser(null, 831 SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL); 832 } else { 833 mNotificationManager.cancelAsUser(null, 834 SystemMessage.NOTE_CAR_MODE_DISABLE, UserHandle.ALL); 835 } 836 } 837 } 838 839 private void updateComputedNightModeLocked() { 840 if (mTwilightManager != null) { 841 TwilightState state = mTwilightManager.getLastTwilightState(); 842 if (state != null) { 843 mComputedNightMode = state.isNight(); 844 } 845 } 846 } 847 848 private void registerVrStateListener() { 849 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService( 850 Context.VR_SERVICE)); 851 try { 852 if (vrManager != null) { 853 vrManager.registerListener(mVrStateCallbacks); 854 } 855 } catch (RemoteException e) { 856 Slog.e(TAG, "Failed to register VR mode state listener: " + e); 857 } 858 } 859 860 /** 861 * Handles "adb shell" commands. 862 */ 863 private static class Shell extends ShellCommand { 864 public static final String NIGHT_MODE_STR_YES = "yes"; 865 public static final String NIGHT_MODE_STR_NO = "no"; 866 public static final String NIGHT_MODE_STR_AUTO = "auto"; 867 public static final String NIGHT_MODE_STR_UNKNOWN = "unknown"; 868 private final IUiModeManager mInterface; 869 870 Shell(IUiModeManager iface) { 871 mInterface = iface; 872 } 873 874 @Override 875 public void onHelp() { 876 final PrintWriter pw = getOutPrintWriter(); 877 pw.println("UiModeManager service (uimode) commands:"); 878 pw.println(" help"); 879 pw.println(" Print this help text."); 880 pw.println(" night [yes|no|auto]"); 881 pw.println(" Set or read night mode."); 882 } 883 884 @Override 885 public int onCommand(String cmd) { 886 if (cmd == null) { 887 return handleDefaultCommands(cmd); 888 } 889 890 try { 891 switch (cmd) { 892 case "night": 893 return handleNightMode(); 894 default: 895 return handleDefaultCommands(cmd); 896 } 897 } catch (RemoteException e) { 898 final PrintWriter err = getErrPrintWriter(); 899 err.println("Remote exception: " + e); 900 } 901 return -1; 902 } 903 904 private int handleNightMode() throws RemoteException { 905 final PrintWriter err = getErrPrintWriter(); 906 final String modeStr = getNextArg(); 907 if (modeStr == null) { 908 printCurrentNightMode(); 909 return 0; 910 } 911 912 final int mode = strToNightMode(modeStr); 913 if (mode >= 0) { 914 mInterface.setNightMode(mode); 915 printCurrentNightMode(); 916 return 0; 917 } else { 918 err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '" 919 + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO + "'"); 920 return -1; 921 } 922 } 923 924 private void printCurrentNightMode() throws RemoteException { 925 final PrintWriter pw = getOutPrintWriter(); 926 final int currMode = mInterface.getNightMode(); 927 final String currModeStr = nightModeToStr(currMode); 928 pw.println("Night mode: " + currModeStr); 929 } 930 931 private static String nightModeToStr(int mode) { 932 switch (mode) { 933 case UiModeManager.MODE_NIGHT_YES: 934 return NIGHT_MODE_STR_YES; 935 case UiModeManager.MODE_NIGHT_NO: 936 return NIGHT_MODE_STR_NO; 937 case UiModeManager.MODE_NIGHT_AUTO: 938 return NIGHT_MODE_STR_AUTO; 939 default: 940 return NIGHT_MODE_STR_UNKNOWN; 941 } 942 } 943 944 private static int strToNightMode(String modeStr) { 945 switch (modeStr) { 946 case NIGHT_MODE_STR_YES: 947 return UiModeManager.MODE_NIGHT_YES; 948 case NIGHT_MODE_STR_NO: 949 return UiModeManager.MODE_NIGHT_NO; 950 case NIGHT_MODE_STR_AUTO: 951 return UiModeManager.MODE_NIGHT_AUTO; 952 default: 953 return -1; 954 } 955 } 956 } 957 958 public final class LocalService extends UiModeManagerInternal { 959 960 @Override 961 public boolean isNightMode() { 962 synchronized (mLock) { 963 final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0; 964 if (LOG) { 965 Slog.d(TAG, 966 "LocalService.isNightMode(): mNightMode=" + mNightMode 967 + "; mComputedNightMode=" + mComputedNightMode 968 + "; uiMode=" + mConfiguration.uiMode 969 + "; isIt=" + isIt); 970 } 971 return isIt; 972 } 973 } 974 } 975 976 private final class UserSwitchedReceiver extends BroadcastReceiver { 977 @Override 978 public void onReceive(Context context, Intent intent) { 979 synchronized (mLock) { 980 final int currentId = intent.getIntExtra( 981 Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM); 982 // only update if the value is actually changed 983 if (updateNightModeFromSettings(context, context.getResources(), currentId)) { 984 updateLocked(0, 0); 985 } 986 } 987 } 988 } 989 } 990