[go: nahoru, domu]

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