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