Home | History | Annotate | Download | only in server
      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