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