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.ActivityTaskManager;
     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.database.ContentObserver;
     37 import android.net.Uri;
     38 import android.os.BatteryManager;
     39 import android.os.Binder;
     40 import android.os.Handler;
     41 import android.os.PowerManager;
     42 import android.os.PowerManager.ServiceType;
     43 import android.os.PowerManagerInternal;
     44 import android.os.RemoteException;
     45 import android.os.ResultReceiver;
     46 import android.os.ServiceManager;
     47 import android.os.ShellCallback;
     48 import android.os.ShellCommand;
     49 import android.os.SystemProperties;
     50 import android.os.UserHandle;
     51 import android.os.UserManager;
     52 import android.provider.Settings.Secure;
     53 import android.service.dreams.Sandman;
     54 import android.service.vr.IVrManager;
     55 import android.service.vr.IVrStateCallbacks;
     56 import android.util.Slog;
     57 
     58 import com.android.internal.R;
     59 import com.android.internal.app.DisableCarModeActivity;
     60 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
     61 import com.android.internal.notification.SystemNotificationChannels;
     62 import com.android.internal.util.DumpUtils;
     63 import com.android.server.twilight.TwilightListener;
     64 import com.android.server.twilight.TwilightManager;
     65 import com.android.server.twilight.TwilightState;
     66 
     67 import java.io.FileDescriptor;
     68 import java.io.PrintWriter;
     69 
     70 final class UiModeManagerService extends SystemService {
     71     private static final String TAG = UiModeManager.class.getSimpleName();
     72     private static final boolean LOG = false;
     73 
     74     // Enable launching of applications when entering the dock.
     75     private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
     76     private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme";
     77 
     78     final Object mLock = new Object();
     79     private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     80 
     81     private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     82     private int mNightMode = UiModeManager.MODE_NIGHT_NO;
     83 
     84     private boolean mCarModeEnabled = false;
     85     private boolean mCharging = false;
     86     private boolean mPowerSave = false;
     87     private int mDefaultUiModeType;
     88     private boolean mCarModeKeepsScreenOn;
     89     private boolean mDeskModeKeepsScreenOn;
     90     private boolean mTelevision;
     91     private boolean mWatch;
     92     private boolean mVrHeadset;
     93     private boolean mComputedNightMode;
     94     private int mCarModeEnableFlags;
     95     private boolean mSetupWizardComplete;
     96 
     97     // flag set by resource, whether to enable Car dock launch when starting car mode.
     98     private boolean mEnableCarDockLaunch = true;
     99     // flag set by resource, whether to lock UI mode to the default one or not.
    100     private boolean mUiModeLocked = false;
    101     // flag set by resource, whether to night mode change for normal all or not.
    102     private boolean mNightModeLocked = false;
    103 
    104     int mCurUiMode = 0;
    105     private int mSetUiMode = 0;
    106     private boolean mHoldingConfiguration = false;
    107 
    108     private Configuration mConfiguration = new Configuration();
    109     boolean mSystemReady;
    110 
    111     private final Handler mHandler = new Handler();
    112 
    113     private TwilightManager mTwilightManager;
    114     private NotificationManager mNotificationManager;
    115     private StatusBarManager mStatusBarManager;
    116 
    117     private PowerManager.WakeLock mWakeLock;
    118 
    119     private final LocalService mLocalService = new LocalService();
    120 
    121     public UiModeManagerService(Context context) {
    122         super(context);
    123     }
    124 
    125     private static Intent buildHomeIntent(String category) {
    126         Intent intent = new Intent(Intent.ACTION_MAIN);
    127         intent.addCategory(category);
    128         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    129                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    130         return intent;
    131     }
    132 
    133     // The broadcast receiver which receives the result of the ordered broadcast sent when
    134     // the dock state changes. The original ordered broadcast is sent with an initial result
    135     // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
    136     // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
    137     private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
    138         @Override
    139         public void onReceive(Context context, Intent intent) {
    140             if (getResultCode() != Activity.RESULT_OK) {
    141                 if (LOG) {
    142                     Slog.v(TAG, "Handling broadcast result for action " + intent.getAction()
    143                             + ": canceled: " + getResultCode());
    144                 }
    145                 return;
    146             }
    147 
    148             final int enableFlags = intent.getIntExtra("enableFlags", 0);
    149             final int disableFlags = intent.getIntExtra("disableFlags", 0);
    150             synchronized (mLock) {
    151                 updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags);
    152             }
    153         }
    154     };
    155 
    156     private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
    157         @Override
    158         public void onReceive(Context context, Intent intent) {
    159             int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
    160                     Intent.EXTRA_DOCK_STATE_UNDOCKED);
    161             updateDockState(state);
    162         }
    163     };
    164 
    165     private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
    166         @Override
    167         public void onReceive(Context context, Intent intent) {
    168             switch (intent.getAction()) {
    169                 case Intent.ACTION_BATTERY_CHANGED:
    170                     mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
    171                     break;
    172             }
    173             synchronized (mLock) {
    174                 if (mSystemReady) {
    175                     updateLocked(0, 0);
    176                 }
    177             }
    178         }
    179     };
    180 
    181     private final TwilightListener mTwilightListener = new TwilightListener() {
    182         @Override
    183         public void onTwilightStateChanged(@Nullable TwilightState state) {
    184             synchronized (mLock) {
    185                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
    186                     updateComputedNightModeLocked();
    187                     updateLocked(0, 0);
    188                 }
    189             }
    190         }
    191     };
    192 
    193     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
    194         @Override
    195         public void onVrStateChanged(boolean enabled) {
    196             synchronized (mLock) {
    197                 mVrHeadset = enabled;
    198                 if (mSystemReady) {
    199                     updateLocked(0, 0);
    200                 }
    201             }
    202         }
    203     };
    204 
    205     private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) {
    206         @Override
    207         public void onChange(boolean selfChange, Uri uri) {
    208             // setup wizard is done now so we can unblock
    209             if (setupWizardCompleteForCurrentUser()) {
    210                 mSetupWizardComplete = true;
    211                 getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
    212                 // update night mode
    213                 Context context = getContext();
    214                 updateNightModeFromSettings(context, context.getResources(),
    215                         UserHandle.getCallingUserId());
    216                 updateLocked(0, 0);
    217             }
    218         }
    219     };
    220 
    221     @Override
    222     public void onSwitchUser(int userHandle) {
    223         super.onSwitchUser(userHandle);
    224         getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
    225         verifySetupWizardCompleted();
    226     }
    227 
    228     @Override
    229     public void onStart() {
    230         final Context context = getContext();
    231 
    232         final PowerManager powerManager =
    233                 (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    234         mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
    235 
    236         // If setup isn't complete for this user listen for completion so we can unblock
    237         // being able to send a night mode configuration change event
    238         verifySetupWizardCompleted();
    239 
    240         context.registerReceiver(mDockModeReceiver,
    241                 new IntentFilter(Intent.ACTION_DOCK_EVENT));
    242         IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    243         context.registerReceiver(mBatteryReceiver, batteryFilter);
    244 
    245         PowerManagerInternal localPowerManager =
    246                 LocalServices.getService(PowerManagerInternal.class);
    247         mPowerSave = localPowerManager.getLowPowerState(ServiceType.NIGHT_MODE).batterySaverEnabled;
    248         localPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE,
    249                 state -> {
    250                     synchronized (mLock) {
    251                         if (mPowerSave == state.batterySaverEnabled) {
    252                             return;
    253                         }
    254                         mPowerSave = state.batterySaverEnabled;
    255                         if (mSystemReady) {
    256                             updateLocked(0, 0);
    257                         }
    258                     }
    259                 });
    260 
    261         mConfiguration.setToDefaults();
    262 
    263         final Resources res = context.getResources();
    264         mDefaultUiModeType = res.getInteger(
    265                 com.android.internal.R.integer.config_defaultUiModeType);
    266         mCarModeKeepsScreenOn = (res.getInteger(
    267                 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
    268         mDeskModeKeepsScreenOn = (res.getInteger(
    269                 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
    270         mEnableCarDockLaunch = res.getBoolean(
    271                 com.android.internal.R.bool.config_enableCarDockHomeLaunch);
    272         mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode);
    273         mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode);
    274 
    275         final PackageManager pm = context.getPackageManager();
    276         mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
    277                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
    278         mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
    279 
    280         updateNightModeFromSettings(context, res, UserHandle.getCallingUserId());
    281 
    282         // Update the initial, static configurations.
    283         SystemServerInitThreadPool.get().submit(() -> {
    284             synchronized (mLock) {
    285                 updateConfigurationLocked();
    286                 sendConfigurationLocked();
    287             }
    288 
    289         }, TAG + ".onStart");
    290         publishBinderService(Context.UI_MODE_SERVICE, mService);
    291         publishLocalService(UiModeManagerInternal.class, mLocalService);
    292 
    293         IntentFilter filter = new IntentFilter();
    294         filter.addAction(Intent.ACTION_USER_SWITCHED);
    295         context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
    296     }
    297 
    298     // Records whether setup wizard has happened or not and adds an observer for this user if not.
    299     private void verifySetupWizardCompleted() {
    300         final Context context = getContext();
    301         final int userId = UserHandle.getCallingUserId();
    302         if (!setupWizardCompleteForCurrentUser()) {
    303             mSetupWizardComplete = false;
    304             context.getContentResolver().registerContentObserver(
    305                     Secure.getUriFor(
    306                             Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId);
    307         } else {
    308             mSetupWizardComplete = true;
    309         }
    310     }
    311 
    312     private boolean setupWizardCompleteForCurrentUser() {
    313         return Secure.getIntForUser(getContext().getContentResolver(),
    314                 Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
    315     }
    316 
    317     /**
    318      * Updates the night mode setting in Settings.Global and returns if the value was successfully
    319      * changed.
    320      * @param context A valid context
    321      * @param res A valid resource object
    322      * @param userId The user to update the setting for
    323      * @return True if the new value is different from the old value. False otherwise.
    324      */
    325     private boolean updateNightModeFromSettings(Context context, Resources res, int userId) {
    326         final int defaultNightMode = res.getInteger(
    327                 com.android.internal.R.integer.config_defaultNightMode);
    328         int oldNightMode = mNightMode;
    329         if (mSetupWizardComplete) {
    330             mNightMode = Secure.getIntForUser(context.getContentResolver(),
    331                     Secure.UI_NIGHT_MODE, defaultNightMode, userId);
    332         } else {
    333             mNightMode = defaultNightMode;
    334         }
    335 
    336         return oldNightMode != mNightMode;
    337     }
    338 
    339     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
    340         @Override
    341         public void enableCarMode(int flags) {
    342             if (isUiModeLocked()) {
    343                 Slog.e(TAG, "enableCarMode while UI mode is locked");
    344                 return;
    345             }
    346             final long ident = Binder.clearCallingIdentity();
    347             try {
    348                 synchronized (mLock) {
    349                     setCarModeLocked(true, flags);
    350                     if (mSystemReady) {
    351                         updateLocked(flags, 0);
    352                     }
    353                 }
    354             } finally {
    355                 Binder.restoreCallingIdentity(ident);
    356             }
    357         }
    358 
    359         @Override
    360         public void disableCarMode(int flags) {
    361             if (isUiModeLocked()) {
    362                 Slog.e(TAG, "disableCarMode while UI mode is locked");
    363                 return;
    364             }
    365             final long ident = Binder.clearCallingIdentity();
    366             try {
    367                 synchronized (mLock) {
    368                     setCarModeLocked(false, 0);
    369                     if (mSystemReady) {
    370                         updateLocked(0, flags);
    371                     }
    372                 }
    373             } finally {
    374                 Binder.restoreCallingIdentity(ident);
    375             }
    376         }
    377 
    378         @Override
    379         public int getCurrentModeType() {
    380             final long ident = Binder.clearCallingIdentity();
    381             try {
    382                 synchronized (mLock) {
    383                     return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
    384                 }
    385             } finally {
    386                 Binder.restoreCallingIdentity(ident);
    387             }
    388         }
    389 
    390         @Override
    391         public void setNightMode(int mode) {
    392             if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
    393                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
    394                     != PackageManager.PERMISSION_GRANTED)) {
    395                 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
    396                 return;
    397             }
    398             if (!mSetupWizardComplete) {
    399                 Slog.d(TAG, "Night mode cannot be changed before setup wizard completes.");
    400                 return;
    401             }
    402             switch (mode) {
    403                 case UiModeManager.MODE_NIGHT_NO:
    404                 case UiModeManager.MODE_NIGHT_YES:
    405                 case UiModeManager.MODE_NIGHT_AUTO:
    406                     break;
    407                 default:
    408                     throw new IllegalArgumentException("Unknown mode: " + mode);
    409             }
    410 
    411             final int user = UserHandle.getCallingUserId();
    412             final long ident = Binder.clearCallingIdentity();
    413             try {
    414                 synchronized (mLock) {
    415                     if (mNightMode != mode) {
    416                         // Only persist setting if not in car mode
    417                         if (!mCarModeEnabled) {
    418                             Secure.putIntForUser(getContext().getContentResolver(),
    419                                     Secure.UI_NIGHT_MODE, mode, user);
    420 
    421                             if (UserManager.get(getContext()).isPrimaryUser()) {
    422                                 SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME,
    423                                         Integer.toString(mode));
    424                             }
    425                         }
    426 
    427                         mNightMode = mode;
    428                         updateLocked(0, 0);
    429                     }
    430                 }
    431             } finally {
    432                 Binder.restoreCallingIdentity(ident);
    433             }
    434         }
    435 
    436         @Override
    437         public int getNightMode() {
    438             synchronized (mLock) {
    439                 return mNightMode;
    440             }
    441         }
    442 
    443         @Override
    444         public boolean isUiModeLocked() {
    445             synchronized (mLock) {
    446                 return mUiModeLocked;
    447             }
    448         }
    449 
    450         @Override
    451         public boolean isNightModeLocked() {
    452             synchronized (mLock) {
    453                 return mNightModeLocked;
    454             }
    455         }
    456 
    457         @Override
    458         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
    459                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
    460             new Shell(mService).exec(mService, in, out, err, args, callback, resultReceiver);
    461         }
    462 
    463         @Override
    464         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    465             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
    466             dumpImpl(pw);
    467         }
    468     };
    469 
    470     void dumpImpl(PrintWriter pw) {
    471         synchronized (mLock) {
    472             pw.println("Current UI Mode Service state:");
    473             pw.print("  mDockState="); pw.print(mDockState);
    474                     pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
    475             pw.print("  mNightMode="); pw.print(mNightMode); pw.print(" (");
    476                     pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
    477                     pw.print(" mNightModeLocked="); pw.print(mNightModeLocked);
    478                     pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
    479                     pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
    480                     pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
    481                     pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);
    482             pw.print("  mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
    483                     pw.print(" mUiModeLocked="); pw.print(mUiModeLocked);
    484                     pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
    485             pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
    486                     pw.print(" mSystemReady="); pw.println(mSystemReady);
    487             if (mTwilightManager != null) {
    488                 // We may not have a TwilightManager.
    489                 pw.print("  mTwilightService.getLastTwilightState()=");
    490                 pw.println(mTwilightManager.getLastTwilightState());
    491             }
    492         }
    493     }
    494 
    495     @Override
    496     public void onBootPhase(int phase) {
    497         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
    498             synchronized (mLock) {
    499                 mTwilightManager = getLocalService(TwilightManager.class);
    500                 mSystemReady = true;
    501                 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
    502                 updateComputedNightModeLocked();
    503                 registerVrStateListener();
    504                 updateLocked(0, 0);
    505             }
    506         }
    507     }
    508 
    509     void setCarModeLocked(boolean enabled, int flags) {
    510         if (mCarModeEnabled != enabled) {
    511             mCarModeEnabled = enabled;
    512 
    513             // When exiting car mode, restore night mode from settings
    514             if (!mCarModeEnabled) {
    515                 Context context = getContext();
    516                 updateNightModeFromSettings(context,
    517                         context.getResources(),
    518                         UserHandle.getCallingUserId());
    519             }
    520         }
    521         mCarModeEnableFlags = flags;
    522     }
    523 
    524     private void updateDockState(int newState) {
    525         synchronized (mLock) {
    526             if (newState != mDockState) {
    527                 mDockState = newState;
    528                 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0);
    529                 if (mSystemReady) {
    530                     updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0);
    531                 }
    532             }
    533         }
    534     }
    535 
    536     private static boolean isDeskDockState(int state) {
    537         switch (state) {
    538             case Intent.EXTRA_DOCK_STATE_DESK:
    539             case Intent.EXTRA_DOCK_STATE_LE_DESK:
    540             case Intent.EXTRA_DOCK_STATE_HE_DESK:
    541                 return true;
    542             default:
    543                 return false;
    544         }
    545     }
    546 
    547     private void updateConfigurationLocked() {
    548         int uiMode = mDefaultUiModeType;
    549         if (mUiModeLocked) {
    550             // no-op, keeps default one
    551         } else if (mTelevision) {
    552             uiMode = Configuration.UI_MODE_TYPE_TELEVISION;
    553         } else if (mWatch) {
    554             uiMode = Configuration.UI_MODE_TYPE_WATCH;
    555         } else if (mCarModeEnabled) {
    556             uiMode = Configuration.UI_MODE_TYPE_CAR;
    557         } else if (isDeskDockState(mDockState)) {
    558             uiMode = Configuration.UI_MODE_TYPE_DESK;
    559         } else if (mVrHeadset) {
    560             uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
    561         }
    562 
    563         if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
    564             if (mTwilightManager != null) {
    565                 mTwilightManager.registerListener(mTwilightListener, mHandler);
    566             }
    567             updateComputedNightModeLocked();
    568             uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
    569                     : Configuration.UI_MODE_NIGHT_NO;
    570         } else {
    571             if (mTwilightManager != null) {
    572                 mTwilightManager.unregisterListener(mTwilightListener);
    573             }
    574             uiMode |= mNightMode << 4;
    575         }
    576 
    577         // Override night mode in power save mode if not in car mode
    578         if (mPowerSave && !mCarModeEnabled) {
    579             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
    580             uiMode |= Configuration.UI_MODE_NIGHT_YES;
    581         }
    582 
    583         if (LOG) {
    584             Slog.d(TAG,
    585                 "updateConfigurationLocked: mDockState=" + mDockState
    586                 + "; mCarMode=" + mCarModeEnabled
    587                 + "; mNightMode=" + mNightMode
    588                 + "; uiMode=" + uiMode);
    589         }
    590 
    591         mCurUiMode = uiMode;
    592         if (!mHoldingConfiguration) {
    593             mConfiguration.uiMode = uiMode;
    594         }
    595     }
    596 
    597     private void sendConfigurationLocked() {
    598         if (mSetUiMode != mConfiguration.uiMode) {
    599             mSetUiMode = mConfiguration.uiMode;
    600 
    601             try {
    602                 ActivityTaskManager.getService().updateConfiguration(mConfiguration);
    603             } catch (RemoteException e) {
    604                 Slog.w(TAG, "Failure communicating with activity manager", e);
    605             }
    606         }
    607     }
    608 
    609     void updateLocked(int enableFlags, int disableFlags) {
    610         String action = null;
    611         String oldAction = null;
    612         if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
    613             adjustStatusBarCarModeLocked();
    614             oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
    615         } else if (isDeskDockState(mLastBroadcastState)) {
    616             oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
    617         }
    618 
    619         if (mCarModeEnabled) {
    620             if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
    621                 adjustStatusBarCarModeLocked();
    622                 if (oldAction != null) {
    623                     sendForegroundBroadcastToAllUsers(oldAction);
    624                 }
    625                 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
    626                 action = UiModeManager.ACTION_ENTER_CAR_MODE;
    627             }
    628         } else if (isDeskDockState(mDockState)) {
    629             if (!isDeskDockState(mLastBroadcastState)) {
    630                 if (oldAction != null) {
    631                     sendForegroundBroadcastToAllUsers(oldAction);
    632                 }
    633                 mLastBroadcastState = mDockState;
    634                 action = UiModeManager.ACTION_ENTER_DESK_MODE;
    635             }
    636         } else {
    637             mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
    638             action = oldAction;
    639         }
    640 
    641         if (action != null) {
    642             if (LOG) {
    643                 Slog.v(TAG, String.format(
    644                     "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x",
    645                     action, enableFlags, disableFlags));
    646             }
    647 
    648             // Send the ordered broadcast; the result receiver will receive after all
    649             // broadcasts have been sent. If any broadcast receiver changes the result
    650             // code from the initial value of RESULT_OK, then the result receiver will
    651             // not launch the corresponding dock application. This gives apps a chance
    652             // to override the behavior and stay in their app even when the device is
    653             // placed into a dock.
    654             Intent intent = new Intent(action);
    655             intent.putExtra("enableFlags", enableFlags);
    656             intent.putExtra("disableFlags", disableFlags);
    657             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    658             getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
    659                     mResultReceiver, null, Activity.RESULT_OK, null, null);
    660 
    661             // Attempting to make this transition a little more clean, we are going
    662             // to hold off on doing a configuration change until we have finished
    663             // the broadcast and started the home activity.
    664             mHoldingConfiguration = true;
    665             updateConfigurationLocked();
    666         } else {
    667             String category = null;
    668             if (mCarModeEnabled) {
    669                 if (mEnableCarDockLaunch
    670                         && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
    671                     category = Intent.CATEGORY_CAR_DOCK;
    672                 }
    673             } else if (isDeskDockState(mDockState)) {
    674                 if (ENABLE_LAUNCH_DESK_DOCK_APP
    675                         && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
    676                     category = Intent.CATEGORY_DESK_DOCK;
    677                 }
    678             } else {
    679                 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
    680                     category = Intent.CATEGORY_HOME;
    681                 }
    682             }
    683 
    684             if (LOG) {
    685                 Slog.v(TAG, "updateLocked: null action, mDockState="
    686                         + mDockState +", category=" + category);
    687             }
    688 
    689             sendConfigurationAndStartDreamOrDockAppLocked(category);
    690         }
    691 
    692         // keep screen on when charging and in car mode
    693         boolean keepScreenOn = mCharging &&
    694                 ((mCarModeEnabled && mCarModeKeepsScreenOn &&
    695                   (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
    696                  (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
    697         if (keepScreenOn != mWakeLock.isHeld()) {
    698             if (keepScreenOn) {
    699                 mWakeLock.acquire();
    700             } else {
    701                 mWakeLock.release();
    702             }
    703         }
    704     }
    705 
    706     private void sendForegroundBroadcastToAllUsers(String action) {
    707         getContext().sendBroadcastAsUser(new Intent(action)
    708                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL);
    709     }
    710 
    711     private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) {
    712         // Launch a dock activity
    713         String category = null;
    714         if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) {
    715             // Only launch car home when car mode is enabled and the caller
    716             // has asked us to switch to it.
    717             if (mEnableCarDockLaunch
    718                     && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
    719                 category = Intent.CATEGORY_CAR_DOCK;
    720             }
    721         } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) {
    722             // Only launch car home when desk mode is enabled and the caller
    723             // has asked us to switch to it.  Currently re-using the car
    724             // mode flag since we don't have a formal API for "desk mode".
    725             if (ENABLE_LAUNCH_DESK_DOCK_APP
    726                     && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
    727                 category = Intent.CATEGORY_DESK_DOCK;
    728             }
    729         } else {
    730             // Launch the standard home app if requested.
    731             if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
    732                 category = Intent.CATEGORY_HOME;
    733             }
    734         }
    735 
    736         if (LOG) {
    737             Slog.v(TAG, String.format(
    738                 "Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, "
    739                     + "category=%s",
    740                 action, enableFlags, disableFlags, category));
    741         }
    742 
    743         sendConfigurationAndStartDreamOrDockAppLocked(category);
    744     }
    745 
    746     private void sendConfigurationAndStartDreamOrDockAppLocked(String category) {
    747         // Update the configuration but don't send it yet.
    748         mHoldingConfiguration = false;
    749         updateConfigurationLocked();
    750 
    751         // Start the dock app, if there is one.
    752         boolean dockAppStarted = false;
    753         if (category != null) {
    754             // Now we are going to be careful about switching the
    755             // configuration and starting the activity -- we need to
    756             // do this in a specific order under control of the
    757             // activity manager, to do it cleanly.  So compute the
    758             // new config, but don't set it yet, and let the
    759             // activity manager take care of both the start and config
    760             // change.
    761             Intent homeIntent = buildHomeIntent(category);
    762             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
    763                 try {
    764                     int result = ActivityTaskManager.getService().startActivityWithConfig(
    765                             null, null, homeIntent, null, null, null, 0, 0,
    766                             mConfiguration, null, UserHandle.USER_CURRENT);
    767                     if (ActivityManager.isStartResultSuccessful(result)) {
    768                         dockAppStarted = true;
    769                     } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
    770                         Slog.e(TAG, "Could not start dock app: " + homeIntent
    771                                 + ", startActivityWithConfig result " + result);
    772                     }
    773                 } catch (RemoteException ex) {
    774                     Slog.e(TAG, "Could not start dock app: " + homeIntent, ex);
    775                 }
    776             }
    777         }
    778 
    779         // Send the new configuration.
    780         sendConfigurationLocked();
    781 
    782         // If we did not start a dock app, then start dreaming if supported.
    783         if (category != null && !dockAppStarted) {
    784             Sandman.startDreamWhenDockedIfAppropriate(getContext());
    785         }
    786     }
    787 
    788     private void adjustStatusBarCarModeLocked() {
    789         final Context context = getContext();
    790         if (mStatusBarManager == null) {
    791             mStatusBarManager = (StatusBarManager)
    792                     context.getSystemService(Context.STATUS_BAR_SERVICE);
    793         }
    794 
    795         // Fear not: StatusBarManagerService manages a list of requests to disable
    796         // features of the status bar; these are ORed together to form the
    797         // active disabled list. So if (for example) the device is locked and
    798         // the status bar should be totally disabled, the calls below will
    799         // have no effect until the device is unlocked.
    800         if (mStatusBarManager != null) {
    801             mStatusBarManager.disable(mCarModeEnabled
    802                 ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
    803                 : StatusBarManager.DISABLE_NONE);
    804         }
    805 
    806         if (mNotificationManager == null) {
    807             mNotificationManager = (NotificationManager)
    808                     context.getSystemService(Context.NOTIFICATION_SERVICE);
    809         }
    810 
    811         if (mNotificationManager != null) {
    812             if (mCarModeEnabled) {
    813                 Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class);
    814 
    815                 Notification.Builder n =
    816                         new Notification.Builder(context, SystemNotificationChannels.CAR_MODE)
    817                         .setSmallIcon(R.drawable.stat_notify_car_mode)
    818                         .setDefaults(Notification.DEFAULT_LIGHTS)
    819                         .setOngoing(true)
    820                         .setWhen(0)
    821                         .setColor(context.getColor(
    822                                 com.android.internal.R.color.system_notification_accent_color))
    823                         .setContentTitle(
    824                                 context.getString(R.string.car_mode_disable_notification_title))
    825                         .setContentText(
    826                                 context.getString(R.string.car_mode_disable_notification_message))
    827                         .setContentIntent(
    828                                 PendingIntent.getActivityAsUser(context, 0, carModeOffIntent, 0,
    829                                         null, UserHandle.CURRENT));
    830                 mNotificationManager.notifyAsUser(null,
    831                         SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL);
    832             } else {
    833                 mNotificationManager.cancelAsUser(null,
    834                         SystemMessage.NOTE_CAR_MODE_DISABLE, UserHandle.ALL);
    835             }
    836         }
    837     }
    838 
    839     private void updateComputedNightModeLocked() {
    840         if (mTwilightManager != null) {
    841             TwilightState state = mTwilightManager.getLastTwilightState();
    842             if (state != null) {
    843                 mComputedNightMode = state.isNight();
    844             }
    845         }
    846     }
    847 
    848     private void registerVrStateListener() {
    849         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
    850                 Context.VR_SERVICE));
    851         try {
    852             if (vrManager != null) {
    853                 vrManager.registerListener(mVrStateCallbacks);
    854             }
    855         } catch (RemoteException e) {
    856             Slog.e(TAG, "Failed to register VR mode state listener: " + e);
    857         }
    858     }
    859 
    860     /**
    861      * Handles "adb shell" commands.
    862      */
    863     private static class Shell extends ShellCommand {
    864         public static final String NIGHT_MODE_STR_YES = "yes";
    865         public static final String NIGHT_MODE_STR_NO = "no";
    866         public static final String NIGHT_MODE_STR_AUTO = "auto";
    867         public static final String NIGHT_MODE_STR_UNKNOWN = "unknown";
    868         private final IUiModeManager mInterface;
    869 
    870         Shell(IUiModeManager iface) {
    871             mInterface = iface;
    872         }
    873 
    874         @Override
    875         public void onHelp() {
    876             final PrintWriter pw = getOutPrintWriter();
    877             pw.println("UiModeManager service (uimode) commands:");
    878             pw.println("  help");
    879             pw.println("    Print this help text.");
    880             pw.println("  night [yes|no|auto]");
    881             pw.println("    Set or read night mode.");
    882         }
    883 
    884         @Override
    885         public int onCommand(String cmd) {
    886             if (cmd == null) {
    887                 return handleDefaultCommands(cmd);
    888             }
    889 
    890             try {
    891                 switch (cmd) {
    892                     case "night":
    893                         return handleNightMode();
    894                     default:
    895                         return handleDefaultCommands(cmd);
    896                 }
    897             } catch (RemoteException e) {
    898                 final PrintWriter err = getErrPrintWriter();
    899                 err.println("Remote exception: " + e);
    900             }
    901             return -1;
    902         }
    903 
    904         private int handleNightMode() throws RemoteException {
    905             final PrintWriter err = getErrPrintWriter();
    906             final String modeStr = getNextArg();
    907             if (modeStr == null) {
    908                 printCurrentNightMode();
    909                 return 0;
    910             }
    911 
    912             final int mode = strToNightMode(modeStr);
    913             if (mode >= 0) {
    914                 mInterface.setNightMode(mode);
    915                 printCurrentNightMode();
    916                 return 0;
    917             } else {
    918                 err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '"
    919                         + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO + "'");
    920                 return -1;
    921             }
    922         }
    923 
    924         private void printCurrentNightMode() throws RemoteException {
    925             final PrintWriter pw = getOutPrintWriter();
    926             final int currMode = mInterface.getNightMode();
    927             final String currModeStr = nightModeToStr(currMode);
    928             pw.println("Night mode: " + currModeStr);
    929         }
    930 
    931         private static String nightModeToStr(int mode) {
    932             switch (mode) {
    933                 case UiModeManager.MODE_NIGHT_YES:
    934                     return NIGHT_MODE_STR_YES;
    935                 case UiModeManager.MODE_NIGHT_NO:
    936                     return NIGHT_MODE_STR_NO;
    937                 case UiModeManager.MODE_NIGHT_AUTO:
    938                     return NIGHT_MODE_STR_AUTO;
    939                 default:
    940                     return NIGHT_MODE_STR_UNKNOWN;
    941             }
    942         }
    943 
    944         private static int strToNightMode(String modeStr) {
    945             switch (modeStr) {
    946                 case NIGHT_MODE_STR_YES:
    947                     return UiModeManager.MODE_NIGHT_YES;
    948                 case NIGHT_MODE_STR_NO:
    949                     return UiModeManager.MODE_NIGHT_NO;
    950                 case NIGHT_MODE_STR_AUTO:
    951                     return UiModeManager.MODE_NIGHT_AUTO;
    952                 default:
    953                     return -1;
    954             }
    955         }
    956     }
    957 
    958     public final class LocalService extends UiModeManagerInternal {
    959 
    960         @Override
    961         public boolean isNightMode() {
    962             synchronized (mLock) {
    963                 final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0;
    964                 if (LOG) {
    965                     Slog.d(TAG,
    966                         "LocalService.isNightMode(): mNightMode=" + mNightMode
    967                         + "; mComputedNightMode=" + mComputedNightMode
    968                         + "; uiMode=" + mConfiguration.uiMode
    969                         + "; isIt=" + isIt);
    970                 }
    971                 return isIt;
    972             }
    973         }
    974     }
    975 
    976     private final class UserSwitchedReceiver extends BroadcastReceiver {
    977         @Override
    978         public void onReceive(Context context, Intent intent) {
    979             synchronized (mLock) {
    980                 final int currentId = intent.getIntExtra(
    981                         Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM);
    982                 // only update if the value is actually changed
    983                 if (updateNightModeFromSettings(context, context.getResources(), currentId)) {
    984                     updateLocked(0, 0);
    985                 }
    986             }
    987         }
    988     }
    989 }
    990