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