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