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 static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE;
20 import static android.app.UiModeManager.DEFAULT_PRIORITY;
21 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
22 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
23 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
24 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
25 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
26 import static android.app.UiModeManager.MODE_NIGHT_NO;
27 import static android.app.UiModeManager.MODE_NIGHT_YES;
28 import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
29 import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
30 import static android.os.UserHandle.USER_SYSTEM;
31 import static android.os.UserHandle.getCallingUserId;
32 import static android.provider.Settings.Secure.CONTRAST_LEVEL;
33 import static android.util.TimeUtils.isTimeBetween;
34 
35 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
36 
37 import android.annotation.IntRange;
38 import android.annotation.NonNull;
39 import android.annotation.Nullable;
40 import android.app.Activity;
41 import android.app.ActivityManager;
42 import android.app.ActivityTaskManager;
43 import android.app.AlarmManager;
44 import android.app.IOnProjectionStateChangedListener;
45 import android.app.IUiModeManager;
46 import android.app.IUiModeManagerCallback;
47 import android.app.KeyguardManager;
48 import android.app.Notification;
49 import android.app.NotificationManager;
50 import android.app.PendingIntent;
51 import android.app.StatusBarManager;
52 import android.app.UiModeManager;
53 import android.app.UiModeManager.NightModeCustomReturnType;
54 import android.app.UiModeManager.NightModeCustomType;
55 import android.content.BroadcastReceiver;
56 import android.content.Context;
57 import android.content.Intent;
58 import android.content.IntentFilter;
59 import android.content.pm.PackageManager;
60 import android.content.res.Configuration;
61 import android.content.res.Resources;
62 import android.database.ContentObserver;
63 import android.net.Uri;
64 import android.os.BatteryManager;
65 import android.os.Binder;
66 import android.os.Handler;
67 import android.os.IBinder;
68 import android.os.PowerManager;
69 import android.os.PowerManager.ServiceType;
70 import android.os.PowerManagerInternal;
71 import android.os.Process;
72 import android.os.RemoteCallbackList;
73 import android.os.RemoteException;
74 import android.os.ResultReceiver;
75 import android.os.ServiceManager;
76 import android.os.ShellCallback;
77 import android.os.ShellCommand;
78 import android.os.SystemProperties;
79 import android.os.UserHandle;
80 import android.provider.Settings;
81 import android.provider.Settings.Secure;
82 import android.service.dreams.Sandman;
83 import android.service.vr.IVrManager;
84 import android.service.vr.IVrStateCallbacks;
85 import android.util.ArraySet;
86 import android.util.Slog;
87 import android.util.SparseArray;
88 
89 import com.android.internal.R;
90 import com.android.internal.annotations.GuardedBy;
91 import com.android.internal.annotations.VisibleForTesting;
92 import com.android.internal.app.DisableCarModeActivity;
93 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
94 import com.android.internal.notification.SystemNotificationChannels;
95 import com.android.internal.util.DumpUtils;
96 import com.android.server.twilight.TwilightListener;
97 import com.android.server.twilight.TwilightManager;
98 import com.android.server.twilight.TwilightState;
99 import com.android.server.wm.ActivityTaskManagerInternal;
100 import com.android.server.wm.WindowManagerInternal;
101 
102 import java.io.FileDescriptor;
103 import java.io.PrintWriter;
104 import java.time.DateTimeException;
105 import java.time.LocalDateTime;
106 import java.time.LocalTime;
107 import java.time.ZoneId;
108 import java.util.ArrayList;
109 import java.util.Arrays;
110 import java.util.Collection;
111 import java.util.HashMap;
112 import java.util.List;
113 import java.util.Map;
114 import java.util.Set;
115 
116 final class UiModeManagerService extends SystemService {
117     private static final String TAG = UiModeManager.class.getSimpleName();
118     private static final boolean LOG = false;
119 
120     // Enable launching of applications when entering the dock.
121     private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
122     private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme";
123     @VisibleForTesting
124     public static final Set<Integer> SUPPORTED_NIGHT_MODE_CUSTOM_TYPES = new ArraySet(
125             new Integer[]{MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, MODE_NIGHT_CUSTOM_TYPE_BEDTIME});
126 
127     private final Injector mInjector;
128     private final Object mLock = new Object();
129 
130     private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
131 
132     private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
133     private int mNightMode = UiModeManager.MODE_NIGHT_NO;
134     private int mNightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
135     private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
136     private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
137     private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
138     private LocalTime mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME;
139 
140     private Map<Integer, String> mCarModePackagePriority = new HashMap<>();
141     private boolean mCarModeEnabled = false;
142     private boolean mCharging = false;
143     private boolean mPowerSave = false;
144     // Do not change configuration now. wait until screen turns off.
145     // This prevents jank and activity restart when the user
146     // is actively using the device
147     private boolean mWaitForScreenOff = false;
148     private int mDefaultUiModeType;
149     private boolean mCarModeKeepsScreenOn;
150     private boolean mDeskModeKeepsScreenOn;
151     private boolean mTelevision;
152     private boolean mCar;
153     private boolean mWatch;
154     private boolean mVrHeadset;
155     private boolean mComputedNightMode;
156     private boolean mLastBedtimeRequestedNightMode = false;
157     private int mCarModeEnableFlags;
158     private boolean mSetupWizardComplete;
159 
160     // flag set by resource, whether to start dream immediately upon docking even if unlocked.
161     private boolean mStartDreamImmediatelyOnDock = true;
162     // flag set by resource, whether to disable dreams when ambient mode suppression is enabled.
163     private boolean mDreamsDisabledByAmbientModeSuppression = false;
164     // flag set by resource, whether to enable Car dock launch when starting car mode.
165     private boolean mEnableCarDockLaunch = true;
166     // flag set by resource, whether to lock UI mode to the default one or not.
167     private boolean mUiModeLocked = false;
168     // flag set by resource, whether to night mode change for normal all or not.
169     private boolean mNightModeLocked = false;
170 
171     int mCurUiMode = 0;
172     private int mSetUiMode = 0;
173     private boolean mHoldingConfiguration = false;
174     private int mCurrentUser;
175 
176     private Configuration mConfiguration = new Configuration();
177     boolean mSystemReady;
178 
179     private final Handler mHandler = new Handler();
180 
181     private TwilightManager mTwilightManager;
182     private NotificationManager mNotificationManager;
183     private StatusBarManager mStatusBarManager;
184     private WindowManagerInternal mWindowManager;
185     private ActivityTaskManagerInternal mActivityTaskManager;
186     private AlarmManager mAlarmManager;
187     private PowerManager mPowerManager;
188     private KeyguardManager mKeyguardManager;
189 
190     // In automatic scheduling, the user is able
191     // to override the computed night mode until the two match
192     // Example: Activate dark mode in the day time until sunrise the next day
193     private boolean mOverrideNightModeOn;
194     private boolean mOverrideNightModeOff;
195     private int mOverrideNightModeUser = USER_SYSTEM;
196 
197     private PowerManager.WakeLock mWakeLock;
198 
199     private final LocalService mLocalService = new LocalService();
200     private PowerManagerInternal mLocalPowerManager;
201 
202     @GuardedBy("mLock")
203     private final SparseArray<RemoteCallbackList<IUiModeManagerCallback>> mUiModeManagerCallbacks =
204             new SparseArray<>();
205 
206     @GuardedBy("mLock")
207     @Nullable
208     private SparseArray<List<ProjectionHolder>> mProjectionHolders;
209     @GuardedBy("mLock")
210     @Nullable
211     private SparseArray<RemoteCallbackList<IOnProjectionStateChangedListener>> mProjectionListeners;
212 
213     @GuardedBy("mLock")
214     private final SparseArray<Float> mContrasts = new SparseArray<>();
215 
UiModeManagerService(Context context)216     public UiModeManagerService(Context context) {
217         this(context, /* setupWizardComplete= */ false, /* tm= */ null, new Injector());
218     }
219 
220     @VisibleForTesting
UiModeManagerService(Context context, boolean setupWizardComplete, TwilightManager tm, Injector injector)221     protected UiModeManagerService(Context context, boolean setupWizardComplete,
222             TwilightManager tm, Injector injector) {
223         super(context);
224         mConfiguration.setToDefaults();
225         mSetupWizardComplete = setupWizardComplete;
226         mTwilightManager = tm;
227         mInjector = injector;
228     }
229 
buildHomeIntent(String category)230     private static Intent buildHomeIntent(String category) {
231         Intent intent = new Intent(Intent.ACTION_MAIN);
232         intent.addCategory(category);
233         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
234                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
235         return intent;
236     }
237 
238     // The broadcast receiver which receives the result of the ordered broadcast sent when
239     // the dock state changes. The original ordered broadcast is sent with an initial result
240     // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
241     // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
242     private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
243         @Override
244         public void onReceive(Context context, Intent intent) {
245             if (getResultCode() != Activity.RESULT_OK) {
246                 if (LOG) {
247                     Slog.v(TAG, "Handling broadcast result for action " + intent.getAction()
248                             + ": canceled: " + getResultCode());
249                 }
250                 return;
251             }
252 
253             final int enableFlags = intent.getIntExtra("enableFlags", 0);
254             final int disableFlags = intent.getIntExtra("disableFlags", 0);
255             synchronized (mLock) {
256                 updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags);
257             }
258         }
259     };
260 
261     private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
262         @Override
263         public void onReceive(Context context, Intent intent) {
264             int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
265                     Intent.EXTRA_DOCK_STATE_UNDOCKED);
266             updateDockState(state);
267         }
268     };
269 
270     private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
271         @Override
272         public void onReceive(Context context, Intent intent) {
273             switch (intent.getAction()) {
274                 case Intent.ACTION_BATTERY_CHANGED:
275                     mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
276                     break;
277             }
278             synchronized (mLock) {
279                 if (mSystemReady) {
280                     updateLocked(0, 0);
281                 }
282             }
283         }
284     };
285 
286     private final TwilightListener mTwilightListener = new TwilightListener() {
287         @Override
288         public void onTwilightStateChanged(@Nullable TwilightState state) {
289             synchronized (mLock) {
290                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) {
291                     if (shouldApplyAutomaticChangesImmediately()) {
292                         updateLocked(0, 0);
293                     } else {
294                         registerScreenOffEventLocked();
295                     }
296                 }
297             }
298         }
299     };
300 
301     /**
302      * DO NOT USE DIRECTLY
303      * see register registerScreenOffEvent and unregisterScreenOffEvent
304      */
305     private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
306         @Override
307         public void onReceive(Context context, Intent intent) {
308             synchronized (mLock) {
309                 // must unregister first before updating
310                 unregisterScreenOffEventLocked();
311                 updateLocked(0, 0);
312             }
313         }
314     };
315 
316     private final BroadcastReceiver mOnTimeChangedHandler = new BroadcastReceiver() {
317         @Override
318         public void onReceive(Context context, Intent intent) {
319             synchronized (mLock) {
320                 updateCustomTimeLocked();
321             }
322         }
323     };
324 
325     private final AlarmManager.OnAlarmListener mCustomTimeListener = () -> {
326         synchronized (mLock) {
327             updateCustomTimeLocked();
328         }
329     };
330 
331     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
332         @Override
333         public void onVrStateChanged(boolean enabled) {
334             synchronized (mLock) {
335                 mVrHeadset = enabled;
336                 if (mSystemReady) {
337                     updateLocked(0, 0);
338                 }
339             }
340         }
341     };
342 
343     private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) {
344         @Override
345         public void onChange(boolean selfChange, Uri uri) {
346             synchronized (mLock) {
347                 // setup wizard is done now so we can unblock
348                 if (setupWizardCompleteForCurrentUser() && !selfChange) {
349                     mSetupWizardComplete = true;
350                     getContext().getContentResolver()
351                             .unregisterContentObserver(mSetupWizardObserver);
352                     // update night mode
353                     Context context = getContext();
354                     updateNightModeFromSettingsLocked(context, context.getResources(),
355                             UserHandle.getCallingUserId());
356                     updateLocked(0, 0);
357                 }
358             }
359         }
360     };
361 
362     private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
363         @Override
364         public void onChange(boolean selfChange, Uri uri) {
365             updateSystemProperties();
366         }
367     };
368 
369     private final ContentObserver mContrastObserver = new ContentObserver(mHandler) {
370         @Override
371         public void onChange(boolean selfChange, Uri uri) {
372             synchronized (mLock) {
373                 if (updateContrastLocked()) {
374                     float contrast = getContrastLocked();
375                     mUiModeManagerCallbacks.get(mCurrentUser, new RemoteCallbackList<>())
376                             .broadcast(ignoreRemoteException(
377                                     callback -> callback.notifyContrastChanged(contrast)));
378                 }
379             }
380         }
381     };
382 
updateSystemProperties()383     private void updateSystemProperties() {
384         int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
385                 mNightMode, 0);
386         if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
387             mode = MODE_NIGHT_YES;
388         }
389         SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
390     }
391 
392     @VisibleForTesting
setStartDreamImmediatelyOnDock(boolean startDreamImmediatelyOnDock)393     void setStartDreamImmediatelyOnDock(boolean startDreamImmediatelyOnDock) {
394         mStartDreamImmediatelyOnDock = startDreamImmediatelyOnDock;
395     }
396 
397     @VisibleForTesting
setDreamsDisabledByAmbientModeSuppression(boolean disabledByAmbientModeSuppression)398     void setDreamsDisabledByAmbientModeSuppression(boolean disabledByAmbientModeSuppression) {
399         mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression;
400     }
401 
402     @Override
onUserSwitching(@ullable TargetUser from, @NonNull TargetUser to)403     public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
404         mCurrentUser = to.getUserIdentifier();
405         if (mNightMode == MODE_NIGHT_AUTO) persistComputedNightMode(from.getUserIdentifier());
406         getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
407         verifySetupWizardCompleted();
408         synchronized (mLock) {
409             updateNightModeFromSettingsLocked(getContext(), getContext().getResources(),
410                     to.getUserIdentifier());
411             updateLocked(0, 0);
412         }
413     }
414 
415     @Override
onBootPhase(int phase)416     public void onBootPhase(int phase) {
417         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
418             synchronized (mLock) {
419                 final Context context = getContext();
420                 mSystemReady = true;
421                 mKeyguardManager = context.getSystemService(KeyguardManager.class);
422                 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
423                 mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
424                 mWindowManager = LocalServices.getService(WindowManagerInternal.class);
425                 mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class);
426                 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
427                 TwilightManager twilightManager = getLocalService(TwilightManager.class);
428                 if (twilightManager != null) mTwilightManager = twilightManager;
429                 mLocalPowerManager =
430                         LocalServices.getService(PowerManagerInternal.class);
431                 initPowerSave();
432                 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
433                 registerVrStateListener();
434                 // register listeners
435                 context.getContentResolver()
436                         .registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
437                                 false, mDarkThemeObserver, 0);
438                 context.getContentResolver().registerContentObserver(
439                         Secure.getUriFor(Secure.CONTRAST_LEVEL), false,
440                         mContrastObserver, UserHandle.USER_ALL);
441                 context.registerReceiver(mDockModeReceiver,
442                         new IntentFilter(Intent.ACTION_DOCK_EVENT));
443                 IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
444                 context.registerReceiver(mBatteryReceiver, batteryFilter);
445                 context.registerReceiver(mSettingsRestored,
446                         new IntentFilter(Intent.ACTION_SETTING_RESTORED));
447                 context.registerReceiver(mOnShutdown,
448                         new IntentFilter(Intent.ACTION_SHUTDOWN));
449                 updateConfigurationLocked();
450                 applyConfigurationExternallyLocked();
451             }
452         }
453     }
454 
455     @Override
onStart()456     public void onStart() {
457         final Context context = getContext();
458         // If setup isn't complete for this user listen for completion so we can unblock
459         // being able to send a night mode configuration change event
460         verifySetupWizardCompleted();
461 
462         final Resources res = context.getResources();
463         mStartDreamImmediatelyOnDock = res.getBoolean(
464                 com.android.internal.R.bool.config_startDreamImmediatelyOnDock);
465         mDreamsDisabledByAmbientModeSuppression = res.getBoolean(
466                 com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
467         mNightMode = res.getInteger(
468                 com.android.internal.R.integer.config_defaultNightMode);
469         mDefaultUiModeType = res.getInteger(
470                 com.android.internal.R.integer.config_defaultUiModeType);
471         mCarModeKeepsScreenOn = (res.getInteger(
472                 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
473         mDeskModeKeepsScreenOn = (res.getInteger(
474                 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
475         mEnableCarDockLaunch = res.getBoolean(
476                 com.android.internal.R.bool.config_enableCarDockHomeLaunch);
477         mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode);
478         mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode);
479         final PackageManager pm = context.getPackageManager();
480         mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
481                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
482         mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
483         mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
484 
485         // Update the initial, static configurations.
486         SystemServerInitThreadPool.submit(() -> {
487             synchronized (mLock) {
488                 TwilightManager twilightManager = getLocalService(TwilightManager.class);
489                 if (twilightManager != null) mTwilightManager = twilightManager;
490                 updateNightModeFromSettingsLocked(context, res, UserHandle.getCallingUserId());
491                 updateSystemProperties();
492             }
493 
494         }, TAG + ".onStart");
495         publishBinderService(Context.UI_MODE_SERVICE, mService);
496         publishLocalService(UiModeManagerInternal.class, mLocalService);
497     }
498 
499     private final BroadcastReceiver mOnShutdown = new BroadcastReceiver() {
500         @Override
501         public void onReceive(Context context, Intent intent) {
502             if (mNightMode == MODE_NIGHT_AUTO) {
503                 persistComputedNightMode(mCurrentUser);
504             }
505         }
506     };
507 
persistComputedNightMode(int userId)508     private void persistComputedNightMode(int userId) {
509         Secure.putIntForUser(getContext().getContentResolver(),
510                 Secure.UI_NIGHT_MODE_LAST_COMPUTED, mComputedNightMode ? 1 : 0,
511                 userId);
512     }
513 
514     private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() {
515         @Override
516         public void onReceive(Context context, Intent intent) {
517             List<String> settings = Arrays.asList(
518                     Secure.UI_NIGHT_MODE, Secure.DARK_THEME_CUSTOM_START_TIME,
519                     Secure.DARK_THEME_CUSTOM_END_TIME);
520             if (settings.contains(intent.getExtras().getCharSequence(Intent.EXTRA_SETTING_NAME))) {
521                 synchronized (mLock) {
522                     updateNightModeFromSettingsLocked(context, context.getResources(),
523                             UserHandle.getCallingUserId());
524                     updateConfigurationLocked();
525                 }
526             }
527         }
528     };
529 
initPowerSave()530     private void initPowerSave() {
531         mPowerSave =
532                 mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE)
533                         .batterySaverEnabled;
534         mLocalPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE, state -> {
535             synchronized (mLock) {
536                 if (mPowerSave == state.batterySaverEnabled) {
537                     return;
538                 }
539                 mPowerSave = state.batterySaverEnabled;
540                 if (mSystemReady) {
541                     updateLocked(0, 0);
542                 }
543             }
544         });
545     }
546 
547     @VisibleForTesting
getService()548     protected IUiModeManager getService() {
549         return mService;
550     }
551 
552     @VisibleForTesting
getConfiguration()553     protected Configuration getConfiguration() {
554         return mConfiguration;
555     }
556 
557     // Records whether setup wizard has happened or not and adds an observer for this user if not.
verifySetupWizardCompleted()558     private void verifySetupWizardCompleted() {
559         final Context context = getContext();
560         final int userId = UserHandle.getCallingUserId();
561         if (!setupWizardCompleteForCurrentUser()) {
562             mSetupWizardComplete = false;
563             context.getContentResolver().registerContentObserver(
564                     Secure.getUriFor(
565                             Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId);
566         } else {
567             mSetupWizardComplete = true;
568         }
569     }
570 
setupWizardCompleteForCurrentUser()571     private boolean setupWizardCompleteForCurrentUser() {
572         return Secure.getIntForUser(getContext().getContentResolver(),
573                 Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
574     }
575 
updateCustomTimeLocked()576     private void updateCustomTimeLocked() {
577         if (mNightMode != MODE_NIGHT_CUSTOM) return;
578         if (shouldApplyAutomaticChangesImmediately()) {
579             updateLocked(0, 0);
580         } else {
581             registerScreenOffEventLocked();
582         }
583         scheduleNextCustomTimeListener();
584     }
585 
586     /**
587      * Updates the night mode setting in Settings.Secure
588      *
589      * @param context A valid context
590      * @param res     A valid resource object
591      * @param userId  The user to update the setting for
592      */
updateNightModeFromSettingsLocked(Context context, Resources res, int userId)593     private void updateNightModeFromSettingsLocked(Context context, Resources res, int userId) {
594         if (mCarModeEnabled || mCar) {
595             return;
596         }
597         if (mSetupWizardComplete) {
598             mNightMode = Secure.getIntForUser(context.getContentResolver(),
599                     Secure.UI_NIGHT_MODE, res.getInteger(
600                             com.android.internal.R.integer.config_defaultNightMode), userId);
601             mNightModeCustomType = Secure.getIntForUser(context.getContentResolver(),
602                     Secure.UI_NIGHT_MODE_CUSTOM_TYPE, MODE_NIGHT_CUSTOM_TYPE_UNKNOWN, userId);
603                     mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
604                     Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0;
605             mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(),
606                     Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0;
607             mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay(
608                     Secure.getLongForUser(context.getContentResolver(),
609                             Secure.DARK_THEME_CUSTOM_START_TIME,
610                             DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000);
611             mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay(
612                     Secure.getLongForUser(context.getContentResolver(),
613                             Secure.DARK_THEME_CUSTOM_END_TIME,
614                             DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
615             if (mNightMode == MODE_NIGHT_AUTO) {
616                 mComputedNightMode = Secure.getIntForUser(context.getContentResolver(),
617                         Secure.UI_NIGHT_MODE_LAST_COMPUTED, 0, userId) != 0;
618             }
619         }
620     }
621 
toMilliSeconds(LocalTime t)622     private static long toMilliSeconds(LocalTime t) {
623         return t.toNanoOfDay() / 1000;
624     }
625 
fromMilliseconds(long t)626     private static LocalTime fromMilliseconds(long t) {
627         return LocalTime.ofNanoOfDay(t * 1000);
628     }
629 
registerScreenOffEventLocked()630     private void registerScreenOffEventLocked() {
631         if (mPowerSave) return;
632         mWaitForScreenOff = true;
633         final IntentFilter intentFilter =
634                 new IntentFilter(Intent.ACTION_SCREEN_OFF);
635         getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
636     }
637 
cancelCustomAlarm()638     private void cancelCustomAlarm() {
639         mAlarmManager.cancel(mCustomTimeListener);
640     }
641 
unregisterScreenOffEventLocked()642     private void unregisterScreenOffEventLocked() {
643         mWaitForScreenOff = false;
644         try {
645             getContext().unregisterReceiver(mOnScreenOffHandler);
646         } catch (IllegalArgumentException e) {
647             // we ignore this exception if the receiver is unregistered already.
648         }
649     }
650 
registerTimeChangeEvent()651     private void registerTimeChangeEvent() {
652         final IntentFilter intentFilter =
653                 new IntentFilter(Intent.ACTION_TIME_CHANGED);
654         intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
655         getContext().registerReceiver(mOnTimeChangedHandler, intentFilter);
656     }
657 
unregisterTimeChangeEvent()658     private void unregisterTimeChangeEvent() {
659         try {
660             getContext().unregisterReceiver(mOnTimeChangedHandler);
661         } catch (IllegalArgumentException e) {
662             // we ignore this exception if the receiver is unregistered already.
663         }
664     }
665 
666     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
667         @Override
668         public void addCallback(IUiModeManagerCallback callback) {
669             int userId = getCallingUserId();
670             synchronized (mLock) {
671                 if (!mUiModeManagerCallbacks.contains(userId)) {
672                     mUiModeManagerCallbacks.put(userId, new RemoteCallbackList<>());
673                 }
674                 mUiModeManagerCallbacks.get(userId).register(callback);
675             }
676         }
677 
678         @Override
679         public void enableCarMode(@UiModeManager.EnableCarMode int flags,
680                 @IntRange(from = 0) int priority, String callingPackage) {
681             if (isUiModeLocked()) {
682                 Slog.e(TAG, "enableCarMode while UI mode is locked");
683                 return;
684             }
685 
686             if (priority != UiModeManager.DEFAULT_PRIORITY
687                     && getContext().checkCallingOrSelfPermission(
688                     android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED)
689                     != PackageManager.PERMISSION_GRANTED) {
690                 throw new SecurityException("Enabling car mode with a priority requires "
691                         + "permission ENTER_CAR_MODE_PRIORITIZED");
692             }
693 
694             // Allow the user to enable car mode using the shell,
695             // e.g. 'adb shell cmd uimode car yes'
696             boolean isShellCaller = mInjector.getCallingUid() == Process.SHELL_UID;
697             if (!isShellCaller) {
698               assertLegit(callingPackage);
699             }
700 
701             final long ident = Binder.clearCallingIdentity();
702             try {
703                 synchronized (mLock) {
704                     setCarModeLocked(true, flags, priority, callingPackage);
705                     if (mSystemReady) {
706                         updateLocked(flags, 0);
707                     }
708                 }
709             } finally {
710                 Binder.restoreCallingIdentity(ident);
711             }
712         }
713 
714         /**
715          * This method is only kept around for the time being; the AIDL has an UnsupportedAppUsage
716          * tag which means this method is technically considered part of the greylist "API".
717          * @param flags
718          */
719         @Override
720         public void disableCarMode(@UiModeManager.DisableCarMode int flags) {
721             disableCarModeByCallingPackage(flags, null /* callingPackage */);
722         }
723 
724         /**
725          * Handles requests to disable car mode.
726          * @param flags Disable car mode flags
727          * @param callingPackage
728          */
729         @Override
730         public void disableCarModeByCallingPackage(@UiModeManager.DisableCarMode int flags,
731                 String callingPackage) {
732             if (isUiModeLocked()) {
733                 Slog.e(TAG, "disableCarMode while UI mode is locked");
734                 return;
735             }
736 
737             // If the caller is the system, we will allow the DISABLE_CAR_MODE_ALL_PRIORITIES car
738             // mode flag to be specified; this is so that the user can disable car mode at all
739             // priorities using the persistent notification.
740             //
741             // We also allow the user to disable car mode using the shell,
742             // e.g. 'adb shell cmd uimode car no'
743             int callingUid = mInjector.getCallingUid();
744             boolean isSystemCaller = callingUid == Process.SYSTEM_UID;
745             boolean isShellCaller = callingUid == Process.SHELL_UID;
746             if (!isSystemCaller && !isShellCaller) {
747                 assertLegit(callingPackage);
748             }
749             final int carModeFlags =
750                     isSystemCaller ? flags : flags & ~UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES;
751 
752             final long ident = Binder.clearCallingIdentity();
753             try {
754                 synchronized (mLock) {
755                     // Determine if the caller has enabled car mode at a priority other than the
756                     // default one.  If they have, then attempt to disable at that priority.
757                     int priority = mCarModePackagePriority.entrySet()
758                             .stream()
759                             .filter(e -> e.getValue().equals(callingPackage))
760                             .findFirst()
761                             .map(Map.Entry::getKey)
762                             .orElse(UiModeManager.DEFAULT_PRIORITY);
763 
764                     setCarModeLocked(false, carModeFlags, priority, callingPackage);
765                     if (mSystemReady) {
766                         updateLocked(0, flags);
767                     }
768                 }
769             } finally {
770                 Binder.restoreCallingIdentity(ident);
771             }
772         }
773 
774         @Override
775         public int getCurrentModeType() {
776             final long ident = Binder.clearCallingIdentity();
777             try {
778                 synchronized (mLock) {
779                     return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
780                 }
781             } finally {
782                 Binder.restoreCallingIdentity(ident);
783             }
784         }
785 
786         @Override
787         public void setNightMode(int mode) {
788             // MODE_NIGHT_CUSTOM_TYPE_SCHEDULE is the default for MODE_NIGHT_CUSTOM.
789             int customModeType = mode == MODE_NIGHT_CUSTOM
790                     ? MODE_NIGHT_CUSTOM_TYPE_SCHEDULE
791                     : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
792             setNightModeInternal(mode, customModeType);
793         }
794 
795         private void setNightModeInternal(int mode, int customModeType) {
796             if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
797                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
798                     != PackageManager.PERMISSION_GRANTED)) {
799                 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
800                 return;
801             }
802             switch (mode) {
803                 case UiModeManager.MODE_NIGHT_NO:
804                 case UiModeManager.MODE_NIGHT_YES:
805                 case MODE_NIGHT_AUTO:
806                     break;
807                 case MODE_NIGHT_CUSTOM:
808                     if (SUPPORTED_NIGHT_MODE_CUSTOM_TYPES.contains(customModeType)) {
809                         break;
810                     }
811                     throw new IllegalArgumentException(
812                             "Can't set the custom type to " + customModeType);
813                 default:
814                     throw new IllegalArgumentException("Unknown mode: " + mode);
815             }
816 
817             final int user = UserHandle.getCallingUserId();
818             final long ident = Binder.clearCallingIdentity();
819             try {
820                 synchronized (mLock) {
821                     if (mNightMode != mode || mNightModeCustomType != customModeType) {
822                         if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
823                             unregisterScreenOffEventLocked();
824                             cancelCustomAlarm();
825                         }
826                         mNightModeCustomType = mode == MODE_NIGHT_CUSTOM
827                                 ? customModeType
828                                 : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
829                         mNightMode = mode;
830                         resetNightModeOverrideLocked();
831                         persistNightMode(user);
832                         // on screen off will update configuration instead
833                         if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
834                                 || shouldApplyAutomaticChangesImmediately()) {
835                             unregisterScreenOffEventLocked();
836                             updateLocked(0, 0);
837                         } else {
838                             registerScreenOffEventLocked();
839                         }
840                     }
841                 }
842             } finally {
843                 Binder.restoreCallingIdentity(ident);
844             }
845         }
846 
847         @Override
848         public int getNightMode() {
849             synchronized (mLock) {
850                 return mNightMode;
851             }
852         }
853 
854         @Override
855         public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) {
856             if (getContext().checkCallingOrSelfPermission(
857                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
858                     != PackageManager.PERMISSION_GRANTED) {
859                 throw new SecurityException(
860                         "setNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission");
861             }
862             setNightModeInternal(MODE_NIGHT_CUSTOM, nightModeCustomType);
863         }
864 
865         @Override
866         public  @NightModeCustomReturnType int getNightModeCustomType() {
867             if (getContext().checkCallingOrSelfPermission(
868                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
869                     != PackageManager.PERMISSION_GRANTED) {
870                 throw new SecurityException(
871                         "getNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission");
872             }
873             synchronized (mLock) {
874                 return mNightModeCustomType;
875             }
876         }
877 
878         @Override
879         public void setApplicationNightMode(@UiModeManager.NightMode int mode) {
880             switch (mode) {
881                 case UiModeManager.MODE_NIGHT_NO:
882                 case UiModeManager.MODE_NIGHT_YES:
883                 case UiModeManager.MODE_NIGHT_AUTO:
884                 case UiModeManager.MODE_NIGHT_CUSTOM:
885                     break;
886                 default:
887                     throw new IllegalArgumentException("Unknown mode: " + mode);
888             }
889             final int configNightMode;
890             switch (mode) {
891                 case MODE_NIGHT_YES:
892                     configNightMode = Configuration.UI_MODE_NIGHT_YES;
893                     break;
894                 case MODE_NIGHT_NO:
895                     configNightMode = Configuration.UI_MODE_NIGHT_NO;
896                     break;
897                 default:
898                     configNightMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
899             }
900             final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
901                     mActivityTaskManager.createPackageConfigurationUpdater();
902             updater.setNightMode(configNightMode);
903             updater.commit();
904         }
905 
906         @Override
907         public boolean isUiModeLocked() {
908             synchronized (mLock) {
909                 return mUiModeLocked;
910             }
911         }
912 
913         @Override
914         public boolean isNightModeLocked() {
915             synchronized (mLock) {
916                 return mNightModeLocked;
917             }
918         }
919 
920         @Override
921         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
922                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
923             new Shell(mService).exec(mService, in, out, err, args, callback, resultReceiver);
924         }
925 
926         @Override
927         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
928             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
929             dumpImpl(pw);
930         }
931 
932         @Override
933         public boolean setNightModeActivatedForCustomMode(int modeNightCustomType, boolean active) {
934             return setNightModeActivatedForModeInternal(modeNightCustomType, active);
935         }
936 
937         @Override
938         public boolean setNightModeActivated(boolean active) {
939             return setNightModeActivatedForModeInternal(mNightModeCustomType, active);
940         }
941 
942         private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) {
943             if (getContext().checkCallingOrSelfPermission(
944                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
945                     != PackageManager.PERMISSION_GRANTED) {
946                 Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
947                 return false;
948             }
949             final int user = Binder.getCallingUserHandle().getIdentifier();
950             if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
951                     android.Manifest.permission.INTERACT_ACROSS_USERS)
952                     != PackageManager.PERMISSION_GRANTED) {
953                 Slog.e(TAG, "Target user is not current user,"
954                         + " INTERACT_ACROSS_USERS permission is required");
955                 return false;
956 
957             }
958             // Store the last requested bedtime night mode state so that we don't need to notify
959             // anyone if the user decides to switch to the night mode to bedtime.
960             if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
961                 mLastBedtimeRequestedNightMode = active;
962             }
963             if (modeCustomType != mNightModeCustomType) {
964                 return false;
965             }
966             synchronized (mLock) {
967                 final long ident = Binder.clearCallingIdentity();
968                 try {
969                     if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
970                         unregisterScreenOffEventLocked();
971                         mOverrideNightModeOff = !active;
972                         mOverrideNightModeOn = active;
973                         mOverrideNightModeUser = mCurrentUser;
974                         persistNightModeOverrides(mCurrentUser);
975                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
976                             && active) {
977                         mNightMode = UiModeManager.MODE_NIGHT_YES;
978                     } else if (mNightMode == UiModeManager.MODE_NIGHT_YES
979                             && !active) {
980                         mNightMode = UiModeManager.MODE_NIGHT_NO;
981                     }
982                     updateConfigurationLocked();
983                     applyConfigurationExternallyLocked();
984                     persistNightMode(mCurrentUser);
985                     return true;
986                 } finally {
987                     Binder.restoreCallingIdentity(ident);
988                 }
989             }
990         }
991 
992         @Override
993         public long getCustomNightModeStart() {
994             return mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000;
995         }
996 
997         @Override
998         public void setCustomNightModeStart(long time) {
999             if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
1000                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
1001                     != PackageManager.PERMISSION_GRANTED) {
1002                 Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission");
1003                 return;
1004             }
1005             final int user = UserHandle.getCallingUserId();
1006             final long ident = Binder.clearCallingIdentity();
1007             try {
1008                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
1009                 if (newTime == null) return;
1010                 mCustomAutoNightModeStartMilliseconds = newTime;
1011                 persistNightMode(user);
1012                 onCustomTimeUpdated(user);
1013             } catch (DateTimeException e) {
1014                 unregisterScreenOffEventLocked();
1015             } finally {
1016                 Binder.restoreCallingIdentity(ident);
1017             }
1018         }
1019 
1020         @Override
1021         public long getCustomNightModeEnd() {
1022             return mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000;
1023         }
1024 
1025         @Override
1026         public void setCustomNightModeEnd(long time) {
1027             if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
1028                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
1029                     != PackageManager.PERMISSION_GRANTED) {
1030                 Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission");
1031                 return;
1032             }
1033             final int user = UserHandle.getCallingUserId();
1034             final long ident = Binder.clearCallingIdentity();
1035             try {
1036                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
1037                 if (newTime == null) return;
1038                 mCustomAutoNightModeEndMilliseconds = newTime;
1039                 onCustomTimeUpdated(user);
1040             } catch (DateTimeException e) {
1041                 unregisterScreenOffEventLocked();
1042             } finally {
1043                 Binder.restoreCallingIdentity(ident);
1044             }
1045         }
1046 
1047         @Override
1048         public boolean requestProjection(IBinder binder,
1049                 @UiModeManager.ProjectionType int projectionType,
1050                 @NonNull String callingPackage) {
1051             assertLegit(callingPackage);
1052             assertSingleProjectionType(projectionType);
1053             enforceProjectionTypePermissions(projectionType);
1054             synchronized (mLock) {
1055                 if (mProjectionHolders == null) {
1056                     mProjectionHolders = new SparseArray<>(1);
1057                 }
1058                 if (!mProjectionHolders.contains(projectionType)) {
1059                     mProjectionHolders.put(projectionType, new ArrayList<>(1));
1060                 }
1061                 List<ProjectionHolder> currentHolders = mProjectionHolders.get(projectionType);
1062 
1063                 // For all projection types, it's a noop if already held.
1064                 for (int i = 0; i < currentHolders.size(); ++i) {
1065                     if (callingPackage.equals(currentHolders.get(i).mPackageName)) {
1066                         return true;
1067                     }
1068                 }
1069 
1070                 // Enforce projection type-specific restrictions here.
1071 
1072                 // Automotive projection can only be set if it is currently unset. The case where it
1073                 // is already set by the calling package is taken care of above.
1074                 if (projectionType == PROJECTION_TYPE_AUTOMOTIVE && !currentHolders.isEmpty()) {
1075                     return false;
1076                 }
1077 
1078                 ProjectionHolder projectionHolder = new ProjectionHolder(callingPackage,
1079                         projectionType, binder,
1080                         UiModeManagerService.this::releaseProjectionUnchecked);
1081                 if (!projectionHolder.linkToDeath()) {
1082                     return false;
1083                 }
1084                 currentHolders.add(projectionHolder);
1085                 Slog.d(TAG, "Package " + callingPackage + " set projection type "
1086                         + projectionType + ".");
1087                 onProjectionStateChangedLocked(projectionType);
1088             }
1089             return true;
1090         }
1091 
1092         @Override
1093         public boolean releaseProjection(@UiModeManager.ProjectionType int projectionType,
1094                 @NonNull String callingPackage) {
1095             assertLegit(callingPackage);
1096             assertSingleProjectionType(projectionType);
1097             enforceProjectionTypePermissions(projectionType);
1098             return releaseProjectionUnchecked(projectionType, callingPackage);
1099         }
1100 
1101         @Override
1102         public @UiModeManager.ProjectionType int getActiveProjectionTypes() {
1103             getContext().enforceCallingOrSelfPermission(
1104                     android.Manifest.permission.READ_PROJECTION_STATE, "getActiveProjectionTypes");
1105             @UiModeManager.ProjectionType int projectionTypeFlag = PROJECTION_TYPE_NONE;
1106             synchronized (mLock) {
1107                 if (mProjectionHolders != null) {
1108                     for (int i = 0; i < mProjectionHolders.size(); ++i) {
1109                         if (!mProjectionHolders.valueAt(i).isEmpty()) {
1110                             projectionTypeFlag = projectionTypeFlag | mProjectionHolders.keyAt(i);
1111                         }
1112                     }
1113                 }
1114             }
1115             return projectionTypeFlag;
1116         }
1117 
1118         @Override
1119         public List<String> getProjectingPackages(
1120                 @UiModeManager.ProjectionType int projectionType) {
1121             getContext().enforceCallingOrSelfPermission(
1122                     android.Manifest.permission.READ_PROJECTION_STATE, "getProjectionState");
1123             synchronized (mLock) {
1124                 List<String> packageNames = new ArrayList<>();
1125                 populateWithRelevantActivePackageNames(projectionType, packageNames);
1126                 return packageNames;
1127             }
1128         }
1129 
1130         public void addOnProjectionStateChangedListener(IOnProjectionStateChangedListener listener,
1131                 @UiModeManager.ProjectionType int projectionType) {
1132             getContext().enforceCallingOrSelfPermission(
1133                     android.Manifest.permission.READ_PROJECTION_STATE,
1134                     "addOnProjectionStateChangedListener");
1135             if (projectionType == PROJECTION_TYPE_NONE) {
1136                 return;
1137             }
1138             synchronized (mLock) {
1139                 if (mProjectionListeners == null) {
1140                     mProjectionListeners = new SparseArray<>(1);
1141                 }
1142                 if (!mProjectionListeners.contains(projectionType)) {
1143                     mProjectionListeners.put(projectionType, new RemoteCallbackList<>());
1144                 }
1145                 if (mProjectionListeners.get(projectionType).register(listener)) {
1146                     // If any of those types are active, send a callback immediately.
1147                     List<String> packageNames = new ArrayList<>();
1148                     @UiModeManager.ProjectionType int activeProjectionTypes =
1149                             populateWithRelevantActivePackageNames(projectionType, packageNames);
1150                     if (!packageNames.isEmpty()) {
1151                         try {
1152                             listener.onProjectionStateChanged(activeProjectionTypes, packageNames);
1153                         } catch (RemoteException e) {
1154                             Slog.w(TAG,
1155                                     "Failed a call to onProjectionStateChanged() during listener "
1156                                             + "registration.");
1157                         }
1158                     }
1159                 }
1160             }
1161         }
1162 
1163 
1164         public void removeOnProjectionStateChangedListener(
1165                 IOnProjectionStateChangedListener listener) {
1166             getContext().enforceCallingOrSelfPermission(
1167                     android.Manifest.permission.READ_PROJECTION_STATE,
1168                     "removeOnProjectionStateChangedListener");
1169             synchronized (mLock) {
1170                 if (mProjectionListeners != null) {
1171                     for (int i = 0; i < mProjectionListeners.size(); ++i) {
1172                         mProjectionListeners.valueAt(i).unregister(listener);
1173                     }
1174                 }
1175             }
1176         }
1177 
1178         @Override
1179         public float getContrast() {
1180             synchronized (mLock) {
1181                 return getContrastLocked();
1182             }
1183         }
1184     };
1185 
enforceProjectionTypePermissions(@iModeManager.ProjectionType int p)1186     private void enforceProjectionTypePermissions(@UiModeManager.ProjectionType int p) {
1187         if ((p & PROJECTION_TYPE_AUTOMOTIVE) != 0) {
1188             getContext().enforceCallingPermission(
1189                     android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION,
1190                     "toggleProjection");
1191         }
1192     }
1193 
assertSingleProjectionType(@iModeManager.ProjectionType int p)1194     private static void assertSingleProjectionType(@UiModeManager.ProjectionType int p) {
1195         // To be a single projection type it must be non-zero and an exact power of two.
1196         boolean projectionTypeIsPowerOfTwoOrZero = (p & p - 1) == 0;
1197         if (p == 0 || !projectionTypeIsPowerOfTwoOrZero) {
1198             throw new IllegalArgumentException("Must specify exactly one projection type.");
1199         }
1200     }
1201 
toPackageNameList(Collection<ProjectionHolder> c)1202     private static List<String> toPackageNameList(Collection<ProjectionHolder> c) {
1203         List<String> packageNames = new ArrayList<>();
1204         for (ProjectionHolder p : c) {
1205             packageNames.add(p.mPackageName);
1206         }
1207         return packageNames;
1208     }
1209 
1210     /**
1211      * Populates a list with the package names that have set any of the given projection types.
1212      * @param projectionType the projection types to include
1213      * @param packageNames the list to populate with package names
1214      * @return the active projection types
1215      */
1216     @GuardedBy("mLock")
1217     @UiModeManager.ProjectionType
populateWithRelevantActivePackageNames( @iModeManager.ProjectionType int projectionType, List<String> packageNames)1218     private int populateWithRelevantActivePackageNames(
1219             @UiModeManager.ProjectionType int projectionType, List<String> packageNames) {
1220         packageNames.clear();
1221         @UiModeManager.ProjectionType int projectionTypeFlag = PROJECTION_TYPE_NONE;
1222         if (mProjectionHolders != null) {
1223             for (int i = 0; i < mProjectionHolders.size(); ++i) {
1224                 int key = mProjectionHolders.keyAt(i);
1225                 List<ProjectionHolder> holders = mProjectionHolders.valueAt(i);
1226                 if ((projectionType & key) != 0) {
1227                     if (packageNames.addAll(toPackageNameList(holders))) {
1228                         projectionTypeFlag = projectionTypeFlag | key;
1229                     }
1230                 }
1231             }
1232         }
1233         return projectionTypeFlag;
1234     }
1235 
releaseProjectionUnchecked(@iModeManager.ProjectionType int projectionType, @NonNull String pkg)1236     private boolean releaseProjectionUnchecked(@UiModeManager.ProjectionType int projectionType,
1237             @NonNull String pkg) {
1238         synchronized (mLock) {
1239             boolean removed = false;
1240             if (mProjectionHolders != null) {
1241                 List<ProjectionHolder> holders = mProjectionHolders.get(projectionType);
1242                 if (holders != null) {
1243                     // Iterate backward so we can safely remove while iterating.
1244                     for (int i = holders.size() - 1; i >= 0; --i) {
1245                         ProjectionHolder holder = holders.get(i);
1246                         if (pkg.equals(holder.mPackageName)) {
1247                             holder.unlinkToDeath();
1248                             Slog.d(TAG, "Projection type " + projectionType + " released by "
1249                                     + pkg + ".");
1250                             holders.remove(i);
1251                             removed = true;
1252                         }
1253                     }
1254                 }
1255             }
1256             if (removed) {
1257                 onProjectionStateChangedLocked(projectionType);
1258             } else {
1259                 Slog.w(TAG, pkg + " tried to release projection type " + projectionType
1260                         + " but was not set by that package.");
1261             }
1262             return removed;
1263         }
1264     }
1265 
1266     /**
1267      * Return the contrast for the current user. If not cached, fetch it from the settings.
1268      */
1269     @GuardedBy("mLock")
getContrastLocked()1270     private float getContrastLocked() {
1271         if (!mContrasts.contains(mCurrentUser)) updateContrastLocked();
1272         return mContrasts.get(mCurrentUser);
1273     }
1274 
1275     /**
1276      * Read the contrast setting for the current user and update {@link #mContrasts}
1277      * if the contrast changed. Returns true if {@link #mContrasts} was updated.
1278      */
1279     @GuardedBy("mLock")
updateContrastLocked()1280     private boolean updateContrastLocked() {
1281         float contrast = Settings.Secure.getFloatForUser(getContext().getContentResolver(),
1282                 CONTRAST_LEVEL, CONTRAST_DEFAULT_VALUE, mCurrentUser);
1283         if (Math.abs(mContrasts.get(mCurrentUser, Float.MAX_VALUE) - contrast) >= 1e-10) {
1284             mContrasts.put(mCurrentUser, contrast);
1285             return true;
1286         }
1287         return false;
1288     }
1289 
1290     private static class ProjectionHolder implements IBinder.DeathRecipient {
1291         private final String mPackageName;
1292         private final @UiModeManager.ProjectionType int mProjectionType;
1293         private final IBinder mBinder;
1294         private final ProjectionReleaser mProjectionReleaser;
1295 
ProjectionHolder(String packageName, @UiModeManager.ProjectionType int projectionType, IBinder binder, ProjectionReleaser projectionReleaser)1296         private ProjectionHolder(String packageName,
1297                 @UiModeManager.ProjectionType int projectionType, IBinder binder,
1298                 ProjectionReleaser projectionReleaser) {
1299             mPackageName = packageName;
1300             mProjectionType = projectionType;
1301             mBinder = binder;
1302             mProjectionReleaser = projectionReleaser;
1303         }
1304 
linkToDeath()1305         private boolean linkToDeath() {
1306             try {
1307                 mBinder.linkToDeath(this, 0);
1308             } catch (RemoteException e) {
1309                 Slog.e(TAG, "linkToDeath failed for projection requester: " + mPackageName + ".",
1310                         e);
1311                 return false;
1312             }
1313             return true;
1314         }
1315 
unlinkToDeath()1316         private void unlinkToDeath() {
1317             mBinder.unlinkToDeath(this, 0);
1318         }
1319 
1320         @Override
binderDied()1321         public void binderDied() {
1322             Slog.w(TAG, "Projection holder " + mPackageName
1323                     + " died. Releasing projection type " + mProjectionType + ".");
1324             mProjectionReleaser.release(mProjectionType, mPackageName);
1325         }
1326 
1327         private interface ProjectionReleaser {
release(@iModeManager.ProjectionType int projectionType, @NonNull String packageName)1328             boolean release(@UiModeManager.ProjectionType int projectionType,
1329                     @NonNull String packageName);
1330         }
1331     }
1332 
assertLegit(@onNull String packageName)1333     private void assertLegit(@NonNull String packageName) {
1334         if (!doesPackageHaveCallingUid(packageName)) {
1335             throw new SecurityException("Caller claimed bogus packageName: " + packageName + ".");
1336         }
1337     }
1338 
doesPackageHaveCallingUid(@onNull String packageName)1339     private boolean doesPackageHaveCallingUid(@NonNull String packageName) {
1340         int callingUid = mInjector.getCallingUid();
1341         int callingUserId = UserHandle.getUserId(callingUid);
1342         final long ident = Binder.clearCallingIdentity();
1343         try {
1344             return getContext().getPackageManager().getPackageUidAsUser(packageName,
1345                     callingUserId) == callingUid;
1346         } catch (PackageManager.NameNotFoundException e) {
1347             return false;
1348         } finally {
1349             Binder.restoreCallingIdentity(ident);
1350         }
1351     }
1352 
1353     @GuardedBy("mLock")
onProjectionStateChangedLocked( @iModeManager.ProjectionType int changedProjectionType)1354     private void onProjectionStateChangedLocked(
1355             @UiModeManager.ProjectionType int changedProjectionType) {
1356         if (mProjectionListeners == null) {
1357             return;
1358         }
1359         for (int i = 0; i < mProjectionListeners.size(); ++i) {
1360             int listenerProjectionType = mProjectionListeners.keyAt(i);
1361             // Every listener that is affected must be called back with all the state they are
1362             // listening for.
1363             if ((changedProjectionType & listenerProjectionType) != 0) {
1364                 RemoteCallbackList<IOnProjectionStateChangedListener> listeners =
1365                         mProjectionListeners.valueAt(i);
1366                 List<String> packageNames = new ArrayList<>();
1367                 @UiModeManager.ProjectionType int activeProjectionTypes =
1368                         populateWithRelevantActivePackageNames(listenerProjectionType,
1369                                 packageNames);
1370                 int listenerCount = listeners.beginBroadcast();
1371                 for (int j = 0; j < listenerCount; ++j) {
1372                     try {
1373                         listeners.getBroadcastItem(j).onProjectionStateChanged(
1374                                 activeProjectionTypes, packageNames);
1375                     } catch (RemoteException e) {
1376                         Slog.w(TAG, "Failed a call to onProjectionStateChanged().");
1377                     }
1378                 }
1379                 listeners.finishBroadcast();
1380             }
1381         }
1382     }
1383 
onCustomTimeUpdated(int user)1384     private void onCustomTimeUpdated(int user) {
1385         persistNightMode(user);
1386         if (mNightMode != MODE_NIGHT_CUSTOM) return;
1387         if (shouldApplyAutomaticChangesImmediately()) {
1388             unregisterScreenOffEventLocked();
1389             updateLocked(0, 0);
1390         } else {
1391             registerScreenOffEventLocked();
1392         }
1393     }
1394 
dumpImpl(PrintWriter pw)1395     void dumpImpl(PrintWriter pw) {
1396         synchronized (mLock) {
1397             pw.println("Current UI Mode Service state:");
1398             pw.print("  mDockState="); pw.print(mDockState);
1399             pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
1400 
1401             pw.print(" mStartDreamImmediatelyOnDock="); pw.print(mStartDreamImmediatelyOnDock);
1402 
1403             pw.print("  mNightMode="); pw.print(mNightMode); pw.print(" (");
1404             pw.print(Shell.nightModeToStr(mNightMode, mNightModeCustomType)); pw.print(") ");
1405             pw.print(" mOverrideOn/Off="); pw.print(mOverrideNightModeOn);
1406             pw.print("/"); pw.print(mOverrideNightModeOff);
1407 
1408             pw.print(" mNightModeLocked="); pw.println(mNightModeLocked);
1409 
1410             pw.print("  mCarModeEnabled="); pw.print(mCarModeEnabled);
1411             pw.print(" (carModeApps=");
1412             for (Map.Entry<Integer, String> entry : mCarModePackagePriority.entrySet()) {
1413                 pw.print(entry.getKey());
1414                 pw.print(":");
1415                 pw.print(entry.getValue());
1416                 pw.print(" ");
1417             }
1418             pw.println("");
1419             pw.print(" waitScreenOff="); pw.print(mWaitForScreenOff);
1420             pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
1421             pw.print(" customStart="); pw.print(mCustomAutoNightModeStartMilliseconds);
1422             pw.print(" customEnd"); pw.print(mCustomAutoNightModeEndMilliseconds);
1423             pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
1424             pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);
1425 
1426             pw.print("  mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
1427             pw.print(" mUiModeLocked="); pw.print(mUiModeLocked);
1428             pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
1429 
1430             pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
1431             pw.print(" mSystemReady="); pw.println(mSystemReady);
1432 
1433             if (mTwilightManager != null) {
1434                 // We may not have a TwilightManager.
1435                 pw.print("  mTwilightService.getLastTwilightState()=");
1436                 pw.println(mTwilightManager.getLastTwilightState());
1437             }
1438         }
1439     }
1440 
1441     /**
1442      * Updates the global car mode state.
1443      * The device is considered to be in car mode if there exists an app at any priority level which
1444      * has entered car mode.
1445      *
1446      * @param enabled {@code true} if the caller wishes to enable car mode, {@code false} otherwise.
1447      * @param flags Flags used when enabling/disabling car mode.
1448      * @param priority The priority level for entering or exiting car mode; defaults to
1449      *                 {@link UiModeManager#DEFAULT_PRIORITY} for callers using
1450      *                 {@link UiModeManager#enableCarMode(int)}.  Callers using
1451      *                 {@link UiModeManager#enableCarMode(int, int)} may specify a priority.
1452      * @param packageName The package name of the app which initiated the request to enable or
1453      *                    disable car mode.
1454      */
setCarModeLocked(boolean enabled, int flags, int priority, String packageName)1455     void setCarModeLocked(boolean enabled, int flags, int priority, String packageName) {
1456         if (enabled) {
1457             enableCarMode(priority, packageName);
1458         } else {
1459             disableCarMode(flags, priority, packageName);
1460         }
1461         boolean isCarModeNowEnabled = isCarModeEnabled();
1462 
1463         if (mCarModeEnabled != isCarModeNowEnabled) {
1464             mCarModeEnabled = isCarModeNowEnabled;
1465             // When exiting car mode, restore night mode from settings
1466             if (!isCarModeNowEnabled) {
1467                 Context context = getContext();
1468                 updateNightModeFromSettingsLocked(context,
1469                         context.getResources(),
1470                         UserHandle.getCallingUserId());
1471             }
1472         }
1473         mCarModeEnableFlags = flags;
1474     }
1475 
1476     /**
1477      * Handles disabling car mode.
1478      * <p>
1479      * Car mode can be disabled at a priority level if any of the following is true:
1480      * 1. The priority being disabled is the {@link UiModeManager#DEFAULT_PRIORITY}.
1481      * 2. The priority level is enabled and the caller is the app who originally enabled it.
1482      * 3. The {@link UiModeManager#DISABLE_CAR_MODE_ALL_PRIORITIES} flag was specified, meaning all
1483      *    car mode priorities are disabled.
1484      *
1485      * @param flags Car mode flags.
1486      * @param priority The priority level at which to disable car mode.
1487      * @param packageName The calling package which initiated the request.
1488      */
disableCarMode(int flags, int priority, String packageName)1489     private void disableCarMode(int flags, int priority, String packageName) {
1490         boolean isDisableAll = (flags & UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES) != 0;
1491         boolean isPriorityTracked = mCarModePackagePriority.keySet().contains(priority);
1492         boolean isDefaultPriority = priority == UiModeManager.DEFAULT_PRIORITY;
1493         boolean isChangeAllowed =
1494                 // Anyone can disable the default priority.
1495                 isDefaultPriority
1496                 // If priority was enabled, only enabling package can disable it.
1497                 || isPriorityTracked && mCarModePackagePriority.get(priority).equals(
1498                 packageName)
1499                 // Disable all priorities flag can disable all regardless.
1500                 || isDisableAll;
1501         if (isChangeAllowed) {
1502             Slog.d(TAG, "disableCarMode: disabling, priority=" + priority
1503                     + ", packageName=" + packageName);
1504             if (isDisableAll) {
1505                 Set<Map.Entry<Integer, String>> entries =
1506                         new ArraySet<>(mCarModePackagePriority.entrySet());
1507                 mCarModePackagePriority.clear();
1508 
1509                 for (Map.Entry<Integer, String> entry : entries) {
1510                     notifyCarModeDisabled(entry.getKey(), entry.getValue());
1511                 }
1512             } else {
1513                 mCarModePackagePriority.remove(priority);
1514                 notifyCarModeDisabled(priority, packageName);
1515             }
1516         }
1517     }
1518 
1519     /**
1520      * Handles enabling car mode.
1521      * <p>
1522      * Car mode can be enabled at any priority if it has not already been enabled at that priority.
1523      * The calling package is tracked for the first app which enters priority at the
1524      * {@link UiModeManager#DEFAULT_PRIORITY}, though any app can disable it at that priority.
1525      *
1526      * @param priority The priority for enabling car mode.
1527      * @param packageName The calling package which initiated the request.
1528      */
enableCarMode(int priority, String packageName)1529     private void enableCarMode(int priority, String packageName) {
1530         boolean isPriorityTracked = mCarModePackagePriority.containsKey(priority);
1531         boolean isPackagePresent = mCarModePackagePriority.containsValue(packageName);
1532         if (!isPriorityTracked && !isPackagePresent) {
1533             Slog.d(TAG, "enableCarMode: enabled at priority=" + priority + ", packageName="
1534                     + packageName);
1535             mCarModePackagePriority.put(priority, packageName);
1536             notifyCarModeEnabled(priority, packageName);
1537         } else {
1538             Slog.d(TAG, "enableCarMode: car mode at priority " + priority + " already enabled.");
1539         }
1540 
1541     }
1542 
notifyCarModeEnabled(int priority, String packageName)1543     private void notifyCarModeEnabled(int priority, String packageName) {
1544         Intent intent = new Intent(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
1545         intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName);
1546         intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority);
1547         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
1548                 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES);
1549     }
1550 
notifyCarModeDisabled(int priority, String packageName)1551     private void notifyCarModeDisabled(int priority, String packageName) {
1552         Intent intent = new Intent(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
1553         intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName);
1554         intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority);
1555         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
1556                 android.Manifest.permission.HANDLE_CAR_MODE_CHANGES);
1557     }
1558 
1559     /**
1560      * Determines if car mode is enabled at any priority level.
1561      * @return {@code true} if car mode is enabled, {@code false} otherwise.
1562      */
isCarModeEnabled()1563     private boolean isCarModeEnabled() {
1564         return mCarModePackagePriority.size() > 0;
1565     }
1566 
updateDockState(int newState)1567     private void updateDockState(int newState) {
1568         synchronized (mLock) {
1569             if (newState != mDockState) {
1570                 mDockState = newState;
1571                 setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0,
1572                         UiModeManager.DEFAULT_PRIORITY, "" /* packageName */);
1573                 if (mSystemReady) {
1574                     updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0);
1575                 }
1576             }
1577         }
1578     }
1579 
isDeskDockState(int state)1580     private static boolean isDeskDockState(int state) {
1581         switch (state) {
1582             case Intent.EXTRA_DOCK_STATE_DESK:
1583             case Intent.EXTRA_DOCK_STATE_LE_DESK:
1584             case Intent.EXTRA_DOCK_STATE_HE_DESK:
1585                 return true;
1586             default:
1587                 return false;
1588         }
1589     }
1590 
persistNightMode(int user)1591     private void persistNightMode(int user) {
1592         // Only persist setting if not in car mode
1593         if (mCarModeEnabled || mCar) return;
1594         Secure.putIntForUser(getContext().getContentResolver(),
1595                 Secure.UI_NIGHT_MODE, mNightMode, user);
1596         Secure.putLongForUser(getContext().getContentResolver(),
1597                 Secure.UI_NIGHT_MODE_CUSTOM_TYPE, mNightModeCustomType, user);
1598         Secure.putLongForUser(getContext().getContentResolver(),
1599                 Secure.DARK_THEME_CUSTOM_START_TIME,
1600                 mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user);
1601         Secure.putLongForUser(getContext().getContentResolver(),
1602                 Secure.DARK_THEME_CUSTOM_END_TIME,
1603                 mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000, user);
1604     }
1605 
persistNightModeOverrides(int user)1606     private void persistNightModeOverrides(int user) {
1607         // Only persist setting if not in car mode
1608         if (mCarModeEnabled || mCar) return;
1609         Secure.putIntForUser(getContext().getContentResolver(),
1610                 Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user);
1611         Secure.putIntForUser(getContext().getContentResolver(),
1612                 Secure.UI_NIGHT_MODE_OVERRIDE_OFF, mOverrideNightModeOff ? 1 : 0, user);
1613     }
1614 
updateConfigurationLocked()1615     private void updateConfigurationLocked() {
1616         int uiMode = mDefaultUiModeType;
1617         if (mUiModeLocked) {
1618             // no-op, keeps default one
1619         } else if (mTelevision) {
1620             uiMode = Configuration.UI_MODE_TYPE_TELEVISION;
1621         } else if (mWatch) {
1622             uiMode = Configuration.UI_MODE_TYPE_WATCH;
1623         } else if (mCarModeEnabled) {
1624             uiMode = Configuration.UI_MODE_TYPE_CAR;
1625         } else if (isDeskDockState(mDockState)) {
1626             uiMode = Configuration.UI_MODE_TYPE_DESK;
1627         } else if (mVrHeadset) {
1628             uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
1629         }
1630 
1631         if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) {
1632             updateComputedNightModeLocked(mNightMode == MODE_NIGHT_YES);
1633         }
1634 
1635         if (mNightMode == MODE_NIGHT_AUTO) {
1636             boolean activateNightMode = mComputedNightMode;
1637             if (mTwilightManager != null) {
1638                 mTwilightManager.registerListener(mTwilightListener, mHandler);
1639                 final TwilightState lastState = mTwilightManager.getLastTwilightState();
1640                 activateNightMode = lastState == null ? mComputedNightMode : lastState.isNight();
1641             }
1642             updateComputedNightModeLocked(activateNightMode);
1643         } else {
1644             if (mTwilightManager != null) {
1645                 mTwilightManager.unregisterListener(mTwilightListener);
1646             }
1647         }
1648 
1649         if (mNightMode == MODE_NIGHT_CUSTOM) {
1650             if (mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
1651                 updateComputedNightModeLocked(mLastBedtimeRequestedNightMode);
1652             } else {
1653                 registerTimeChangeEvent();
1654                 final boolean activate = computeCustomNightMode();
1655                 updateComputedNightModeLocked(activate);
1656                 scheduleNextCustomTimeListener();
1657             }
1658         } else {
1659             unregisterTimeChangeEvent();
1660         }
1661 
1662         // Override night mode in power save mode if not in car mode
1663         if (mPowerSave && !mCarModeEnabled && !mCar) {
1664             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
1665             uiMode |= Configuration.UI_MODE_NIGHT_YES;
1666         } else {
1667             uiMode = getComputedUiModeConfiguration(uiMode);
1668         }
1669 
1670         if (LOG) {
1671             Slog.d(TAG,
1672                     "updateConfigurationLocked: mDockState=" + mDockState
1673                     + "; mCarMode=" + mCarModeEnabled
1674                     + "; mNightMode=" + mNightMode
1675                     + "; mNightModeCustomType=" + mNightModeCustomType
1676                     + "; uiMode=" + uiMode);
1677         }
1678 
1679         mCurUiMode = uiMode;
1680         if (!mHoldingConfiguration && (!mWaitForScreenOff || mPowerSave)) {
1681             mConfiguration.uiMode = uiMode;
1682         }
1683     }
1684 
1685     @UiModeManager.NightMode
getComputedUiModeConfiguration(@iModeManager.NightMode int uiMode)1686     private int getComputedUiModeConfiguration(@UiModeManager.NightMode int uiMode) {
1687         uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
1688                 : Configuration.UI_MODE_NIGHT_NO;
1689         uiMode &= mComputedNightMode ? ~Configuration.UI_MODE_NIGHT_NO
1690                 : ~Configuration.UI_MODE_NIGHT_YES;
1691         return uiMode;
1692     }
1693 
computeCustomNightMode()1694     private boolean computeCustomNightMode() {
1695         return isTimeBetween(LocalTime.now(),
1696                 mCustomAutoNightModeStartMilliseconds,
1697                 mCustomAutoNightModeEndMilliseconds);
1698     }
1699 
applyConfigurationExternallyLocked()1700     private void applyConfigurationExternallyLocked() {
1701         if (mSetUiMode != mConfiguration.uiMode) {
1702             mSetUiMode = mConfiguration.uiMode;
1703             // load splash screen instead of screenshot
1704             mWindowManager.clearSnapshotCache();
1705             try {
1706                 ActivityTaskManager.getService().updateConfiguration(mConfiguration);
1707             } catch (RemoteException e) {
1708                 Slog.w(TAG, "Failure communicating with activity manager", e);
1709             } catch (SecurityException e) {
1710                 Slog.e(TAG, "Activity does not have the ", e);
1711             }
1712         }
1713     }
1714 
shouldApplyAutomaticChangesImmediately()1715     private boolean shouldApplyAutomaticChangesImmediately() {
1716         return mCar || !mPowerManager.isInteractive()
1717                 || mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
1718     }
1719 
scheduleNextCustomTimeListener()1720     private void scheduleNextCustomTimeListener() {
1721         cancelCustomAlarm();
1722         LocalDateTime now = LocalDateTime.now();
1723         final boolean active = computeCustomNightMode();
1724         final LocalDateTime next = active
1725                 ? getDateTimeAfter(mCustomAutoNightModeEndMilliseconds, now)
1726                 : getDateTimeAfter(mCustomAutoNightModeStartMilliseconds, now);
1727         final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
1728         mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, mCustomTimeListener, null);
1729     }
1730 
getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime)1731     private LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
1732         final LocalDateTime ldt = LocalDateTime.of(compareTime.toLocalDate(), localTime);
1733 
1734         // Check if the local time has passed, if so return the same time tomorrow.
1735         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
1736     }
1737 
updateLocked(int enableFlags, int disableFlags)1738     void updateLocked(int enableFlags, int disableFlags) {
1739         String action = null;
1740         String oldAction = null;
1741         if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
1742             adjustStatusBarCarModeLocked();
1743             oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
1744         } else if (isDeskDockState(mLastBroadcastState)) {
1745             oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
1746         }
1747 
1748         if (mCarModeEnabled) {
1749             if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
1750                 adjustStatusBarCarModeLocked();
1751                 if (oldAction != null) {
1752                     sendForegroundBroadcastToAllUsers(oldAction);
1753                 }
1754                 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
1755                 action = UiModeManager.ACTION_ENTER_CAR_MODE;
1756             }
1757         } else if (isDeskDockState(mDockState)) {
1758             if (!isDeskDockState(mLastBroadcastState)) {
1759                 if (oldAction != null) {
1760                     sendForegroundBroadcastToAllUsers(oldAction);
1761                 }
1762                 mLastBroadcastState = mDockState;
1763                 action = UiModeManager.ACTION_ENTER_DESK_MODE;
1764             }
1765         } else {
1766             mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
1767             action = oldAction;
1768         }
1769 
1770         if (action != null) {
1771             if (LOG) {
1772                 Slog.v(TAG, String.format(
1773                     "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x",
1774                     action, enableFlags, disableFlags));
1775             }
1776 
1777             // Send the ordered broadcast; the result receiver will receive after all
1778             // broadcasts have been sent. If any broadcast receiver changes the result
1779             // code from the initial value of RESULT_OK, then the result receiver will
1780             // not launch the corresponding dock application. This gives apps a chance
1781             // to override the behavior and stay in their app even when the device is
1782             // placed into a dock.
1783             Intent intent = new Intent(action);
1784             intent.putExtra("enableFlags", enableFlags);
1785             intent.putExtra("disableFlags", disableFlags);
1786             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1787             getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
1788                     mResultReceiver, null, Activity.RESULT_OK, null, null);
1789 
1790             // Attempting to make this transition a little more clean, we are going
1791             // to hold off on doing a configuration change until we have finished
1792             // the broadcast and started the home activity.
1793             mHoldingConfiguration = true;
1794             updateConfigurationLocked();
1795         } else {
1796             String category = null;
1797             if (mCarModeEnabled) {
1798                 if (mEnableCarDockLaunch
1799                         && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1800                     category = Intent.CATEGORY_CAR_DOCK;
1801                 }
1802             } else if (isDeskDockState(mDockState)) {
1803                 if (ENABLE_LAUNCH_DESK_DOCK_APP
1804                         && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1805                     category = Intent.CATEGORY_DESK_DOCK;
1806                 }
1807             } else {
1808                 if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
1809                     category = Intent.CATEGORY_HOME;
1810                 }
1811             }
1812 
1813             if (LOG) {
1814                 Slog.v(TAG, "updateLocked: null action, mDockState="
1815                         + mDockState +", category=" + category);
1816             }
1817 
1818             sendConfigurationAndStartDreamOrDockAppLocked(category);
1819         }
1820 
1821         // keep screen on when charging and in car mode
1822         boolean keepScreenOn = mCharging &&
1823                 ((mCarModeEnabled && mCarModeKeepsScreenOn &&
1824                 (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
1825                 (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
1826         if (keepScreenOn != mWakeLock.isHeld()) {
1827             if (keepScreenOn) {
1828                 mWakeLock.acquire();
1829             } else {
1830                 mWakeLock.release();
1831             }
1832         }
1833     }
1834 
sendForegroundBroadcastToAllUsers(String action)1835     private void sendForegroundBroadcastToAllUsers(String action) {
1836         getContext().sendBroadcastAsUser(new Intent(action)
1837                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL);
1838     }
1839 
updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags)1840     private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) {
1841         // Launch a dock activity
1842         String category = null;
1843         if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) {
1844             // Only launch car home when car mode is enabled and the caller
1845             // has asked us to switch to it.
1846             if (mEnableCarDockLaunch
1847                     && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1848                 category = Intent.CATEGORY_CAR_DOCK;
1849             }
1850         } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) {
1851             // Only launch car home when desk mode is enabled and the caller
1852             // has asked us to switch to it.  Currently re-using the car
1853             // mode flag since we don't have a formal API for "desk mode".
1854             if (ENABLE_LAUNCH_DESK_DOCK_APP
1855                     && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
1856                 category = Intent.CATEGORY_DESK_DOCK;
1857             }
1858         } else {
1859             // Launch the standard home app if requested.
1860             if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
1861                 category = Intent.CATEGORY_HOME;
1862             }
1863         }
1864 
1865         if (LOG) {
1866             Slog.v(TAG, String.format(
1867                     "Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, "
1868                             + "category=%s",
1869                     action, enableFlags, disableFlags, category));
1870         }
1871 
1872         sendConfigurationAndStartDreamOrDockAppLocked(category);
1873     }
1874 
sendConfigurationAndStartDreamOrDockAppLocked(String category)1875     private void sendConfigurationAndStartDreamOrDockAppLocked(String category) {
1876         // Update the configuration but don't send it yet.
1877         mHoldingConfiguration = false;
1878         updateConfigurationLocked();
1879 
1880         // Start the dock app, if there is one.
1881         boolean dockAppStarted = false;
1882         if (category != null) {
1883             // Now we are going to be careful about switching the
1884             // configuration and starting the activity -- we need to
1885             // do this in a specific order under control of the
1886             // activity manager, to do it cleanly.  So compute the
1887             // new config, but don't set it yet, and let the
1888             // activity manager take care of both the start and config
1889             // change.
1890             Intent homeIntent = buildHomeIntent(category);
1891             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
1892                 try {
1893                     int result = ActivityTaskManager.getService().startActivityWithConfig(
1894                             null, getContext().getBasePackageName(),
1895                             getContext().getAttributionTag(), homeIntent, null, null, null, 0, 0,
1896                             mConfiguration, null, UserHandle.USER_CURRENT);
1897                     if (ActivityManager.isStartResultSuccessful(result)) {
1898                         dockAppStarted = true;
1899                     } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
1900                         Slog.e(TAG, "Could not start dock app: " + homeIntent
1901                                 + ", startActivityWithConfig result " + result);
1902                     }
1903                 } catch (RemoteException ex) {
1904                     Slog.e(TAG, "Could not start dock app: " + homeIntent, ex);
1905                 }
1906             }
1907         }
1908 
1909         // Send the new configuration.
1910         applyConfigurationExternallyLocked();
1911 
1912         final boolean dreamsSuppressed = mDreamsDisabledByAmbientModeSuppression
1913                 && mLocalPowerManager.isAmbientDisplaySuppressed();
1914 
1915         // If we did not start a dock app, then start dreaming if appropriate.
1916         if (category != null && !dockAppStarted && !dreamsSuppressed && (
1917                 mStartDreamImmediatelyOnDock
1918                         || mWindowManager.isKeyguardShowingAndNotOccluded()
1919                         || !mPowerManager.isInteractive())) {
1920             mInjector.startDreamWhenDockedIfAppropriate(getContext());
1921         }
1922     }
1923 
adjustStatusBarCarModeLocked()1924     private void adjustStatusBarCarModeLocked() {
1925         final Context context = getContext();
1926         if (mStatusBarManager == null) {
1927             mStatusBarManager = (StatusBarManager)
1928                     context.getSystemService(Context.STATUS_BAR_SERVICE);
1929         }
1930 
1931         // Fear not: StatusBarManagerService manages a list of requests to disable
1932         // features of the status bar; these are ORed together to form the
1933         // active disabled list. So if (for example) the device is locked and
1934         // the status bar should be totally disabled, the calls below will
1935         // have no effect until the device is unlocked.
1936         if (mStatusBarManager != null) {
1937             mStatusBarManager.disable(mCarModeEnabled
1938                     ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
1939                     : StatusBarManager.DISABLE_NONE);
1940         }
1941 
1942         if (mNotificationManager == null) {
1943             mNotificationManager = (NotificationManager)
1944                     context.getSystemService(Context.NOTIFICATION_SERVICE);
1945         }
1946 
1947         if (mNotificationManager != null) {
1948             if (mCarModeEnabled) {
1949                 Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class);
1950 
1951                 Notification.Builder n =
1952                         new Notification.Builder(context, SystemNotificationChannels.CAR_MODE)
1953                         .setSmallIcon(R.drawable.stat_notify_car_mode)
1954                         .setDefaults(Notification.DEFAULT_LIGHTS)
1955                         .setOngoing(true)
1956                         .setWhen(0)
1957                         .setColor(context.getColor(
1958                                 com.android.internal.R.color.system_notification_accent_color))
1959                         .setContentTitle(
1960                                 context.getString(R.string.car_mode_disable_notification_title))
1961                         .setContentText(
1962                                 context.getString(R.string.car_mode_disable_notification_message))
1963 
1964                         .setContentIntent(
1965                                 // TODO(b/173744200) Please replace FLAG_MUTABLE_UNAUDITED below
1966                                 // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE.
1967                                 PendingIntent.getActivityAsUser(context, 0,
1968                                         carModeOffIntent, PendingIntent.FLAG_MUTABLE,
1969                                         null, UserHandle.CURRENT));
1970                 mNotificationManager.notifyAsUser(null,
1971                         SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL);
1972             } else {
1973                 mNotificationManager.cancelAsUser(null,
1974                         SystemMessage.NOTE_CAR_MODE_DISABLE, UserHandle.ALL);
1975             }
1976         }
1977     }
1978 
updateComputedNightModeLocked(boolean activate)1979     private void updateComputedNightModeLocked(boolean activate) {
1980         mComputedNightMode = activate;
1981         if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) {
1982             return;
1983         }
1984         if (mOverrideNightModeOn && !mComputedNightMode) {
1985             mComputedNightMode = true;
1986             return;
1987         }
1988         if (mOverrideNightModeOff && mComputedNightMode) {
1989             mComputedNightMode = false;
1990             return;
1991         }
1992         if (mNightMode != MODE_NIGHT_AUTO || (mTwilightManager != null
1993                 && mTwilightManager.getLastTwilightState() != null)) {
1994             resetNightModeOverrideLocked();
1995         }
1996     }
1997 
resetNightModeOverrideLocked()1998     private boolean resetNightModeOverrideLocked() {
1999         if (mOverrideNightModeOff || mOverrideNightModeOn) {
2000             mOverrideNightModeOff = false;
2001             mOverrideNightModeOn = false;
2002             persistNightModeOverrides(mOverrideNightModeUser);
2003             mOverrideNightModeUser = USER_SYSTEM;
2004             return true;
2005         }
2006         return false;
2007     }
2008 
registerVrStateListener()2009     private void registerVrStateListener() {
2010         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
2011                 Context.VR_SERVICE));
2012         try {
2013             if (vrManager != null) {
2014                 vrManager.registerListener(mVrStateCallbacks);
2015             }
2016         } catch (RemoteException e) {
2017             Slog.e(TAG, "Failed to register VR mode state listener: " + e);
2018         }
2019     }
2020 
2021     /**
2022      * Handles "adb shell" commands.
2023      */
2024     private static class Shell extends ShellCommand {
2025         public static final String NIGHT_MODE_STR_YES = "yes";
2026         public static final String NIGHT_MODE_STR_NO = "no";
2027         public static final String NIGHT_MODE_STR_AUTO = "auto";
2028         public static final String NIGHT_MODE_STR_CUSTOM_SCHEDULE = "custom_schedule";
2029         public static final String NIGHT_MODE_STR_CUSTOM_BEDTIME = "custom_bedtime";
2030         public static final String NIGHT_MODE_STR_UNKNOWN = "unknown";
2031         private final IUiModeManager mInterface;
2032 
Shell(IUiModeManager iface)2033         Shell(IUiModeManager iface) {
2034             mInterface = iface;
2035         }
2036 
2037         @Override
onHelp()2038         public void onHelp() {
2039             final PrintWriter pw = getOutPrintWriter();
2040             pw.println("UiModeManager service (uimode) commands:");
2041             pw.println("  help");
2042             pw.println("    Print this help text.");
2043             pw.println("  night [yes|no|auto|custom_schedule|custom_bedtime]");
2044             pw.println("    Set or read night mode.");
2045             pw.println("  car [yes|no]");
2046             pw.println("    Set or read car mode.");
2047             pw.println("  time [start|end] <ISO time>");
2048             pw.println("    Set custom start/end schedule time"
2049                     + " (night mode must be set to custom to apply).");
2050         }
2051 
2052         @Override
onCommand(String cmd)2053         public int onCommand(String cmd) {
2054             if (cmd == null) {
2055                 return handleDefaultCommands(cmd);
2056             }
2057 
2058             try {
2059                 switch (cmd) {
2060                     case "night":
2061                         return handleNightMode();
2062                     case "car":
2063                         return handleCarMode();
2064                     case "time":
2065                         return handleCustomTime();
2066                     default:
2067                         return handleDefaultCommands(cmd);
2068                 }
2069             } catch (RemoteException e) {
2070                 final PrintWriter err = getErrPrintWriter();
2071                 err.println("Remote exception: " + e);
2072             }
2073             return -1;
2074         }
2075 
handleCustomTime()2076         private int handleCustomTime() throws RemoteException {
2077             final String modeStr = getNextArg();
2078             if (modeStr == null) {
2079                 printCustomTime();
2080                 return 0;
2081             }
2082             switch (modeStr) {
2083                 case "start":
2084                     final String start = getNextArg();
2085                     mInterface.setCustomNightModeStart(toMilliSeconds(LocalTime.parse(start)));
2086                     return 0;
2087                 case "end":
2088                     final String end = getNextArg();
2089                     mInterface.setCustomNightModeEnd(toMilliSeconds(LocalTime.parse(end)));
2090                     return 0;
2091                 default:
2092                     getErrPrintWriter().println("command must be in [start|end]");
2093                     return -1;
2094             }
2095         }
2096 
printCustomTime()2097         private void printCustomTime() throws RemoteException {
2098             getOutPrintWriter().println("start " + fromMilliseconds(
2099                     mInterface.getCustomNightModeStart()).toString());
2100             getOutPrintWriter().println("end " + fromMilliseconds(
2101                     mInterface.getCustomNightModeEnd()).toString());
2102         }
2103 
handleNightMode()2104         private int handleNightMode() throws RemoteException {
2105             final PrintWriter err = getErrPrintWriter();
2106             final String modeStr = getNextArg();
2107             if (modeStr == null) {
2108                 printCurrentNightMode();
2109                 return 0;
2110             }
2111 
2112             final int mode = strToNightMode(modeStr);
2113             final int customType = strToNightModeCustomType(modeStr);
2114             if (mode >= 0) {
2115                 mInterface.setNightMode(mode);
2116                 if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
2117                     mInterface.setNightModeCustomType(customType);
2118                 }
2119                 printCurrentNightMode();
2120                 return 0;
2121             } else {
2122                 err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '"
2123                         + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO
2124                         +  "', or '" + NIGHT_MODE_STR_CUSTOM_SCHEDULE + "', or '"
2125                         + NIGHT_MODE_STR_CUSTOM_BEDTIME + "'");
2126                 return -1;
2127             }
2128         }
2129 
printCurrentNightMode()2130         private void printCurrentNightMode() throws RemoteException {
2131             final PrintWriter pw = getOutPrintWriter();
2132             final int currMode = mInterface.getNightMode();
2133             final int customType = mInterface.getNightModeCustomType();
2134             final String currModeStr = nightModeToStr(currMode, customType);
2135             pw.println("Night mode: " + currModeStr);
2136         }
2137 
nightModeToStr(int mode, int customType)2138         private static String nightModeToStr(int mode, int customType) {
2139             switch (mode) {
2140                 case UiModeManager.MODE_NIGHT_YES:
2141                     return NIGHT_MODE_STR_YES;
2142                 case UiModeManager.MODE_NIGHT_NO:
2143                     return NIGHT_MODE_STR_NO;
2144                 case UiModeManager.MODE_NIGHT_AUTO:
2145                     return NIGHT_MODE_STR_AUTO;
2146                 case MODE_NIGHT_CUSTOM:
2147                     if (customType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE) {
2148                         return NIGHT_MODE_STR_CUSTOM_SCHEDULE;
2149                     }
2150                     if (customType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
2151                         return NIGHT_MODE_STR_CUSTOM_BEDTIME;
2152                     }
2153                 default:
2154                     return NIGHT_MODE_STR_UNKNOWN;
2155             }
2156         }
2157 
strToNightMode(String modeStr)2158         private static int strToNightMode(String modeStr) {
2159             switch (modeStr) {
2160                 case NIGHT_MODE_STR_YES:
2161                     return UiModeManager.MODE_NIGHT_YES;
2162                 case NIGHT_MODE_STR_NO:
2163                     return UiModeManager.MODE_NIGHT_NO;
2164                 case NIGHT_MODE_STR_AUTO:
2165                     return UiModeManager.MODE_NIGHT_AUTO;
2166                 case NIGHT_MODE_STR_CUSTOM_SCHEDULE:
2167                 case NIGHT_MODE_STR_CUSTOM_BEDTIME:
2168                     return UiModeManager.MODE_NIGHT_CUSTOM;
2169                 default:
2170                     return -1;
2171             }
2172         }
2173 
strToNightModeCustomType(String customTypeStr)2174         private static int strToNightModeCustomType(String customTypeStr) {
2175             switch (customTypeStr) {
2176                 case NIGHT_MODE_STR_CUSTOM_BEDTIME:
2177                     return UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
2178                 case NIGHT_MODE_STR_CUSTOM_SCHEDULE:
2179                     return UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
2180                 default:
2181                     return -1;
2182             }
2183         }
2184 
handleCarMode()2185         private int handleCarMode() throws RemoteException {
2186             final PrintWriter err = getErrPrintWriter();
2187             final String modeStr = getNextArg();
2188             if (modeStr == null) {
2189                 printCurrentCarMode();
2190                 return 0;
2191             }
2192 
2193             if (modeStr.equals("yes")) {
2194                 mInterface.enableCarMode(0 /* flags */, DEFAULT_PRIORITY, "" /* package */);
2195                 printCurrentCarMode();
2196                 return 0;
2197             } else if (modeStr.equals("no")) {
2198                 mInterface.disableCarMode(0 /* flags */);
2199                 printCurrentCarMode();
2200                 return 0;
2201             } else {
2202                 err.println("Error: mode must be 'yes', or 'no'");
2203                 return -1;
2204             }
2205         }
2206 
printCurrentCarMode()2207         private void printCurrentCarMode() throws RemoteException {
2208             final PrintWriter pw = getOutPrintWriter();
2209             final int currMode = mInterface.getCurrentModeType();
2210             pw.println("Car mode: " + (currMode == Configuration.UI_MODE_TYPE_CAR ? "yes" : "no"));
2211         }
2212     }
2213 
2214     public final class LocalService extends UiModeManagerInternal {
2215 
2216         @Override
isNightMode()2217         public boolean isNightMode() {
2218             synchronized (mLock) {
2219                 final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0;
2220                 if (LOG) {
2221                     Slog.d(TAG,
2222                         "LocalService.isNightMode(): mNightMode=" + mNightMode
2223                         + "; mComputedNightMode=" + mComputedNightMode
2224                         + "; uiMode=" + mConfiguration.uiMode
2225                         + "; isIt=" + isIt);
2226                 }
2227                 return isIt;
2228             }
2229         }
2230     }
2231 
2232     @VisibleForTesting
2233     public static class Injector {
getCallingUid()2234         public int getCallingUid() {
2235             return Binder.getCallingUid();
2236         }
2237 
startDreamWhenDockedIfAppropriate(Context context)2238         public void startDreamWhenDockedIfAppropriate(Context context) {
2239             Sandman.startDreamWhenDockedIfAppropriate(context);
2240         }
2241     }
2242 }
2243