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