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.IntRange;
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.app.ActivityManager;
23 import android.app.ActivityTaskManager;
24 import android.app.AlarmManager;
25 import android.app.IUiModeManager;
26 import android.app.Notification;
27 import android.app.NotificationManager;
28 import android.app.PendingIntent;
29 import android.app.StatusBarManager;
30 import android.app.UiModeManager;
31 import android.content.BroadcastReceiver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.pm.PackageManager;
36 import android.content.res.Configuration;
37 import android.content.res.Resources;
38 import android.database.ContentObserver;
39 import android.net.Uri;
40 import android.os.BatteryManager;
41 import android.os.Binder;
42 import android.os.Handler;
43 import android.os.PowerManager;
44 import android.os.PowerManager.ServiceType;
45 import android.os.PowerManagerInternal;
46 import android.os.Process;
47 import android.os.RemoteException;
48 import android.os.ResultReceiver;
49 import android.os.ServiceManager;
50 import android.os.ShellCallback;
51 import android.os.ShellCommand;
52 import android.os.SystemProperties;
53 import android.os.UserHandle;
54 import android.provider.Settings.Secure;
55 import android.service.dreams.Sandman;
56 import android.service.vr.IVrManager;
57 import android.service.vr.IVrStateCallbacks;
58 import android.util.ArraySet;
59 import android.util.Slog;
60 
61 import com.android.internal.R;
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.app.DisableCarModeActivity;
64 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
65 import com.android.internal.notification.SystemNotificationChannels;
66 import com.android.internal.util.DumpUtils;
67 import com.android.server.twilight.TwilightListener;
68 import com.android.server.twilight.TwilightManager;
69 import com.android.server.twilight.TwilightState;
70 import com.android.server.wm.WindowManagerInternal;
71 
72 import java.io.FileDescriptor;
73 import java.io.PrintWriter;
74 import java.time.DateTimeException;
75 import java.time.LocalDateTime;
76 import java.time.LocalTime;
77 import java.time.ZoneId;
78 import java.util.Arrays;
79 import java.util.HashMap;
80 import java.util.List;
81 import java.util.Map;
82 import java.util.Set;
83 
84 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
85 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
86 import static android.app.UiModeManager.MODE_NIGHT_YES;
87 import static android.os.UserHandle.USER_SYSTEM;
88 import static android.util.TimeUtils.isTimeBetween;
89 
90 final class UiModeManagerService extends SystemService {
91     private static final String TAG = UiModeManager.class.getSimpleName();
92     private static final boolean LOG = false;
93 
94     // Enable launching of applications when entering the dock.
95     private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
96     private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme";
97 
98     final Object mLock = new Object();
99     private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
100 
101     private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
102     private int mNightMode = UiModeManager.MODE_NIGHT_NO;
103     private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
104     private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
105     private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
106     private LocalTime mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME;
107 
108     private Map<Integer, String> mCarModePackagePriority = new HashMap<>();
109     private boolean mCarModeEnabled = false;
110     private boolean mCharging = false;
111     private boolean mPowerSave = false;
112     // Do not change configuration now. wait until screen turns off.
113     // This prevents jank and activity restart when the user
114     // is actively using the device
115     private boolean mWaitForScreenOff = false;
116     private int mDefaultUiModeType;
117     private boolean mCarModeKeepsScreenOn;
118     private boolean mDeskModeKeepsScreenOn;
119     private boolean mTelevision;
120     private boolean mCar;
121     private boolean mWatch;
122     private boolean mVrHeadset;
123     private boolean mComputedNightMode;
124     private int mCarModeEnableFlags;
125     private boolean mSetupWizardComplete;
126 
127     // flag set by resource, whether to enable Car dock launch when starting car mode.
128     private boolean mEnableCarDockLaunch = true;
129     // flag set by resource, whether to lock UI mode to the default one or not.
130     private boolean mUiModeLocked = false;
131     // flag set by resource, whether to night mode change for normal all or not.
132     private boolean mNightModeLocked = false;
133 
134     int mCurUiMode = 0;
135     private int mSetUiMode = 0;
136     private boolean mHoldingConfiguration = false;
137 
138     private Configuration mConfiguration = new Configuration();
139     boolean mSystemReady;
140 
141     private final Handler mHandler = new Handler();
142 
143     private TwilightManager mTwilightManager;
144     private NotificationManager mNotificationManager;
145     private StatusBarManager mStatusBarManager;
146     private WindowManagerInternal mWindowManager;
147     private AlarmManager mAlarmManager;
148     private PowerManager mPowerManager;
149 
150     // In automatic scheduling, the user is able
151     // to override the computed night mode until the two match
152     // Example: Activate dark mode in the day time until sunrise the next day
153     private boolean mOverrideNightModeOn;
154     private boolean mOverrideNightModeOff;
155     private int mOverrideNightModeUser = USER_SYSTEM;
156 
157     private PowerManager.WakeLock mWakeLock;
158 
159     private final LocalService mLocalService = new LocalService();
160     private PowerManagerInternal mLocalPowerManager;
161 
UiModeManagerService(Context context)162     public UiModeManagerService(Context context) {
163         super(context);
164         mConfiguration.setToDefaults();
165     }
166 
167     @VisibleForTesting
UiModeManagerService(Context context, boolean setupWizardComplete, TwilightManager tm)168     protected UiModeManagerService(Context context, boolean setupWizardComplete,
169             TwilightManager tm) {
170         this(context);
171         mSetupWizardComplete = setupWizardComplete;
172         mTwilightManager = tm;
173     }
174 
buildHomeIntent(String category)175     private static Intent buildHomeIntent(String category) {
176         Intent intent = new Intent(Intent.ACTION_MAIN);
177         intent.addCategory(category);
178         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
179                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
180         return intent;
181     }
182 
183     // The broadcast receiver which receives the result of the ordered broadcast sent when
184     // the dock state changes. The original ordered broadcast is sent with an initial result
185     // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
186     // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
187     private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
188         @Override
189         public void onReceive(Context context, Intent intent) {
190             if (getResultCode() != Activity.RESULT_OK) {
191                 if (LOG) {
192                     Slog.v(TAG, "Handling broadcast result for action " + intent.getAction()
193                             + ": canceled: " + getResultCode());
194                 }
195                 return;
196             }
197 
198             final int enableFlags = intent.getIntExtra("enableFlags", 0);
199             final int disableFlags = intent.getIntExtra("disableFlags", 0);
200             synchronized (mLock) {
201                 updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags);
202             }
203         }
204     };
205 
206     private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
207         @Override
208         public void onReceive(Context context, Intent intent) {
209             int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
210                     Intent.EXTRA_DOCK_STATE_UNDOCKED);
211             updateDockState(state);
212         }
213     };
214 
215     private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
216         @Override
217         public void onReceive(Context context, Intent intent) {
218             switch (intent.getAction()) {
219                 case Intent.ACTION_BATTERY_CHANGED:
220                     mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
221                     break;
222             }
223             synchronized (mLock) {
224                 if (mSystemReady) {
225                     updateLocked(0, 0);
226                 }
227             }
228         }
229     };
230 
231     private final TwilightListener mTwilightListener = new TwilightListener() {
232         @Override
233         public void onTwilightStateChanged(@Nullable TwilightState state) {
234             synchronized (mLock) {
235                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) {
236                     if (shouldApplyAutomaticChangesImmediately()) {
237                         updateLocked(0, 0);
238                     } else {
239                         registerScreenOffEventLocked();
240                     }
241                 }
242             }
243         }
244     };
245 
246     /**
247      * DO NOT USE DIRECTLY
248      * see register registerScreenOffEvent and unregisterScreenOffEvent
249      */
250     private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
251         @Override
252         public void onReceive(Context context, Intent intent) {
253             synchronized (mLock) {
254                 // must unregister first before updating
255                 unregisterScreenOffEventLocked();
256                 updateLocked(0, 0);
257             }
258         }
259     };
260 
261     private final BroadcastReceiver mOnTimeChangedHandler = new BroadcastReceiver() {
262         @Override
263         public void onReceive(Context context, Intent intent) {
264             synchronized (mLock) {
265                 updateCustomTimeLocked();
266             }
267         }
268     };
269 
270     private final AlarmManager.OnAlarmListener mCustomTimeListener = () -> {
271         synchronized (mLock) {
272             updateCustomTimeLocked();
273         }
274     };
275 
276     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
277         @Override
278         public void onVrStateChanged(boolean enabled) {
279             synchronized (mLock) {
280                 mVrHeadset = enabled;
281                 if (mSystemReady) {
282                     updateLocked(0, 0);
283                 }
284             }
285         }
286     };
287 
288     private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) {
289         @Override
290         public void onChange(boolean selfChange, Uri uri) {
291             synchronized (mLock) {
292                 // setup wizard is done now so we can unblock
293                 if (setupWizardCompleteForCurrentUser() && !selfChange) {
294                     mSetupWizardComplete = true;
295                     getContext().getContentResolver()
296                             .unregisterContentObserver(mSetupWizardObserver);
297                     // update night mode
298                     Context context = getContext();
299                     updateNightModeFromSettingsLocked(context, context.getResources(),
300                             UserHandle.getCallingUserId());
301                     updateLocked(0, 0);
302                 }
303             }
304         }
305     };
306 
307     private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
308         @Override
309         public void onChange(boolean selfChange, Uri uri) {
310             updateSystemProperties();
311         }
312     };
313 
updateSystemProperties()314     private void updateSystemProperties() {
315         int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
316                 mNightMode, 0);
317         if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
318             mode = MODE_NIGHT_YES;
319         }
320         SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
321     }
322 
323     @Override
onSwitchUser(int userHandle)324     public void onSwitchUser(int userHandle) {
325         super.onSwitchUser(userHandle);
326         getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
327         verifySetupWizardCompleted();
328     }
329 
330     @Override
onBootPhase(int phase)331     public void onBootPhase(int phase) {
332         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
333             synchronized (mLock) {
334                 final Context context = getContext();
335                 mSystemReady = true;
336                 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
337                 mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
338                 mWindowManager = LocalServices.getService(WindowManagerInternal.class);
339                 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
340                 TwilightManager twilightManager = getLocalService(TwilightManager.class);
341                 if (twilightManager != null) mTwilightManager = twilightManager;
342                 mLocalPowerManager =
343                         LocalServices.getService(PowerManagerInternal.class);
344                 initPowerSave();
345                 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
346                 registerVrStateListener();
347                 // register listeners
348                 context.getContentResolver()
349                         .registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
350                                 false, mDarkThemeObserver, 0);
351                 context.registerReceiver(mDockModeReceiver,
352                         new IntentFilter(Intent.ACTION_DOCK_EVENT));
353                 IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
354                 context.registerReceiver(mBatteryReceiver, batteryFilter);
355                 IntentFilter filter = new IntentFilter();
356                 filter.addAction(Intent.ACTION_USER_SWITCHED);
357                 context.registerReceiver(mSettingsRestored,
358                         new IntentFilter(Intent.ACTION_SETTING_RESTORED));
359                 context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
360                 updateConfigurationLocked();
361                 applyConfigurationExternallyLocked();
362             }
363         }
364     }
365 
366     @Override
onStart()367     public void onStart() {
368         final Context context = getContext();
369         // If setup isn't complete for this user listen for completion so we can unblock
370         // being able to send a night mode configuration change event
371         verifySetupWizardCompleted();
372 
373         final Resources res = context.getResources();
374         mNightMode = res.getInteger(
375                 com.android.internal.R.integer.config_defaultNightMode);
376         mDefaultUiModeType = res.getInteger(
377                 com.android.internal.R.integer.config_defaultUiModeType);
378         mCarModeKeepsScreenOn = (res.getInteger(
379                 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
380         mDeskModeKeepsScreenOn = (res.getInteger(
381                 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
382         mEnableCarDockLaunch = res.getBoolean(
383                 com.android.internal.R.bool.config_enableCarDockHomeLaunch);
384         mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode);
385         mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode);
386         final PackageManager pm = context.getPackageManager();
387         mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
388                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
389         mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
390         mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
391 
392         // Update the initial, static configurations.
393         SystemServerInitThreadPool.submit(() -> {
394             synchronized (mLock) {
395                 TwilightManager twilightManager = getLocalService(TwilightManager.class);
396                 if (twilightManager != null) mTwilightManager = twilightManager;
397                 updateNightModeFromSettingsLocked(context, res, UserHandle.getCallingUserId());
398                 updateSystemProperties();
399             }
400 
401         }, TAG + ".onStart");
402         publishBinderService(Context.UI_MODE_SERVICE, mService);
403         publishLocalService(UiModeManagerInternal.class, mLocalService);
404     }
405 
406     private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() {
407         @Override
408         public void onReceive(Context context, Intent intent) {
409             List<String> settings = Arrays.asList(
410                     Secure.UI_NIGHT_MODE, Secure.DARK_THEME_CUSTOM_START_TIME,
411                     Secure.DARK_THEME_CUSTOM_END_TIME);
412             if (settings.contains(intent.getExtras().getCharSequence(Intent.EXTRA_SETTING_NAME))) {
413                 synchronized (mLock) {
414                     updateNightModeFromSettingsLocked(context, context.getResources(),
415                             UserHandle.getCallingUserId());
416                     updateConfigurationLocked();
417                 }
418             }
419         }
420     };
421 
initPowerSave()422     private void initPowerSave() {
423         mPowerSave =
424                 mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE)
425                         .batterySaverEnabled;
426         mLocalPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE, state -> {
427             synchronized (mLock) {
428                 if (mPowerSave == state.batterySaverEnabled) {
429                     return;
430                 }
431                 mPowerSave = state.batterySaverEnabled;
432                 if (mSystemReady) {
433                     updateLocked(0, 0);
434                 }
435             }
436         });
437     }
438 
439     @VisibleForTesting
getService()440     protected IUiModeManager getService() {
441         return mService;
442     }
443 
444     @VisibleForTesting
getConfiguration()445     protected Configuration getConfiguration() {
446         return mConfiguration;
447     }
448 
449     // Records whether setup wizard has happened or not and adds an observer for this user if not.
verifySetupWizardCompleted()450     private void verifySetupWizardCompleted() {
451         final Context context = getContext();
452         final int userId = UserHandle.getCallingUserId();
453         if (!setupWizardCompleteForCurrentUser()) {
454             mSetupWizardComplete = false;
455             context.getContentResolver().registerContentObserver(
456                     Secure.getUriFor(
457                             Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId);
458         } else {
459             mSetupWizardComplete = true;
460         }
461     }
462 
setupWizardCompleteForCurrentUser()463     private boolean setupWizardCompleteForCurrentUser() {
464         return Secure.getIntForUser(getContext().getContentResolver(),
465                 Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
466     }
467 
updateCustomTimeLocked()468     private void updateCustomTimeLocked() {
469         if (mNightMode != MODE_NIGHT_CUSTOM) return;
470         if (shouldApplyAutomaticChangesImmediately()) {
471             updateLocked(0, 0);
472         } else {
473             registerScreenOffEventLocked();
474         }
475         scheduleNextCustomTimeListener();
476     }
477 
478     /**
479      * Updates the night mode setting in Settings.Global and returns if the value was successfully
480      * changed.
481      *
482      * @param context A valid context
483      * @param res     A valid resource object
484      * @param userId  The user to update the setting for
485      * @return True if the new value is different from the old value. False otherwise.
486      */
updateNightModeFromSettingsLocked(Context context, Resources res, int userId)487     private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) {
488         int oldNightMode = mNightMode;
489         if (mSetupWizardComplete) {
490             mNightMode = Secure.getIntForUser(context.getContentResolver(),
491                     Secure.UI_NIGHT_MODE, mNightMode, userId);
492             mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
493                     Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0;
494             mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(),
495                     Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0;
496             mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay(
497                     Secure.getLongForUser(context.getContentResolver(),
498                             Secure.DARK_THEME_CUSTOM_START_TIME,
499                             DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000);
500             mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay(
501                     Secure.getLongForUser(context.getContentResolver(),
502                             Secure.DARK_THEME_CUSTOM_END_TIME,
503                             DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
504         }
505 
506         return oldNightMode != mNightMode;
507     }
508 
toMilliSeconds(LocalTime t)509     private static long toMilliSeconds(LocalTime t) {
510         return t.toNanoOfDay() / 1000;
511     }
512 
fromMilliseconds(long t)513     private static LocalTime fromMilliseconds(long t) {
514         return LocalTime.ofNanoOfDay(t * 1000);
515     }
516 
registerScreenOffEventLocked()517     private void registerScreenOffEventLocked() {
518         if (mPowerSave) return;
519         mWaitForScreenOff = true;
520         final IntentFilter intentFilter =
521                 new IntentFilter(Intent.ACTION_SCREEN_OFF);
522         getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
523     }
524 
cancelCustomAlarm()525     private void cancelCustomAlarm() {
526         mAlarmManager.cancel(mCustomTimeListener);
527     }
528 
unregisterScreenOffEventLocked()529     private void unregisterScreenOffEventLocked() {
530         mWaitForScreenOff = false;
531         try {
532             getContext().unregisterReceiver(mOnScreenOffHandler);
533         } catch (IllegalArgumentException e) {
534             // we ignore this exception if the receiver is unregistered already.
535         }
536     }
537 
registerTimeChangeEvent()538     private void registerTimeChangeEvent() {
539         final IntentFilter intentFilter =
540                 new IntentFilter(Intent.ACTION_TIME_CHANGED);
541         intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
542         getContext().registerReceiver(mOnTimeChangedHandler, intentFilter);
543     }
544 
unregisterTimeChangeEvent()545     private void unregisterTimeChangeEvent() {
546         try {
547             getContext().unregisterReceiver(mOnTimeChangedHandler);
548         } catch (IllegalArgumentException e) {
549             // we ignore this exception if the receiver is unregistered already.
550         }
551     }
552 
553     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
554         @Override
555         public void enableCarMode(@UiModeManager.EnableCarMode int flags,
556                 @IntRange(from = 0) int priority, String callingPackage) {
557             if (isUiModeLocked()) {
558                 Slog.e(TAG, "enableCarMode while UI mode is locked");
559                 return;
560             }
561 
562             if (priority != UiModeManager.DEFAULT_PRIORITY
563                     && getContext().checkCallingOrSelfPermission(
564                     android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED)
565                     != PackageManager.PERMISSION_GRANTED) {
566                 throw new SecurityException("Enabling car mode with a priority requires "
567                         + "permission ENTER_CAR_MODE_PRIORITIZED");
568             }
569 
570             final long ident = Binder.clearCallingIdentity();
571             try {
572                 synchronized (mLock) {
573                     setCarModeLocked(true, flags, priority, callingPackage);
574                     if (mSystemReady) {
575                         updateLocked(flags, 0);
576                     }
577                 }
578             } finally {
579                 Binder.restoreCallingIdentity(ident);
580             }
581         }
582 
583         /**
584          * This method is only kept around for the time being; the AIDL has an UnsupportedAppUsage
585          * tag which means this method is technically considered part of the greylist "API".
586          * @param flags
587          */
588         @Override
589         public void disableCarMode(@UiModeManager.DisableCarMode int flags) {
590             disableCarModeByCallingPackage(flags, null /* callingPackage */);
591         }
592 
593         /**
594          * Handles requests to disable car mode.
595          * @param flags Disable car mode flags
596          * @param callingPackage
597          */
598         @Override
599         public void disableCarModeByCallingPackage(@UiModeManager.DisableCarMode int flags,
600                 String callingPackage) {
601             if (isUiModeLocked()) {
602                 Slog.e(TAG, "disableCarMode while UI mode is locked");
603                 return;
604             }
605 
606             // If the caller is the system, we will allow the DISABLE_CAR_MODE_ALL_PRIORITIES car
607             // mode flag to be specified; this is so that the user can disable car mode at all
608             // priorities using the persistent notification.
609             boolean isSystemCaller = Binder.getCallingUid() == Process.SYSTEM_UID;
610             final int carModeFlags =
611                     isSystemCaller ? flags : flags & ~UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES;
612 
613             final long ident = Binder.clearCallingIdentity();
614             try {
615                 synchronized (mLock) {
616                     // Determine if the caller has enabled car mode at a priority other than the
617                     // default one.  If they have, then attempt to disable at that priority.
618                     int priority = mCarModePackagePriority.entrySet()
619                             .stream()
620                             .filter(e -> e.getValue().equals(callingPackage))
621                             .findFirst()
622                             .map(Map.Entry::getKey)
623                             .orElse(UiModeManager.DEFAULT_PRIORITY);
624 
625                     setCarModeLocked(false, carModeFlags, priority, callingPackage);
626                     if (mSystemReady) {
627                         updateLocked(0, flags);
628                     }
629                 }
630             } finally {
631                 Binder.restoreCallingIdentity(ident);
632             }
633         }
634 
635         @Override
636         public int getCurrentModeType() {
637             final long ident = Binder.clearCallingIdentity();
638             try {
639                 synchronized (mLock) {
640                     return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
641                 }
642             } finally {
643                 Binder.restoreCallingIdentity(ident);
644             }
645         }
646 
647         @Override
648         public void setNightMode(int mode) {
649             if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
650                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
651                     != PackageManager.PERMISSION_GRANTED)) {
652                 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
653                 return;
654             }
655             switch (mode) {
656                 case UiModeManager.MODE_NIGHT_NO:
657                 case UiModeManager.MODE_NIGHT_YES:
658                 case MODE_NIGHT_AUTO:
659                 case MODE_NIGHT_CUSTOM:
660                     break;
661                 default:
662                     throw new IllegalArgumentException("Unknown mode: " + mode);
663             }
664 
665             final int user = UserHandle.getCallingUserId();
666             final long ident = Binder.clearCallingIdentity();
667             try {
668                 synchronized (mLock) {
669                     if (mNightMode != mode) {
670                         if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
671                             unregisterScreenOffEventLocked();
672                             cancelCustomAlarm();
673                         }
674 
675                         mNightMode = mode;
676                         resetNightModeOverrideLocked();
677                         persistNightMode(user);
678                         // on screen off will update configuration instead
679                         if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
680                                 || shouldApplyAutomaticChangesImmediately()) {
681                             unregisterScreenOffEventLocked();
682                             updateLocked(0, 0);
683                         } else {
684                             registerScreenOffEventLocked();
685                         }
686                     }
687                 }
688             } finally {
689                 Binder.restoreCallingIdentity(ident);
690             }
691         }
692 
693         @Override
694         public int getNightMode() {
695             synchronized (mLock) {
696                 return mNightMode;
697             }
698         }
699 
700         @Override
701         public boolean isUiModeLocked() {
702             synchronized (mLock) {
703                 return mUiModeLocked;
704             }
705         }
706 
707         @Override
708         public boolean isNightModeLocked() {
709             synchronized (mLock) {
710                 return mNightModeLocked;
711             }
712         }
713 
714         @Override
715         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
716                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
717             new Shell(mService).exec(mService, in, out, err, args, callback, resultReceiver);
718         }
719 
720         @Override
721         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
722             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
723             dumpImpl(pw);
724         }
725 
726         @Override
727         public boolean setNightModeActivated(boolean active) {
728             synchronized (mLock) {
729                 final int user = UserHandle.getCallingUserId();
730                 final long ident = Binder.clearCallingIdentity();
731                 try {
732                     if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
733                         unregisterScreenOffEventLocked();
734                         mOverrideNightModeOff = !active;
735                         mOverrideNightModeOn = active;
736                         mOverrideNightModeUser = user;
737                         persistNightModeOverrides(user);
738                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
739                             && active) {
740                         mNightMode = UiModeManager.MODE_NIGHT_YES;
741                     } else if (mNightMode == UiModeManager.MODE_NIGHT_YES
742                             && !active) {
743                         mNightMode = UiModeManager.MODE_NIGHT_NO;
744                     }
745                     updateConfigurationLocked();
746                     applyConfigurationExternallyLocked();
747                     persistNightMode(user);
748                     return true;
749                 } finally {
750                     Binder.restoreCallingIdentity(ident);
751                 }
752             }
753         }
754 
755         @Override
756         public long getCustomNightModeStart() {
757             return mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000;
758         }
759 
760         @Override
761         public void setCustomNightModeStart(long time) {
762             if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
763                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
764                     != PackageManager.PERMISSION_GRANTED) {
765                 Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission");
766                 return;
767             }
768             final int user = UserHandle.getCallingUserId();
769             final long ident = Binder.clearCallingIdentity();
770             try {
771                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
772                 if (newTime == null) return;
773                 mCustomAutoNightModeStartMilliseconds = newTime;
774                 persistNightMode(user);
775                 onCustomTimeUpdated(user);
776             } catch (DateTimeException e) {
777                 unregisterScreenOffEventLocked();
778             } finally {
779                 Binder.restoreCallingIdentity(ident);
780             }
781         }
782 
783         @Override
784         public long getCustomNightModeEnd() {
785             return mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000;
786         }
787 
788         @Override
789         public void setCustomNightModeEnd(long time) {
790             if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
791                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
792                     != PackageManager.PERMISSION_GRANTED) {
793                 Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission");
794                 return;
795             }
796             final int user = UserHandle.getCallingUserId();
797             final long ident = Binder.clearCallingIdentity();
798             try {
799                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
800                 if (newTime == null) return;
801                 mCustomAutoNightModeEndMilliseconds = newTime;
802                 onCustomTimeUpdated(user);
803             } catch (DateTimeException e) {
804                 unregisterScreenOffEventLocked();
805             } finally {
806                 Binder.restoreCallingIdentity(ident);
807             }
808         }
809     };
810 
onCustomTimeUpdated(int user)811     private void onCustomTimeUpdated(int user) {
812         persistNightMode(user);
813         if (mNightMode != MODE_NIGHT_CUSTOM) return;
814         if (shouldApplyAutomaticChangesImmediately()) {
815             unregisterScreenOffEventLocked();
816             updateLocked(0, 0);
817         } else {
818             registerScreenOffEventLocked();
819         }
820     }
821 
dumpImpl(PrintWriter pw)822     void dumpImpl(PrintWriter pw) {
823         synchronized (mLock) {
824             pw.println("Current UI Mode Service state:");
825             pw.print("  mDockState="); pw.print(mDockState);
826             pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
827 
828             pw.print("  mNightMode="); pw.print(mNightMode); pw.print(" (");
829             pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
830             pw.print(" mOverrideOn/Off="); pw.print(mOverrideNightModeOn);
831             pw.print("/"); pw.print(mOverrideNightModeOff);
832 
833             pw.print(" mNightModeLocked="); pw.println(mNightModeLocked);
834 
835             pw.print("  mCarModeEnabled="); pw.print(mCarModeEnabled);
836             pw.print(" (carModeApps=");
837             for (Map.Entry<Integer, String> entry : mCarModePackagePriority.entrySet()) {
838                 pw.print(entry.getKey());
839                 pw.print(":");
840                 pw.print(entry.getValue());
841                 pw.print(" ");
842             }
843             pw.println("");
844             pw.print(" waitScreenOff="); pw.print(mWaitForScreenOff);
845             pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
846             pw.print(" customStart="); pw.print(mCustomAutoNightModeStartMilliseconds);
847             pw.print(" customEnd"); pw.print(mCustomAutoNightModeEndMilliseconds);
848             pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
849             pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);
850 
851             pw.print("  mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
852             pw.print(" mUiModeLocked="); pw.print(mUiModeLocked);
853             pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
854 
855             pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
856             pw.print(" mSystemReady="); pw.println(mSystemReady);
857 
858             if (mTwilightManager != null) {
859                 // We may not have a TwilightManager.
860                 pw.print("  mTwilightService.getLastTwilightState()=");
861                 pw.println(mTwilightManager.getLastTwilightState());
862             }
863         }
864     }
865 
866     /**
867      * Updates the global car mode state.
868      * The device is considered to be in car mode if there exists an app at any priority level which
869      * has entered car mode.
870      *
871      * @param enabled {@code true} if the caller wishes to enable car mode, {@code false} otherwise.
872      * @param flags Flags used when enabling/disabling car mode.
873      * @param priority The priority level for entering or exiting car mode; defaults to
874      *                 {@link UiModeManager#DEFAULT_PRIORITY} for callers using
875      *                 {@link UiModeManager#enableCarMode(int)}.  Callers using
876      *                 {@link UiModeManager#enableCarMode(int, int)} may specify a priority.
877      * @param packageName The package name of the app which initiated the request to enable or
878      *                    disable car mode.
879      */
setCarModeLocked(boolean enabled, int flags, int priority, String packageName)880     void setCarModeLocked(boolean enabled, int flags, int priority, String packageName) {
881         if (enabled) {
882             enableCarMode(priority, packageName);
883         } else {
884             disableCarMode(flags, priority, packageName);
885         }
886         boolean isCarModeNowEnabled = isCarModeEnabled();
887 
888         if (mCarModeEnabled != isCarModeNowEnabled) {
889             mCarModeEnabled = isCarModeNowEnabled;
890             // When exiting car mode, restore night mode from settings
891             if (!isCarModeNowEnabled) {
892                 Context context = getContext();
893                 updateNightModeFromSettingsLocked(context,
894                         context.getResources(),
895                         UserHandle.getCallingUserId());
896             }
897         }
898         mCarModeEnableFlags = flags;
899     }
900 
901     /**
902      * Handles disabling car mode.
903      * <p>
904      * Car mode can be disabled at a priority level if any of the following is true:
905      * 1. The priority being disabled is the {@link UiModeManager#DEFAULT_PRIORITY}.
906      * 2. The priority level is enabled and the caller is the app who originally enabled it.
907      * 3. The {@link UiModeManager#DISABLE_CAR_MODE_ALL_PRIORITIES} flag was specified, meaning all
908      *    car mode priorities are disabled.
909      *
910      * @param flags Car mode flags.
911      * @param priority The priority level at which to disable car mode.
912      * @param packageName The calling package which initiated the request.
913      */
disableCarMode(int flags, int priority, String packageName)914     private void disableCarMode(int flags, int priority, String packageName) {
915         boolean isDisableAll = (flags & UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES) != 0;
916         boolean isPriorityTracked = mCarModePackagePriority.keySet().contains(priority);
917         boolean isDefaultPriority = priority == UiModeManager.DEFAULT_PRIORITY;
918         boolean isChangeAllowed =
919                 // Anyone can disable the default priority.
920                 isDefaultPriority
921                 // If priority was enabled, only enabling package can disable it.
922                 || isPriorityTracked && mCarModePackagePriority.get(priority).equals(
923                 packageName)
924                 // Disable all priorities flag can disable all regardless.
925                 || isDisableAll;
926         if (isChangeAllowed) {
927             Slog.d(TAG, "disableCarMode: disabling, priority=" + priority
928                     + ", packageName=" + packageName);
929             if (isDisableAll) {
930                 Set<Map.Entry<Integer, String>> entries =
931                         new ArraySet<>(mCarModePackagePriority.entrySet());
932                 mCarModePackagePriority.clear();
933 
934                 for (Map.Entry<Integer, String> entry : entries) {
935                     notifyCarModeDisabled(entry.getKey(), entry.getValue());
936                 }
937             } else {
938                 mCarModePackagePriority.remove(priority);
939                 notifyCarModeDisabled(priority, packageName);
940             }
941         }
942     }
943 
944     /**
945      * Handles enabling car mode.
946      * <p>
947      * Car mode can be enabled at any priority if it has not already been enabled at that priority.
948      * The calling package is tracked for the first app which enters priority at the
949      * {@link UiModeManager#DEFAULT_PRIORITY}, though any app can disable it at that priority.
950      *
951      * @param priority The priority for enabling car mode.
952      * @param packageName The calling package which initiated the request.
953      */
enableCarMode(int priority, String packageName)954     private void enableCarMode(int priority, String packageName) {
955         boolean isPriorityTracked = mCarModePackagePriority.containsKey(priority);
956         boolean isPackagePresent = mCarModePackagePriority.containsValue(packageName);
957         if (!isPriorityTracked && !isPackagePresent) {
958             Slog.d(TAG, "enableCarMode: enabled at priority=" + priority + ", packageName="
959                     + packageName);
960             mCarModePackagePriority.put(priority, packageName);
961             notifyCarModeEnabled(priority, packageName);
962         } else {
963             Slog.d(TAG, "enableCarMode: car mode at priority " + priority + " already enabled.");
964         }
965 
966     }
967 
notifyCarModeEnabled(int priority, String packageName)968     private void notifyCarModeEnabled(int priority, String packageName) {
969         Intent intent = new Intent(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
970         intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName);
971         intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority);
972         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
973                 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES);
974     }
975 
notifyCarModeDisabled(int priority, String packageName)976     private void notifyCarModeDisabled(int priority, String packageName) {
977         Intent intent = new Intent(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
978         intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName);
979         intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority);
980         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
981                 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES);
982     }
983 
984     /**
985      * Determines if car mode is enabled at any priority level.
986      * @return {@code true} if car mode is enabled, {@code false} otherwise.
987      */
isCarModeEnabled()988     private boolean isCarModeEnabled() {
989         return mCarModePackagePriority.size() > 0;
990     }
991 
updateDockState(int newState)992     private void updateDockState(int newState) {
993         synchronized (mLock) {
994             if (newState != mDockState) {
995                 mDockState = newState;
996                 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0,
997                         UiModeManager.DEFAULT_PRIORITY, "" /* packageName */);
998                 if (mSystemReady) {
999                     updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0);
1000                 }
1001             }
1002         }
1003     }
1004 
isDeskDockState(int state)1005     private static boolean isDeskDockState(int state) {
1006         switch (state) {
1007             case Intent.EXTRA_DOCK_STATE_DESK:
1008             case Intent.EXTRA_DOCK_STATE_LE_DESK:
1009             case Intent.EXTRA_DOCK_STATE_HE_DESK:
1010                 return true;
1011             default:
1012                 return false;
1013         }
1014     }
1015 
persistNightMode(int user)1016     private void persistNightMode(int user) {
1017         // Only persist setting if not in car mode
1018         if (mCarModeEnabled) return;
1019         Secure.putIntForUser(getContext().getContentResolver(),
1020                 Secure.UI_NIGHT_MODE, mNightMode, user);
1021         Secure.putLongForUser(getContext().getContentResolver(),
1022                 Secure.DARK_THEME_CUSTOM_START_TIME,
1023                 mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user);
1024         Secure.putLongForUser(getContext().getContentResolver(),
1025                 Secure.DARK_THEME_CUSTOM_END_TIME,
1026                 mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000, user);
1027     }
1028 
persistNightModeOverrides(int user)1029     private void persistNightModeOverrides(int user) {
1030         // Only persist setting if not in car mode
1031         if (mCarModeEnabled) return;
1032         Secure.putIntForUser(getContext().getContentResolver(),
1033                 Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user);
1034         Secure.putIntForUser(getContext().getContentResolver(),
1035                 Secure.UI_NIGHT_MODE_OVERRIDE_OFF, mOverrideNightModeOff ? 1 : 0, user);
1036     }
1037 
updateConfigurationLocked()1038     private void updateConfigurationLocked() {
1039         int uiMode = mDefaultUiModeType;
1040         if (mUiModeLocked) {
1041             // no-op, keeps default one
1042         } else if (mTelevision) {
1043             uiMode = Configuration.UI_MODE_TYPE_TELEVISION;
1044         } else if (mWatch) {
1045             uiMode = Configuration.UI_MODE_TYPE_WATCH;
1046         } else if (mCarModeEnabled) {
1047             uiMode = Configuration.UI_MODE_TYPE_CAR;
1048         } else if (isDeskDockState(mDockState)) {
1049             uiMode = Configuration.UI_MODE_TYPE_DESK;
1050         } else if (mVrHeadset) {
1051             uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
1052         }
1053 
1054         if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) {
1055             updateComputedNightModeLocked(mNightMode == MODE_NIGHT_YES);
1056         }
1057 
1058         if (mNightMode == MODE_NIGHT_AUTO) {
1059             boolean activateNightMode = mComputedNightMode;
1060             if (mTwilightManager != null) {
1061                 mTwilightManager.registerListener(mTwilightListener, mHandler);
1062                 final TwilightState lastState = mTwilightManager.getLastTwilightState();
1063                 activateNightMode = lastState == null ? mComputedNightMode : lastState.isNight();
1064             }
1065             updateComputedNightModeLocked(activateNightMode);
1066         } else {
1067             if (mTwilightManager != null) {
1068                 mTwilightManager.unregisterListener(mTwilightListener);
1069             }
1070         }
1071 
1072         if (mNightMode == MODE_NIGHT_CUSTOM) {
1073             registerTimeChangeEvent();
1074             final boolean activate = computeCustomNightMode();
1075             updateComputedNightModeLocked(activate);
1076             scheduleNextCustomTimeListener();
1077         } else {
1078             unregisterTimeChangeEvent();
1079         }
1080 
1081         // Override night mode in power save mode if not in car mode
1082         if (mPowerSave && !mCarModeEnabled) {
1083             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
1084             uiMode |= Configuration.UI_MODE_NIGHT_YES;
1085         } else {
1086             uiMode = getComputedUiModeConfiguration(uiMode);
1087         }
1088 
1089         if (LOG) {
1090             Slog.d(TAG,
1091                     "updateConfigurationLocked: mDockState=" + mDockState
1092                     + "; mCarMode=" + mCarModeEnabled
1093                     + "; mNightMode=" + mNightMode
1094                     + "; uiMode=" + uiMode);
1095         }
1096 
1097         mCurUiMode = uiMode;
1098         if (!mHoldingConfiguration && (!mWaitForScreenOff || mPowerSave)) {
1099             mConfiguration.uiMode = uiMode;
1100         }
1101     }
1102 
1103     @UiModeManager.NightMode
getComputedUiModeConfiguration(@iModeManager.NightMode int uiMode)1104     private int getComputedUiModeConfiguration(@UiModeManager.NightMode int uiMode) {
1105         uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
1106                 : Configuration.UI_MODE_NIGHT_NO;
1107         uiMode &= mComputedNightMode ? ~Configuration.UI_MODE_NIGHT_NO
1108                 : ~Configuration.UI_MODE_NIGHT_YES;
1109         return uiMode;
1110     }
1111 
computeCustomNightMode()1112     private boolean computeCustomNightMode() {
1113         return isTimeBetween(LocalTime.now(),
1114                 mCustomAutoNightModeStartMilliseconds,
1115                 mCustomAutoNightModeEndMilliseconds);
1116     }
1117 
applyConfigurationExternallyLocked()1118     private void applyConfigurationExternallyLocked() {
1119         if (mSetUiMode != mConfiguration.uiMode) {
1120             mSetUiMode = mConfiguration.uiMode;
1121             // load splash screen instead of screenshot
1122             mWindowManager.clearSnapshotCache();
1123             try {
1124                 ActivityTaskManager.getService().updateConfiguration(mConfiguration);
1125             } catch (RemoteException e) {
1126                 Slog.w(TAG, "Failure communicating with activity manager", e);
1127             } catch (SecurityException e) {
1128                 Slog.e(TAG, "Activity does not have the ", e);
1129             }
1130         }
1131     }
1132 
shouldApplyAutomaticChangesImmediately()1133     private boolean shouldApplyAutomaticChangesImmediately() {
1134         return mCar || !mPowerManager.isInteractive();
1135     }
1136 
scheduleNextCustomTimeListener()1137     private void scheduleNextCustomTimeListener() {
1138         cancelCustomAlarm();
1139         LocalDateTime now = LocalDateTime.now();
1140         final boolean active = computeCustomNightMode();
1141         final LocalDateTime next = active
1142                 ? getDateTimeAfter(mCustomAutoNightModeEndMilliseconds, now)
1143                 : getDateTimeAfter(mCustomAutoNightModeStartMilliseconds, now);
1144         final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
1145         mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, mCustomTimeListener, null);
1146     }
1147 
getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime)1148     private LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
1149         final LocalDateTime ldt = LocalDateTime.of(compareTime.toLocalDate(), localTime);
1150 
1151         // Check if the local time has passed, if so return the same time tomorrow.
1152         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
1153     }
1154 
updateLocked(int enableFlags, int disableFlags)1155     void updateLocked(int enableFlags, int disableFlags) {
1156         String action = null;
1157         String oldAction = null;
1158         if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
1159             adjustStatusBarCarModeLocked();
1160             oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
1161         } else if (isDeskDockState(mLastBroadcastState)) {
1162             oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
1163         }
1164 
1165         if (mCarModeEnabled) {
1166             if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
1167                 adjustStatusBarCarModeLocked();
1168                 if (oldAction != null) {
1169                     sendForegroundBroadcastToAllUsers(oldAction);
1170                 }
1171                 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
1172                 action = UiModeManager.ACTION_ENTER_CAR_MODE;
1173             }
1174         } else if (isDeskDockState(mDockState)) {
1175             if (!isDeskDockState(mLastBroadcastState)) {
1176                 if (oldAction != null) {
1177                     sendForegroundBroadcastToAllUsers(oldAction);
1178                 }
1179                 mLastBroadcastState = mDockState;
1180                 action = UiModeManager.ACTION_ENTER_DESK_MODE;
1181             }
1182         } else {
1183             mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
1184             action = oldAction;
1185         }
1186 
1187         if (action != null) {
1188             if (LOG) {
1189                 Slog.v(TAG, String.format(
1190                     "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x",
1191                     action, enableFlags, disableFlags));
1192             }
1193 
1194             // Send the ordered broadcast; the result receiver will receive after all
1195             // broadcasts have been sent. If any broadcast receiver changes the result
1196             // code from the initial value of RESULT_OK, then the result receiver will
1197             // not launch the corresponding dock application. This gives apps a chance
1198             // to override the behavior and stay in their app even when the device is
1199             // placed into a dock.
1200             Intent intent = new Intent(action);
1201             intent.putExtra("enableFlags", enableFlags);
1202             intent.putExtra("disableFlags", disableFlags);
1203             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1204             getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
1205                     mResultReceiver, null, Activity.RESULT_OK, null, null);
1206 
1207             // Attempting to make this transition a little more clean, we are going
1208             // to hold off on doing a configuration change until we have finished
1209             // the broadcast and started the home activity.
1210             mHoldingConfiguration = true;
1211             updateConfigurationLocked();
1212         } else {
1213             String category = null;
1214             if (mCarModeEnabled) {
1215                 if (mEnableCarDockLaunch
1216                         && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1217                     category = Intent.CATEGORY_CAR_DOCK;
1218                 }
1219             } else if (isDeskDockState(mDockState)) {
1220                 if (ENABLE_LAUNCH_DESK_DOCK_APP
1221                         && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1222                     category = Intent.CATEGORY_DESK_DOCK;
1223                 }
1224             } else {
1225                 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
1226                     category = Intent.CATEGORY_HOME;
1227                 }
1228             }
1229 
1230             if (LOG) {
1231                 Slog.v(TAG, "updateLocked: null action, mDockState="
1232                         + mDockState +", category=" + category);
1233             }
1234 
1235             sendConfigurationAndStartDreamOrDockAppLocked(category);
1236         }
1237 
1238         // keep screen on when charging and in car mode
1239         boolean keepScreenOn = mCharging &&
1240                 ((mCarModeEnabled && mCarModeKeepsScreenOn &&
1241                 (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
1242                 (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
1243         if (keepScreenOn != mWakeLock.isHeld()) {
1244             if (keepScreenOn) {
1245                 mWakeLock.acquire();
1246             } else {
1247                 mWakeLock.release();
1248             }
1249         }
1250     }
1251 
sendForegroundBroadcastToAllUsers(String action)1252     private void sendForegroundBroadcastToAllUsers(String action) {
1253         getContext().sendBroadcastAsUser(new Intent(action)
1254                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL);
1255     }
1256 
updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags)1257     private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) {
1258         // Launch a dock activity
1259         String category = null;
1260         if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) {
1261             // Only launch car home when car mode is enabled and the caller
1262             // has asked us to switch to it.
1263             if (mEnableCarDockLaunch
1264                     && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1265                 category = Intent.CATEGORY_CAR_DOCK;
1266             }
1267         } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) {
1268             // Only launch car home when desk mode is enabled and the caller
1269             // has asked us to switch to it.  Currently re-using the car
1270             // mode flag since we don't have a formal API for "desk mode".
1271             if (ENABLE_LAUNCH_DESK_DOCK_APP
1272                     && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1273                 category = Intent.CATEGORY_DESK_DOCK;
1274             }
1275         } else {
1276             // Launch the standard home app if requested.
1277             if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
1278                 category = Intent.CATEGORY_HOME;
1279             }
1280         }
1281 
1282         if (LOG) {
1283             Slog.v(TAG, String.format(
1284                     "Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, "
1285                             + "category=%s",
1286                     action, enableFlags, disableFlags, category));
1287         }
1288 
1289         sendConfigurationAndStartDreamOrDockAppLocked(category);
1290     }
1291 
sendConfigurationAndStartDreamOrDockAppLocked(String category)1292     private void sendConfigurationAndStartDreamOrDockAppLocked(String category) {
1293         // Update the configuration but don't send it yet.
1294         mHoldingConfiguration = false;
1295         updateConfigurationLocked();
1296 
1297         // Start the dock app, if there is one.
1298         boolean dockAppStarted = false;
1299         if (category != null) {
1300             // Now we are going to be careful about switching the
1301             // configuration and starting the activity -- we need to
1302             // do this in a specific order under control of the
1303             // activity manager, to do it cleanly.  So compute the
1304             // new config, but don't set it yet, and let the
1305             // activity manager take care of both the start and config
1306             // change.
1307             Intent homeIntent = buildHomeIntent(category);
1308             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
1309                 try {
1310                     int result = ActivityTaskManager.getService().startActivityWithConfig(
1311                             null, getContext().getBasePackageName(),
1312                             getContext().getAttributionTag(), homeIntent, null, null, null, 0, 0,
1313                             mConfiguration, null, UserHandle.USER_CURRENT);
1314                     if (ActivityManager.isStartResultSuccessful(result)) {
1315                         dockAppStarted = true;
1316                     } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
1317                         Slog.e(TAG, "Could not start dock app: " + homeIntent
1318                                 + ", startActivityWithConfig result " + result);
1319                     }
1320                 } catch (RemoteException ex) {
1321                     Slog.e(TAG, "Could not start dock app: " + homeIntent, ex);
1322                 }
1323             }
1324         }
1325 
1326         // Send the new configuration.
1327         applyConfigurationExternallyLocked();
1328 
1329         // If we did not start a dock app, then start dreaming if supported.
1330         if (category != null && !dockAppStarted) {
1331             Sandman.startDreamWhenDockedIfAppropriate(getContext());
1332         }
1333     }
1334 
adjustStatusBarCarModeLocked()1335     private void adjustStatusBarCarModeLocked() {
1336         final Context context = getContext();
1337         if (mStatusBarManager == null) {
1338             mStatusBarManager = (StatusBarManager)
1339                     context.getSystemService(Context.STATUS_BAR_SERVICE);
1340         }
1341 
1342         // Fear not: StatusBarManagerService manages a list of requests to disable
1343         // features of the status bar; these are ORed together to form the
1344         // active disabled list. So if (for example) the device is locked and
1345         // the status bar should be totally disabled, the calls below will
1346         // have no effect until the device is unlocked.
1347         if (mStatusBarManager != null) {
1348             mStatusBarManager.disable(mCarModeEnabled
1349                     ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
1350                     : StatusBarManager.DISABLE_NONE);
1351         }
1352 
1353         if (mNotificationManager == null) {
1354             mNotificationManager = (NotificationManager)
1355                     context.getSystemService(Context.NOTIFICATION_SERVICE);
1356         }
1357 
1358         if (mNotificationManager != null) {
1359             if (mCarModeEnabled) {
1360                 Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class);
1361 
1362                 Notification.Builder n =
1363                         new Notification.Builder(context, SystemNotificationChannels.CAR_MODE)
1364                         .setSmallIcon(R.drawable.stat_notify_car_mode)
1365                         .setDefaults(Notification.DEFAULT_LIGHTS)
1366                         .setOngoing(true)
1367                         .setWhen(0)
1368                         .setColor(context.getColor(
1369                                 com.android.internal.R.color.system_notification_accent_color))
1370                         .setContentTitle(
1371                                 context.getString(R.string.car_mode_disable_notification_title))
1372                         .setContentText(
1373                                 context.getString(R.string.car_mode_disable_notification_message))
1374                         .setContentIntent(
1375                                 PendingIntent.getActivityAsUser(context, 0,
1376                                         carModeOffIntent, 0,
1377                                         null, UserHandle.CURRENT));
1378                 mNotificationManager.notifyAsUser(null,
1379                         SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL);
1380             } else {
1381                 mNotificationManager.cancelAsUser(null,
1382                         SystemMessage.NOTE_CAR_MODE_DISABLE, UserHandle.ALL);
1383             }
1384         }
1385     }
1386 
updateComputedNightModeLocked(boolean activate)1387     private void updateComputedNightModeLocked(boolean activate) {
1388         mComputedNightMode = activate;
1389         if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) {
1390             return;
1391         }
1392         if (mOverrideNightModeOn && !mComputedNightMode) {
1393             mComputedNightMode = true;
1394             return;
1395         }
1396         if (mOverrideNightModeOff && mComputedNightMode) {
1397             mComputedNightMode = false;
1398             return;
1399         }
1400         resetNightModeOverrideLocked();
1401     }
1402 
resetNightModeOverrideLocked()1403     private boolean resetNightModeOverrideLocked() {
1404         if (mOverrideNightModeOff || mOverrideNightModeOn) {
1405             mOverrideNightModeOff = false;
1406             mOverrideNightModeOn = false;
1407             persistNightModeOverrides(mOverrideNightModeUser);
1408             mOverrideNightModeUser = USER_SYSTEM;
1409             return true;
1410         }
1411         return false;
1412     }
1413 
registerVrStateListener()1414     private void registerVrStateListener() {
1415         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
1416                 Context.VR_SERVICE));
1417         try {
1418             if (vrManager != null) {
1419                 vrManager.registerListener(mVrStateCallbacks);
1420             }
1421         } catch (RemoteException e) {
1422             Slog.e(TAG, "Failed to register VR mode state listener: " + e);
1423         }
1424     }
1425 
1426     /**
1427      * Handles "adb shell" commands.
1428      */
1429     private static class Shell extends ShellCommand {
1430         public static final String NIGHT_MODE_STR_YES = "yes";
1431         public static final String NIGHT_MODE_STR_NO = "no";
1432         public static final String NIGHT_MODE_STR_AUTO = "auto";
1433         public static final String NIGHT_MODE_STR_CUSTOM = "custom";
1434         public static final String NIGHT_MODE_STR_UNKNOWN = "unknown";
1435         private final IUiModeManager mInterface;
1436 
Shell(IUiModeManager iface)1437         Shell(IUiModeManager iface) {
1438             mInterface = iface;
1439         }
1440 
1441         @Override
onHelp()1442         public void onHelp() {
1443             final PrintWriter pw = getOutPrintWriter();
1444             pw.println("UiModeManager service (uimode) commands:");
1445             pw.println("  help");
1446             pw.println("    Print this help text.");
1447             pw.println("  night [yes|no|auto|custom]");
1448             pw.println("    Set or read night mode.");
1449             pw.println("  time [start|end] <ISO time>");
1450             pw.println("    Set custom start/end schedule time"
1451                     + " (night mode must be set to custom to apply).");
1452         }
1453 
1454         @Override
onCommand(String cmd)1455         public int onCommand(String cmd) {
1456             if (cmd == null) {
1457                 return handleDefaultCommands(cmd);
1458             }
1459 
1460             try {
1461                 switch (cmd) {
1462                     case "night":
1463                         return handleNightMode();
1464                     case "time":
1465                         return handleCustomTime();
1466                     default:
1467                         return handleDefaultCommands(cmd);
1468                 }
1469             } catch (RemoteException e) {
1470                 final PrintWriter err = getErrPrintWriter();
1471                 err.println("Remote exception: " + e);
1472             }
1473             return -1;
1474         }
1475 
handleCustomTime()1476         private int handleCustomTime() throws RemoteException {
1477             final String modeStr = getNextArg();
1478             if (modeStr == null) {
1479                 printCustomTime();
1480                 return 0;
1481             }
1482             switch (modeStr) {
1483                 case "start":
1484                     final String start = getNextArg();
1485                     mInterface.setCustomNightModeStart(toMilliSeconds(LocalTime.parse(start)));
1486                     return 0;
1487                 case "end":
1488                     final String end = getNextArg();
1489                     mInterface.setCustomNightModeEnd(toMilliSeconds(LocalTime.parse(end)));
1490                     return 0;
1491                 default:
1492                     getErrPrintWriter().println("command must be in [start|end]");
1493                     return -1;
1494             }
1495         }
1496 
printCustomTime()1497         private void printCustomTime() throws RemoteException {
1498             getOutPrintWriter().println("start " + fromMilliseconds(
1499                     mInterface.getCustomNightModeStart()).toString());
1500             getOutPrintWriter().println("end " + fromMilliseconds(
1501                     mInterface.getCustomNightModeEnd()).toString());
1502         }
1503 
handleNightMode()1504         private int handleNightMode() throws RemoteException {
1505             final PrintWriter err = getErrPrintWriter();
1506             final String modeStr = getNextArg();
1507             if (modeStr == null) {
1508                 printCurrentNightMode();
1509                 return 0;
1510             }
1511 
1512             final int mode = strToNightMode(modeStr);
1513             if (mode >= 0) {
1514                 mInterface.setNightMode(mode);
1515                 printCurrentNightMode();
1516                 return 0;
1517             } else {
1518                 err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '"
1519                         + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO
1520                         +  "', or '" + NIGHT_MODE_STR_CUSTOM + "'");
1521                 return -1;
1522             }
1523         }
1524 
printCurrentNightMode()1525         private void printCurrentNightMode() throws RemoteException {
1526             final PrintWriter pw = getOutPrintWriter();
1527             final int currMode = mInterface.getNightMode();
1528             final String currModeStr = nightModeToStr(currMode);
1529             pw.println("Night mode: " + currModeStr);
1530         }
1531 
nightModeToStr(int mode)1532         private static String nightModeToStr(int mode) {
1533             switch (mode) {
1534                 case UiModeManager.MODE_NIGHT_YES:
1535                     return NIGHT_MODE_STR_YES;
1536                 case UiModeManager.MODE_NIGHT_NO:
1537                     return NIGHT_MODE_STR_NO;
1538                 case UiModeManager.MODE_NIGHT_AUTO:
1539                     return NIGHT_MODE_STR_AUTO;
1540                 case MODE_NIGHT_CUSTOM:
1541                     return NIGHT_MODE_STR_CUSTOM;
1542                 default:
1543                     return NIGHT_MODE_STR_UNKNOWN;
1544             }
1545         }
1546 
strToNightMode(String modeStr)1547         private static int strToNightMode(String modeStr) {
1548             switch (modeStr) {
1549                 case NIGHT_MODE_STR_YES:
1550                     return UiModeManager.MODE_NIGHT_YES;
1551                 case NIGHT_MODE_STR_NO:
1552                     return UiModeManager.MODE_NIGHT_NO;
1553                 case NIGHT_MODE_STR_AUTO:
1554                     return UiModeManager.MODE_NIGHT_AUTO;
1555                 case NIGHT_MODE_STR_CUSTOM:
1556                     return UiModeManager.MODE_NIGHT_CUSTOM;
1557                 default:
1558                     return -1;
1559             }
1560         }
1561     }
1562 
1563     public final class LocalService extends UiModeManagerInternal {
1564 
1565         @Override
isNightMode()1566         public boolean isNightMode() {
1567             synchronized (mLock) {
1568                 final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0;
1569                 if (LOG) {
1570                     Slog.d(TAG,
1571                         "LocalService.isNightMode(): mNightMode=" + mNightMode
1572                         + "; mComputedNightMode=" + mComputedNightMode
1573                         + "; uiMode=" + mConfiguration.uiMode
1574                         + "; isIt=" + isIt);
1575                 }
1576                 return isIt;
1577             }
1578         }
1579     }
1580 
1581     private final class UserSwitchedReceiver extends BroadcastReceiver {
1582         @Override
onReceive(Context context, Intent intent)1583         public void onReceive(Context context, Intent intent) {
1584             synchronized (mLock) {
1585                 final int currentId = intent.getIntExtra(
1586                         Intent.EXTRA_USER_HANDLE, USER_SYSTEM);
1587                 // only update if the value is actually changed
1588                 if (updateNightModeFromSettingsLocked(context, context.getResources(), currentId)) {
1589                     updateLocked(0, 0);
1590                 }
1591             }
1592         }
1593     }
1594 }
1595