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