1 /*
2  * Copyright (C) 2010 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 android.app;
18 
19 import static android.app.Flags.enableNightModeBinderCache;
20 
21 import android.annotation.CallbackExecutor;
22 import android.annotation.FlaggedApi;
23 import android.annotation.FloatRange;
24 import android.annotation.IntDef;
25 import android.annotation.IntRange;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.RequiresPermission;
29 import android.annotation.SystemApi;
30 import android.annotation.SystemService;
31 import android.annotation.TestApi;
32 import android.compat.annotation.UnsupportedAppUsage;
33 import android.content.Context;
34 import android.content.res.Configuration;
35 import android.os.Binder;
36 import android.os.IpcDataCache;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.ServiceManager.ServiceNotFoundException;
40 import android.util.ArrayMap;
41 import android.util.ArraySet;
42 import android.util.Log;
43 import android.util.Slog;
44 
45 import com.android.internal.annotations.GuardedBy;
46 import com.android.internal.util.function.pooled.PooledLambda;
47 
48 import java.lang.annotation.Retention;
49 import java.lang.annotation.RetentionPolicy;
50 import java.lang.ref.WeakReference;
51 import java.time.LocalTime;
52 import java.util.Comparator;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Objects;
56 import java.util.Set;
57 import java.util.concurrent.Executor;
58 import java.util.stream.Stream;
59 
60 /**
61  * This class provides access to the system uimode services.  These services
62  * allow applications to control UI modes of the device.
63  * It provides functionality to disable the car mode and it gives access to the
64  * night mode settings.
65  *
66  * <p>These facilities are built on top of the underlying
67  * {@link android.content.Intent#ACTION_DOCK_EVENT} broadcasts that are sent when the user
68  * physical places the device into and out of a dock.  When that happens,
69  * the UiModeManager switches the system {@link android.content.res.Configuration}
70  * to the appropriate UI mode, sends broadcasts about the mode switch, and
71  * starts the corresponding mode activity if appropriate.  See the
72  * broadcasts {@link #ACTION_ENTER_CAR_MODE} and
73  * {@link #ACTION_ENTER_DESK_MODE} for more information.
74  *
75  * <p>In addition, the user may manually switch the system to car mode without
76  * physically being in a dock.  While in car mode -- whether by manual action
77  * from the user or being physically placed in a dock -- a notification is
78  * displayed allowing the user to exit dock mode.  Thus the dock mode
79  * represented here may be different than the current state of the underlying
80  * dock event broadcast.
81  */
82 @SystemService(Context.UI_MODE_SERVICE)
83 public class UiModeManager {
84 
85     private static final String TAG = "UiModeManager";
86 
87 
88     /**
89      * A listener with a single method that is invoked whenever the packages projecting using the
90      * {@link ProjectionType}s for which it is registered change.
91      *
92      * @hide
93      */
94     @SystemApi
95     public interface OnProjectionStateChangedListener {
96         /**
97          * Callback invoked when projection state changes for a {@link ProjectionType} for which
98          * this listener was added.
99          * @param projectionType the listened-for {@link ProjectionType}s that have changed
100          * @param packageNames the {@link Set} of package names that have currently set those
101          *     {@link ProjectionType}s.
102          */
onProjectionStateChanged(@rojectionType int projectionType, @NonNull Set<String> packageNames)103         void onProjectionStateChanged(@ProjectionType int projectionType,
104                 @NonNull Set<String> packageNames);
105     }
106 
107     /**
108      * Listener for the UI contrast. To listen for changes to
109      * the UI contrast on the device, implement this interface and
110      * register it with the system by calling {@link #addContrastChangeListener}.
111      */
112     public interface ContrastChangeListener {
113 
114         /**
115          * Called when the color contrast enabled state changes.
116          *
117          * @param contrast The color contrast as in {@link #getContrast}
118          */
onContrastChanged(@loatRangefrom = -1.0f, to = 1.0f) float contrast)119         void onContrastChanged(@FloatRange(from = -1.0f, to = 1.0f) float contrast);
120     }
121 
122     /**
123      * Broadcast sent when the device's UI has switched to car mode, either
124      * by being placed in a car dock or explicit action of the user.  After
125      * sending the broadcast, the system will start the intent
126      * {@link android.content.Intent#ACTION_MAIN} with category
127      * {@link android.content.Intent#CATEGORY_CAR_DOCK}
128      * to display the car UI, which typically what an application would
129      * implement to provide their own interface.  However, applications can
130      * also monitor this Intent in order to be informed of mode changes or
131      * prevent the normal car UI from being displayed by setting the result
132      * of the broadcast to {@link Activity#RESULT_CANCELED}.
133      * <p>
134      * This intent is broadcast when {@link #getCurrentModeType()} transitions to
135      * {@link Configuration#UI_MODE_TYPE_CAR} from some other ui mode.
136      */
137     public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE";
138 
139     /**
140      * Broadcast sent when an app has entered car mode using either {@link #enableCarMode(int)} or
141      * {@link #enableCarMode(int, int)}.
142      * <p>
143      * Unlike {@link #ACTION_ENTER_CAR_MODE}, which is only sent when the global car mode state
144      * (i.e. {@link #getCurrentModeType()}) transitions to {@link Configuration#UI_MODE_TYPE_CAR},
145      * this intent is sent any time an app declares it has entered car mode.  Thus, this intent is
146      * intended for use by a component which needs to know not only when the global car mode state
147      * changed, but also when the highest priority app declaring car mode has changed.
148      * <p>
149      * This broadcast includes the package name of the app which requested to enter car mode in
150      * {@link #EXTRA_CALLING_PACKAGE}.  The priority the app entered car mode at is specified in
151      * {@link #EXTRA_PRIORITY}.
152      * <p>
153      * This is primarily intended to be received by other components of the Android OS.
154      * <p>
155      * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES}
156      * @hide
157      */
158     @SystemApi
159     public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED =
160             "android.app.action.ENTER_CAR_MODE_PRIORITIZED";
161 
162     /**
163      * Broadcast sent when the device's UI has switch away from car mode back
164      * to normal mode.  Typically used by a car mode app, to dismiss itself
165      * when the user exits car mode.
166      * <p>
167      * This intent is broadcast when {@link #getCurrentModeType()} transitions from
168      * {@link Configuration#UI_MODE_TYPE_CAR} to some other ui mode.
169      */
170     public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE";
171 
172     /**
173      * Broadcast sent when an app has exited car mode using {@link #disableCarMode(int)}.
174      * <p>
175      * Unlike {@link #ACTION_EXIT_CAR_MODE}, which is only sent when the global car mode state
176      * (i.e. {@link #getCurrentModeType()}) transitions to a non-car mode state such as
177      * {@link Configuration#UI_MODE_TYPE_NORMAL}, this intent is sent any time an app declares it
178      * has exited car mode.  Thus, this intent is intended for use by a component which needs to
179      * know not only when the global car mode state changed, but also when the highest priority app
180      * declaring car mode has changed.
181      * <p>
182      * This broadcast includes the package name of the app which requested to exit car mode in
183      * {@link #EXTRA_CALLING_PACKAGE}.  The priority the app originally entered car mode at is
184      * specified in {@link #EXTRA_PRIORITY}.
185      * <p>
186      * If {@link #DISABLE_CAR_MODE_ALL_PRIORITIES} is used when disabling car mode (i.e. this is
187      * initiated by the user via the persistent car mode notification), this broadcast is sent once
188      * for each priority level for which car mode is being disabled.
189      * <p>
190      * This is primarily intended to be received by other components of the Android OS.
191      * <p>
192      * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES}
193      * @hide
194      */
195     @SystemApi
196     public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED =
197             "android.app.action.EXIT_CAR_MODE_PRIORITIZED";
198 
199     /**
200      * Broadcast sent when the device's UI has switched to desk mode,
201      * by being placed in a desk dock.  After
202      * sending the broadcast, the system will start the intent
203      * {@link android.content.Intent#ACTION_MAIN} with category
204      * {@link android.content.Intent#CATEGORY_DESK_DOCK}
205      * to display the desk UI, which typically what an application would
206      * implement to provide their own interface.  However, applications can
207      * also monitor this Intent in order to be informed of mode changes or
208      * prevent the normal desk UI from being displayed by setting the result
209      * of the broadcast to {@link Activity#RESULT_CANCELED}.
210      */
211     public static String ACTION_ENTER_DESK_MODE = "android.app.action.ENTER_DESK_MODE";
212 
213     /**
214      * Broadcast sent when the device's UI has switched away from desk mode back
215      * to normal mode.  Typically used by a desk mode app, to dismiss itself
216      * when the user exits desk mode.
217      */
218     public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
219 
220     /**
221      * String extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and
222      * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the package name of the app which
223      * requested to enter or exit car mode.
224      * @hide
225      */
226     @SystemApi
227     public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE";
228 
229     /**
230      * Integer extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and
231      * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the priority level at which car mode
232      * is being disabled.
233      * @hide
234      */
235     @SystemApi
236     public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY";
237 
238     /** @hide */
239     @IntDef(prefix = { "MODE_" }, value = {
240             MODE_NIGHT_AUTO,
241             MODE_NIGHT_CUSTOM,
242             MODE_NIGHT_NO,
243             MODE_NIGHT_YES
244     })
245     @Retention(RetentionPolicy.SOURCE)
246     public @interface NightMode {}
247 
248     /**
249      * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
250      * automatically switch night mode on and off based on the time.
251      */
252     public static final int MODE_NIGHT_AUTO = 0;
253 
254     /**
255      * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
256      * automatically switch night mode on and off based on the time.
257      */
258     public static final int MODE_NIGHT_CUSTOM = 3;
259 
260     /**
261      * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
262      * never run in night mode.
263      */
264     public static final int MODE_NIGHT_NO = 1;
265 
266     /**
267      * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
268      * always run in night mode.
269      */
270     public static final int MODE_NIGHT_YES = 2;
271 
272     /** @hide */
273     @IntDef(prefix = { "MODE_ATTENTION_THEME_OVERLAY_" }, value = {
274             MODE_ATTENTION_THEME_OVERLAY_OFF,
275             MODE_ATTENTION_THEME_OVERLAY_NIGHT,
276             MODE_ATTENTION_THEME_OVERLAY_DAY
277     })
278     @Retention(RetentionPolicy.SOURCE)
279     public @interface AttentionModeThemeOverlayType {}
280 
281     /** @hide */
282     @IntDef(prefix = { "MODE_ATTENTION_THEME_OVERLAY_" }, value = {
283             MODE_ATTENTION_THEME_OVERLAY_OFF,
284             MODE_ATTENTION_THEME_OVERLAY_NIGHT,
285             MODE_ATTENTION_THEME_OVERLAY_DAY,
286             MODE_ATTENTION_THEME_OVERLAY_UNKNOWN
287     })
288     @Retention(RetentionPolicy.SOURCE)
289     public @interface AttentionModeThemeOverlayReturnType {}
290 
291     /**
292      * Constant for {@link #setAttentionModeThemeOverlay(int)} (int)} and {@link
293      * #getAttentionModeThemeOverlay()}: Keeps night mode as set by {@link #setNightMode(int)}.
294      * @hide
295      */
296     @FlaggedApi(Flags.FLAG_MODES_API)
297     @TestApi
298     public static final int MODE_ATTENTION_THEME_OVERLAY_OFF = 1000;
299 
300     /**
301      * Constant for {@link #setAttentionModeThemeOverlay(int)} (int)} and {@link
302      * #getAttentionModeThemeOverlay()}: Maintains night mode always on.
303      * @hide
304      */
305     @FlaggedApi(Flags.FLAG_MODES_API)
306     @TestApi
307     public static final int MODE_ATTENTION_THEME_OVERLAY_NIGHT = 1001;
308 
309     /**
310      * Constant for {@link #setAttentionModeThemeOverlay(int)} (int)} and {@link
311      * #getAttentionModeThemeOverlay()}: Maintains night mode always off (Light).
312      * @hide
313      */
314     @FlaggedApi(Flags.FLAG_MODES_API)
315     @TestApi
316     public static final int MODE_ATTENTION_THEME_OVERLAY_DAY = 1002;
317 
318     /**
319      * Constant for {@link #getAttentionModeThemeOverlay()}: Error communication with server.
320      * @hide
321      */
322     @FlaggedApi(Flags.FLAG_MODES_API)
323     @TestApi
324     public static final int MODE_ATTENTION_THEME_OVERLAY_UNKNOWN = -1;
325 
326     /**
327      * Granular types for {@link #setNightModeCustomType(int)}
328      * @hide
329      */
330     @IntDef(prefix = { "MODE_NIGHT_CUSTOM_TYPE_" }, value = {
331             MODE_NIGHT_CUSTOM_TYPE_SCHEDULE,
332             MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
333     })
334     @Retention(RetentionPolicy.SOURCE)
335     public @interface NightModeCustomType {}
336 
337     /**
338      * Granular types for {@link #getNightModeCustomType()}
339      * @hide
340      */
341     @IntDef(prefix = { "MODE_NIGHT_CUSTOM_TYPE_" }, value = {
342             MODE_NIGHT_CUSTOM_TYPE_UNKNOWN,
343             MODE_NIGHT_CUSTOM_TYPE_SCHEDULE,
344             MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
345     })
346     @Retention(RetentionPolicy.SOURCE)
347     public @interface NightModeCustomReturnType {}
348 
349     /**
350      * A granular type for {@link #MODE_NIGHT_CUSTOM} which is unknown.
351      * <p>
352      * This is the default value when the night mode is set to value other than
353      * {@link #MODE_NIGHT_CUSTOM}.
354      * @hide
355      */
356     @SystemApi
357     public static final int MODE_NIGHT_CUSTOM_TYPE_UNKNOWN = -1;
358 
359     /**
360      * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on a custom schedule.
361      * <p>
362      * This is the default value when night mode is set to {@link #MODE_NIGHT_CUSTOM} unless the
363      * the night mode custom type is specified by calling {@link #setNightModeCustomType(int)}.
364      * @hide
365      */
366     @SystemApi
367     public static final int MODE_NIGHT_CUSTOM_TYPE_SCHEDULE = 0;
368 
369     /**
370      * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on the bedtime schedule.
371      * @hide
372      */
373     @SystemApi
374     public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1;
375 
376     private static Globals sGlobals;
377 
378     /**
379      * Context required for getting the opPackageName of API caller; maybe be {@code null} if the
380      * old constructor marked with UnSupportedAppUsage is used.
381      */
382     private @Nullable Context mContext;
383 
384     private final Object mLock = new Object();
385     /**
386      * Map that stores internally created {@link InnerListener} objects keyed by their corresponding
387      * externally provided callback objects.
388      */
389     @GuardedBy("mLock")
390     private final Map<OnProjectionStateChangedListener, InnerListener>
391             mProjectionStateListenerMap = new ArrayMap<>();
392 
393     /**
394      * Resource manager that prevents memory leakage of Contexts via binder objects if clients
395      * fail to remove listeners.
396      */
397     @GuardedBy("mLock")
398     private final OnProjectionStateChangedListenerResourceManager
399             mOnProjectionStateChangedListenerResourceManager =
400             new OnProjectionStateChangedListenerResourceManager();
401 
402     private static class Globals extends IUiModeManagerCallback.Stub {
403 
404         private final IUiModeManager mService;
405         private final Object mGlobalsLock = new Object();
406 
407         private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE;
408 
409         /**
410          * Map that stores user provided {@link ContrastChangeListener} callbacks,
411          * and the executors on which these callbacks should be called.
412          */
413         private final ArrayMap<ContrastChangeListener, Executor>
414                 mContrastChangeListeners = new ArrayMap<>();
415 
Globals(IUiModeManager service)416         Globals(IUiModeManager service) {
417             mService = service;
418             try {
419                 mService.addCallback(this);
420                 mContrast = mService.getContrast();
421             } catch (RemoteException e) {
422                 Log.e(TAG, "Setup failed: UiModeManagerService is dead", e);
423             }
424         }
425 
getContrast()426         private float getContrast() {
427             synchronized (mGlobalsLock) {
428                 return mContrast;
429             }
430         }
431 
addContrastChangeListener(ContrastChangeListener listener, Executor executor)432         private void addContrastChangeListener(ContrastChangeListener listener, Executor executor) {
433             synchronized (mGlobalsLock) {
434                 mContrastChangeListeners.put(listener, executor);
435             }
436         }
437 
removeContrastChangeListener(ContrastChangeListener listener)438         private void removeContrastChangeListener(ContrastChangeListener listener) {
439             synchronized (mGlobalsLock) {
440                 mContrastChangeListeners.remove(listener);
441             }
442         }
443 
444         @Override
notifyContrastChanged(float contrast)445         public void notifyContrastChanged(float contrast) {
446             synchronized (mGlobalsLock) {
447                 // if value changed in the settings, update the cached value and notify listeners
448                 if (Math.abs(mContrast - contrast) < 1e-10) return;
449                 mContrast = contrast;
450                 mContrastChangeListeners.forEach((listener, executor) -> executor.execute(
451                         () -> listener.onContrastChanged(contrast)));
452             }
453         }
454     }
455 
456     /**
457      * Define constants and conversions between {@link ContrastLevel}s and contrast values.
458      * <p>
459      * Contrast values are floats defined in [-1, 1], as defined in {@link #getContrast}.
460      * This is the official data type for contrast;
461      * all methods from the public API return contrast values.
462      * </p>
463      * <p>
464      * {@code ContrastLevel}, on the other hand, is an internal-only enumeration of contrasts that
465      * can be set from the system ui. Each {@code ContrastLevel} has an associated contrast value.
466      * </p>
467      * <p>
468      * Currently, a user chan chose from three contrast levels:
469      * <ul>
470      *     <li>{@link #CONTRAST_LEVEL_STANDARD}, corresponding to the default contrast value 0f</li>
471      *     <li>{@link #CONTRAST_LEVEL_MEDIUM}, corresponding to the contrast value 0.5f</li>
472      *     <li>{@link #CONTRAST_LEVEL_HIGH}, corresponding to the maximum contrast value 1f</li>
473      * </ul>
474      * </p>
475      *
476      * @hide
477      */
478     public static class ContrastUtils {
479 
480         private static final float CONTRAST_MIN_VALUE = -1f;
481         private static final float CONTRAST_MAX_VALUE = 1f;
482         public static final float CONTRAST_DEFAULT_VALUE = 0f;
483 
484         @IntDef(flag = true, prefix = { "CONTRAST_LEVEL_" }, value = {
485                 CONTRAST_LEVEL_STANDARD,
486                 CONTRAST_LEVEL_MEDIUM,
487                 CONTRAST_LEVEL_HIGH
488         })
489         @Retention(RetentionPolicy.SOURCE)
490         public @interface ContrastLevel {}
491 
492         public static final int CONTRAST_LEVEL_STANDARD = 0;
493         public static final int CONTRAST_LEVEL_MEDIUM = 1;
494         public static final int CONTRAST_LEVEL_HIGH = 2;
495 
allContrastLevels()496         private static Stream<Integer> allContrastLevels() {
497             return Stream.of(CONTRAST_LEVEL_STANDARD, CONTRAST_LEVEL_MEDIUM, CONTRAST_LEVEL_HIGH);
498         }
499 
500         /**
501          * Convert a contrast value in [-1, 1] to its associated {@link ContrastLevel}
502          */
toContrastLevel(float contrast)503         public static @ContrastLevel int toContrastLevel(float contrast) {
504             if (contrast < CONTRAST_MIN_VALUE || contrast > CONTRAST_MAX_VALUE) {
505                 throw new IllegalArgumentException("contrast values should be in [-1, 1]");
506             }
507             return allContrastLevels().min(Comparator.comparingDouble(contrastLevel ->
508                     Math.abs(contrastLevel - 2 * contrast))).orElseThrow();
509         }
510 
511         /**
512          * Convert a {@link ContrastLevel} to its associated contrast value in [-1, 1]
513          */
fromContrastLevel(@ontrastLevel int contrastLevel)514         public static float fromContrastLevel(@ContrastLevel int contrastLevel) {
515             if (allContrastLevels().noneMatch(level -> level == contrastLevel)) {
516                 throw new IllegalArgumentException("unrecognized contrast level: " + contrastLevel);
517             }
518             return contrastLevel / 2f;
519         }
520     }
521 
522     @UnsupportedAppUsage
UiModeManager()523     /*package*/ UiModeManager() throws ServiceNotFoundException {
524         this(null /* context */);
525     }
526 
UiModeManager(Context context)527     /*package*/ UiModeManager(Context context) throws ServiceNotFoundException {
528         IUiModeManager service = IUiModeManager.Stub.asInterface(
529                 ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE));
530         mContext = context;
531         if (service == null) return;
532         synchronized (mLock) {
533             if (sGlobals == null) sGlobals = new Globals(service);
534         }
535     }
536 
537     /**
538      * Flag for use with {@link #enableCarMode(int)}: go to the car
539      * home activity as part of the enable.  Enabling this way ensures
540      * a clean transition between the current activity (in non-car-mode) and
541      * the car home activity that will serve as home while in car mode.  This
542      * will switch to the car home activity even if we are already in car mode.
543      */
544     public static final int ENABLE_CAR_MODE_GO_CAR_HOME = 0x0001;
545 
546     /**
547      * Flag for use with {@link #enableCarMode(int)}: allow sleep mode while in car mode.
548      * By default, when this flag is not set, the system may hold a full wake lock to keep the
549      * screen turned on and prevent the system from entering sleep mode while in car mode.
550      * Setting this flag disables such behavior and the system may enter sleep mode
551      * if there is no other user activity and no other wake lock held.
552      * Setting this flag can be relevant for a car dock application that does not require the
553      * screen kept on.
554      */
555     public static final int ENABLE_CAR_MODE_ALLOW_SLEEP = 0x0002;
556 
557     /** @hide */
558     @IntDef(prefix = {"ENABLE_CAR_MODE_"}, value = {
559             ENABLE_CAR_MODE_GO_CAR_HOME,
560             ENABLE_CAR_MODE_ALLOW_SLEEP
561     })
562     @Retention(RetentionPolicy.SOURCE)
563     public @interface EnableCarMode {}
564 
565     /**
566      * Force device into car mode, like it had been placed in the car dock.
567      * This will cause the device to switch to the car home UI as part of
568      * the mode switch.
569      * @param flags Must be 0.
570      */
enableCarMode(int flags)571     public void enableCarMode(int flags) {
572         enableCarMode(DEFAULT_PRIORITY, flags);
573     }
574 
575     /**
576      * Force device into car mode, like it had been placed in the car dock.  This will cause the
577      * device to switch to the car home UI as part of the mode switch.
578      * <p>
579      * An app may request to enter car mode when the system is already in car mode.  The app may
580      * specify a "priority" when entering car mode.  The device will remain in car mode
581      * (i.e. {@link #getCurrentModeType()} is {@link Configuration#UI_MODE_TYPE_CAR}) as long as
582      * there is a priority level at which car mode have been enabled.
583      * <p>
584      * Specifying a priority level when entering car mode is important in cases where multiple apps
585      * on a device implement a car-mode {@link android.telecom.InCallService} (see
586      * {@link android.telecom.TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI}).  The
587      * {@link android.telecom.InCallService} associated with the highest priority app which entered
588      * car mode will be bound to by Telecom and provided with information about ongoing calls on
589      * the device.
590      * <p>
591      * System apps holding the required permission can enable car mode when the app determines the
592      * correct conditions exist for that app to be in car mode.  The device maker should ensure that
593      * where multiple apps exist on the device which can potentially enter car mode, appropriate
594      * priorities are used to ensure that calls delivered by the
595      * {@link android.telecom.InCallService} API are sent to the highest priority app given the
596      * desired behavior of the car mode experience on the device.
597      * <p>
598      * If app A and app B both meet their own criteria to enable car mode, and it is desired that
599      * app B should be the one which should receive call information in that scenario, the priority
600      * for app B should be higher than the one for app A.  The higher priority of app B compared to
601      * A means it will be bound to during calls and app A will not.  When app B no longer meets its
602      * criteria for providing a car mode experience it uses {@link #disableCarMode(int)} to disable
603      * car mode at its priority level.  The system will then unbind from app B and bind to app A as
604      * it has the next highest priority.
605      * <p>
606      * When an app enables car mode at a certain priority, it can disable car mode at the specified
607      * priority level using {@link #disableCarMode(int)}.  An app may only enable car mode at a
608      * single priority.
609      * <p>
610      * Public apps are assumed to enter/exit car mode at the lowest priority,
611      * {@link #DEFAULT_PRIORITY}.
612      *
613      * @param priority The declared priority for the caller, where {@link #DEFAULT_PRIORITY} (0) is
614      *                 the lowest priority and higher numbers represent a higher priority.
615      *                 The priorities apps declare when entering car mode is determined by the
616      *                 device manufacturer based on the desired car mode experience.
617      * @param flags Car mode flags.
618      * @hide
619      */
620     @SystemApi
621     @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED)
enableCarMode(@ntRangefrom = 0) int priority, @EnableCarMode int flags)622     public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) {
623         if (sGlobals != null) {
624             try {
625                 sGlobals.mService.enableCarMode(flags, priority,
626                         mContext == null ? null : mContext.getOpPackageName());
627             } catch (RemoteException e) {
628                 throw e.rethrowFromSystemServer();
629             }
630         }
631     }
632 
633     /**
634      * Flag for use with {@link #disableCarMode(int)}: go to the normal
635      * home activity as part of the disable.  Disabling this way ensures
636      * a clean transition between the current activity (in car mode) and
637      * the original home activity (which was typically last running without
638      * being in car mode).
639      */
640     public static final int DISABLE_CAR_MODE_GO_HOME = 0x0001;
641 
642     /**
643      * Flag for use with {@link #disableCarMode(int)}: Disables car mode at ALL priority levels.
644      * Primarily intended for use from {@link com.android.internal.app.DisableCarModeActivity} to
645      * provide the user with a means to exit car mode at all priority levels.
646      * @hide
647      */
648     public static final int DISABLE_CAR_MODE_ALL_PRIORITIES = 0x0002;
649 
650     /** @hide */
651     @IntDef(prefix = { "DISABLE_CAR_MODE_" }, value = {
652             DISABLE_CAR_MODE_GO_HOME
653     })
654     @Retention(RetentionPolicy.SOURCE)
655     public @interface DisableCarMode {}
656 
657     /**
658      * The default priority used for entering car mode.
659      * <p>
660      * Callers of the {@link #enableCarMode(int)} priority will be assigned the default priority.
661      * This is considered the lowest possible priority for enabling car mode.
662      * <p>
663      * System apps can specify a priority other than the default priority when using
664      * {@link #enableCarMode(int, int)} to enable car mode.
665      * @hide
666      */
667     @SystemApi
668     public static final int DEFAULT_PRIORITY = 0;
669 
670     /**
671      * Turn off special mode if currently in car mode.
672      * @param flags One of the disable car mode flags.
673      */
disableCarMode(@isableCarMode int flags)674     public void disableCarMode(@DisableCarMode int flags) {
675         if (sGlobals != null) {
676             try {
677                 sGlobals.mService.disableCarModeByCallingPackage(flags,
678                         mContext == null ? null : mContext.getOpPackageName());
679             } catch (RemoteException e) {
680                 throw e.rethrowFromSystemServer();
681             }
682         }
683     }
684 
685     /**
686      * Return the current running mode type.  May be one of
687      * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL},
688      * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK},
689      * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR},
690      * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TELEVISION},
691      * {@link Configuration#UI_MODE_TYPE_APPLIANCE Configuration.UI_MODE_TYPE_APPLIANCE},
692      * {@link Configuration#UI_MODE_TYPE_WATCH Configuration.UI_MODE_TYPE_WATCH}, or
693      * {@link Configuration#UI_MODE_TYPE_VR_HEADSET Configuration.UI_MODE_TYPE_VR_HEADSET}.
694      */
getCurrentModeType()695     public int getCurrentModeType() {
696         if (sGlobals != null) {
697             try {
698                 return sGlobals.mService.getCurrentModeType();
699             } catch (RemoteException e) {
700                 throw e.rethrowFromSystemServer();
701             }
702         }
703         return Configuration.UI_MODE_TYPE_NORMAL;
704     }
705 
706     /**
707      * Sets the system-wide night mode.
708      * <p>
709      * The mode can be one of:
710      * <ul>
711      *   <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
712      *       {@code notnight} mode</li>
713      *   <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
714      *       {@code night} mode</li>
715      *   <li><em>{@link #MODE_NIGHT_CUSTOM}</em> automatically switches between
716      *       {@code night} and {@code notnight} based on the custom time set (or default)</li>
717      *   <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
718      *       {@code night} and {@code notnight} based on the device's current
719      *       location and certain other sensors</li>
720      * </ul>
721      * <p>
722      * <strong>Note:</strong> On API 22 and below, changes to the night mode
723      * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
724      * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
725      * device. On API 23 through API 28, changes to night mode are always effective.
726      * <p>
727      * Starting in API 29, when the device is in car mode and this method is called, night mode
728      * will change, but the new setting is not persisted and the previously persisted setting
729      * will be restored when the device exits car mode.
730      * <p>
731      * Changes to night mode take effect globally and will result in a configuration change
732      * (and potentially an Activity lifecycle event) being applied to all running apps.
733      * Developers interested in an app-local implementation of night mode should consider using
734      * {@link #setApplicationNightMode(int)} to set and persist the -night qualifier locally or
735      * {@link androidx.appcompat.app.AppCompatDelegate#setDefaultNightMode(int)} for the
736      * backward compatible implementation.
737      *
738      * @param mode the night mode to set
739      * @see #getNightMode()
740      * @see #setApplicationNightMode(int)
741      */
setNightMode(@ightMode int mode)742     public void setNightMode(@NightMode int mode) {
743         if (sGlobals != null) {
744             try {
745                 sGlobals.mService.setNightMode(mode);
746             } catch (RemoteException e) {
747                 throw e.rethrowFromSystemServer();
748             }
749         }
750     }
751 
752     /**
753      * Sets the current night mode to {@link #MODE_NIGHT_CUSTOM} with the custom night mode type
754      * {@code nightModeCustomType}.
755      *
756      * @param nightModeCustomType
757      * @throws IllegalArgumentException if passed an unsupported type to
758      *         {@code nightModeCustomType}.
759      * @hide
760      */
761     @SystemApi
762     @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
setNightModeCustomType(@ightModeCustomType int nightModeCustomType)763     public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) {
764         if (sGlobals != null) {
765             try {
766                 sGlobals.mService.setNightModeCustomType(nightModeCustomType);
767             } catch (RemoteException e) {
768                 throw e.rethrowFromSystemServer();
769             }
770         }
771     }
772 
773     /**
774      * Returns the custom night mode type.
775      * <p>
776      * If the current night mode is not {@link #MODE_NIGHT_CUSTOM}, returns
777      * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}.
778      * @hide
779      */
780     @SystemApi
781     @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
getNightModeCustomType()782     public @NightModeCustomReturnType int getNightModeCustomType() {
783         if (sGlobals != null) {
784             try {
785                 return sGlobals.mService.getNightModeCustomType();
786             } catch (RemoteException e) {
787                 throw e.rethrowFromSystemServer();
788             }
789         }
790         return MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
791     }
792 
793     /**
794      * Overlays current Attention mode Night Mode overlay.
795      * {@code attentionModeThemeOverlayType}.
796      *
797      * @throws IllegalArgumentException if passed an unsupported type to
798      *                                  {@code AttentionModeThemeOverlayType}.
799      * @hide
800      */
801     @FlaggedApi(Flags.FLAG_MODES_API)
802     @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
setAttentionModeThemeOverlay( @ttentionModeThemeOverlayType int attentionModeThemeOverlayType)803     public void setAttentionModeThemeOverlay(
804             @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
805         if (sGlobals != null) {
806             try {
807                 sGlobals.mService.setAttentionModeThemeOverlay(attentionModeThemeOverlayType);
808             } catch (RemoteException e) {
809                 throw e.rethrowFromSystemServer();
810             }
811         }
812     }
813 
814     /**
815      * Returns the currently configured Attention Mode theme overlay.
816      * <p>
817      * May be one of:
818      *   <ul>
819      *     <li>{@link #MODE_ATTENTION_THEME_OVERLAY_OFF}</li>
820      *     <li>{@link #MODE_ATTENTION_THEME_OVERLAY_NIGHT}</li>
821      *     <li>{@link #MODE_ATTENTION_THEME_OVERLAY_DAY}</li>
822      *     <li>{@link #MODE_ATTENTION_THEME_OVERLAY_UNKNOWN}</li>
823      *   </ul>
824      * </p>
825      *
826      * @hide
827      */
828     @FlaggedApi(Flags.FLAG_MODES_API)
829     @TestApi
830     @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
getAttentionModeThemeOverlay()831     public @AttentionModeThemeOverlayReturnType int getAttentionModeThemeOverlay() {
832         if (sGlobals != null) {
833             try {
834                 return sGlobals.mService.getAttentionModeThemeOverlay();
835             } catch (RemoteException e) {
836                 throw e.rethrowFromSystemServer();
837             }
838         }
839         return MODE_ATTENTION_THEME_OVERLAY_UNKNOWN;
840     }
841 
842     /**
843      * Sets and persist the night mode for this application.
844      * <p>
845      * The mode can be one of:
846      * <ul>
847      *   <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
848      *       {@code notnight} mode</li>
849      *   <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
850      *       {@code night} mode</li>
851      *   <li><em>{@link #MODE_NIGHT_CUSTOM}</em> automatically switches between
852      *       {@code night} and {@code notnight} based on the custom time set (or default)</li>
853      *   <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
854      *       {@code night} and {@code notnight} based on the device's current
855      *       location and certain other sensors</li>
856      * </ul>
857      * <p>
858      * Changes to night mode take effect locally and will result in a configuration change
859      * (and potentially an Activity lifecycle event) being applied to this application. The mode
860      * is persisted for this application until it is either modified by the application, the
861      * user clears the data for the application, or this application is uninstalled.
862      * <p>
863      * Developers interested in a non-persistent app-local implementation of night mode should
864      * consider using {@link androidx.appcompat.app.AppCompatDelegate#setDefaultNightMode(int)}
865      * to manage the -night qualifier locally.
866      *
867      * @param mode the night mode to set
868      * @see #setNightMode(int)
869      */
setApplicationNightMode(@ightMode int mode)870     public void setApplicationNightMode(@NightMode int mode) {
871         if (sGlobals != null) {
872             try {
873                 sGlobals.mService.setApplicationNightMode(mode);
874             } catch (RemoteException e) {
875                 throw e.rethrowFromSystemServer();
876             }
877         }
878     }
879 
getNightModeFromServer()880     private Integer getNightModeFromServer() {
881         try {
882             if (sGlobals != null) {
883                 return sGlobals.mService.getNightMode();
884             }
885             return -1;
886         } catch (RemoteException e) {
887             throw e.rethrowFromSystemServer();
888         }
889     }
890 
891 
892     /**
893      * Retrieve the night mode for the user.
894      */
895     private final IpcDataCache.QueryHandler<Void, Integer> mNightModeQuery =
896             new IpcDataCache.QueryHandler<>() {
897 
898                 @Override
899                 @NonNull
900                 public Integer apply(Void query) {
901                     return getNightModeFromServer();
902                 }
903             };
904 
905     private static final String NIGHT_MODE_API = "getNightMode";
906 
907     /**
908      * Cache the night mode for a user.
909      */
910     private final IpcDataCache<Void, Integer> mNightModeCache =
911             new IpcDataCache<>(1, IpcDataCache.MODULE_SYSTEM,
912                     NIGHT_MODE_API, /* cacheName= */ "NightModeCache", mNightModeQuery);
913 
914     /**
915      * Invalidate the night mode cache.
916      *
917      * @hide
918      */
919     @FlaggedApi(Flags.FLAG_ENABLE_NIGHT_MODE_BINDER_CACHE)
invalidateNightModeCache()920     public static void invalidateNightModeCache() {
921         IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM,
922                 NIGHT_MODE_API);
923     }
924 
925     /**
926      * Returns the currently configured night mode.
927      * <p>
928      * May be one of:
929      * <ul>
930      *   <li>{@link #MODE_NIGHT_NO}</li>
931      *   <li>{@link #MODE_NIGHT_YES}</li>
932      *   <li>{@link #MODE_NIGHT_AUTO}</li>
933      *   <li>{@link #MODE_NIGHT_CUSTOM}</li>
934      *   <li>{@code -1} on error</li>
935      * </ul>
936      *
937      * @return the current night mode, or {@code -1} on error
938      * @see #setNightMode(int)
939      */
getNightMode()940     public @NightMode int getNightMode() {
941         if (enableNightModeBinderCache()) {
942             return mNightModeCache.query(null);
943         } else {
944             return getNightModeFromServer();
945         }
946     }
947 
948     /**
949      * @return If UI mode is locked or not. When UI mode is locked, calls to change UI mode
950      *         like {@link #enableCarMode(int)} will silently fail.
951      * @hide
952      */
953     @TestApi
isUiModeLocked()954     public boolean isUiModeLocked() {
955         if (sGlobals != null) {
956             try {
957                 return sGlobals.mService.isUiModeLocked();
958             } catch (RemoteException e) {
959                 throw e.rethrowFromSystemServer();
960             }
961         }
962         return true;
963     }
964 
965     /**
966      * Returns whether night mode is locked or not.
967      * <p>
968      * When night mode is locked, only privileged system components may change
969      * night mode and calls from non-privileged applications to change night
970      * mode will fail silently.
971      *
972      * @return {@code true} if night mode is locked or {@code false} otherwise
973      * @hide
974      */
975     @TestApi
isNightModeLocked()976     public boolean isNightModeLocked() {
977         if (sGlobals != null) {
978             try {
979                 return sGlobals.mService.isNightModeLocked();
980             } catch (RemoteException e) {
981                 throw e.rethrowFromSystemServer();
982             }
983         }
984         return true;
985     }
986 
987     /**
988      * [De]activating night mode for the current user if the current night mode is custom and the
989      * custom type matches {@code nightModeCustomType}.
990      *
991      * @param nightModeCustomType the specify type of custom mode
992      * @param active {@code true} to activate night mode. Otherwise, deactivate night mode
993      * @return {@code true} if night mode has successfully activated for the requested
994      *         {@code nightModeCustomType}.
995      * @hide
996      */
997     @SystemApi
998     @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
setNightModeActivatedForCustomMode(@ightModeCustomType int nightModeCustomType, boolean active)999     public boolean setNightModeActivatedForCustomMode(@NightModeCustomType int nightModeCustomType,
1000             boolean active) {
1001         if (sGlobals != null) {
1002             try {
1003                 return sGlobals.mService.setNightModeActivatedForCustomMode(
1004                         nightModeCustomType, active);
1005             } catch (RemoteException e) {
1006                 throw e.rethrowFromSystemServer();
1007             }
1008         }
1009         return false;
1010     }
1011 
1012     /**
1013      * Activating night mode for the current user
1014      *
1015      * @return {@code true} if the change is successful
1016      * @hide
1017      */
1018     @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
setNightModeActivated(boolean active)1019     public boolean setNightModeActivated(boolean active) {
1020         if (sGlobals != null) {
1021             try {
1022                 return sGlobals.mService.setNightModeActivated(active);
1023             } catch (RemoteException e) {
1024                 throw e.rethrowFromSystemServer();
1025             }
1026         }
1027         return false;
1028     }
1029 
1030     /**
1031      * Returns the time of the day Dark theme activates
1032      * <p>
1033      * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
1034      * this time set to activate it automatically.
1035      */
1036     @NonNull
getCustomNightModeStart()1037     public LocalTime getCustomNightModeStart() {
1038         if (sGlobals != null) {
1039             try {
1040                 return LocalTime.ofNanoOfDay(sGlobals.mService.getCustomNightModeStart() * 1000);
1041             } catch (RemoteException e) {
1042                 throw e.rethrowFromSystemServer();
1043             }
1044         }
1045         return LocalTime.MIDNIGHT;
1046     }
1047 
1048     /**
1049      * Sets the time of the day Dark theme activates
1050      * <p>
1051      * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
1052      * this time set to activate it automatically
1053      * @param time The time of the day Dark theme should activate
1054      */
setCustomNightModeStart(@onNull LocalTime time)1055     public void setCustomNightModeStart(@NonNull LocalTime time) {
1056         if (sGlobals != null) {
1057             try {
1058                 sGlobals.mService.setCustomNightModeStart(time.toNanoOfDay() / 1000);
1059             } catch (RemoteException e) {
1060                 throw e.rethrowFromSystemServer();
1061             }
1062         }
1063     }
1064 
1065     /**
1066      * Returns the time of the day Dark theme deactivates
1067      * <p>
1068      * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
1069      * this time set to deactivate it automatically.
1070      */
1071     @NonNull
getCustomNightModeEnd()1072     public LocalTime getCustomNightModeEnd() {
1073         if (sGlobals != null) {
1074             try {
1075                 return LocalTime.ofNanoOfDay(sGlobals.mService.getCustomNightModeEnd() * 1000);
1076             } catch (RemoteException e) {
1077                 throw e.rethrowFromSystemServer();
1078             }
1079         }
1080         return LocalTime.MIDNIGHT;
1081     }
1082 
1083     /**
1084      * Sets the time of the day Dark theme deactivates
1085      * <p>
1086      * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
1087      * this time set to deactivate it automatically.
1088      * @param time The time of the day Dark theme should deactivate
1089      */
setCustomNightModeEnd(@onNull LocalTime time)1090     public void setCustomNightModeEnd(@NonNull LocalTime time) {
1091         if (sGlobals != null) {
1092             try {
1093                 sGlobals.mService.setCustomNightModeEnd(time.toNanoOfDay() / 1000);
1094             } catch (RemoteException e) {
1095                 throw e.rethrowFromSystemServer();
1096             }
1097         }
1098     }
1099 
1100     /**
1101      * Indicates no projection type. Can be used to compare with the {@link ProjectionType} in
1102      * {@link OnProjectionStateChangedListener#onProjectionStateChanged(int, Set)}.
1103      *
1104      * @hide
1105      */
1106     @SystemApi
1107     @TestApi
1108     public static final int PROJECTION_TYPE_NONE = 0x0000;
1109     /**
1110      * Automotive projection prevents degradation of GPS to save battery, routes incoming calls to
1111      * the automotive role holder, etc. For use with {@link #requestProjection(int)} and
1112      * {@link #clearProjectionState(int)}.
1113      *
1114      * @hide
1115      */
1116     @SystemApi
1117     @TestApi
1118     public static final int PROJECTION_TYPE_AUTOMOTIVE = 0x0001;
1119     /**
1120      * Indicates all projection types. For use with
1121      * {@link #addOnProjectionStateChangedListener(int, Executor, OnProjectionStateChangedListener)}
1122      * and {@link #getProjectingPackages(int)}.
1123      *
1124      * @hide
1125      */
1126     @SystemApi
1127     @TestApi
1128     public static final int PROJECTION_TYPE_ALL = -1;  // All bits on
1129 
1130     /** @hide */
1131     @IntDef(prefix = {"PROJECTION_TYPE_"}, value = {
1132             PROJECTION_TYPE_NONE,
1133             PROJECTION_TYPE_AUTOMOTIVE,
1134             PROJECTION_TYPE_ALL,
1135     })
1136     @Retention(RetentionPolicy.SOURCE)
1137     public @interface ProjectionType {
1138     }
1139 
1140     /**
1141      * Sets the given {@link ProjectionType}.
1142      *
1143      * Caller must have {@link android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION} if
1144      * argument is {@link #PROJECTION_TYPE_AUTOMOTIVE}.
1145      * @param projectionType the type of projection to request. This must be a single
1146      * {@link ProjectionType} and cannot be a bitmask.
1147      * @return true if the projection was successfully set
1148      * @throws IllegalArgumentException if passed {@link #PROJECTION_TYPE_NONE},
1149      * {@link #PROJECTION_TYPE_ALL}, or any combination of more than one {@link ProjectionType}.
1150      *
1151      * @hide
1152      */
1153     @SystemApi
1154     @TestApi
1155     @RequiresPermission(value = android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION,
1156             conditional = true)
requestProjection(@rojectionType int projectionType)1157     public boolean requestProjection(@ProjectionType int projectionType) {
1158         if (sGlobals != null) {
1159             try {
1160                 return sGlobals.mService.requestProjection(new Binder(), projectionType,
1161                         mContext.getOpPackageName());
1162             } catch (RemoteException e) {
1163                 throw e.rethrowFromSystemServer();
1164             }
1165         }
1166         return false;
1167     }
1168 
1169     /**
1170      * Releases the given {@link ProjectionType}.
1171      *
1172      * Caller must have {@link android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION} if
1173      * argument is {@link #PROJECTION_TYPE_AUTOMOTIVE}.
1174      * @param projectionType the type of projection to release. This must be a single
1175      * {@link ProjectionType} and cannot be a bitmask.
1176      * @return true if the package had set projection and it was successfully released
1177      * @throws IllegalArgumentException if passed {@link #PROJECTION_TYPE_NONE},
1178      * {@link #PROJECTION_TYPE_ALL}, or any combination of more than one {@link ProjectionType}.
1179      *
1180      * @hide
1181      */
1182     @SystemApi
1183     @TestApi
1184     @RequiresPermission(value = android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION,
1185             conditional = true)
releaseProjection(@rojectionType int projectionType)1186     public boolean releaseProjection(@ProjectionType int projectionType) {
1187         if (sGlobals != null) {
1188             try {
1189                 return sGlobals.mService.releaseProjection(
1190                         projectionType, mContext.getOpPackageName());
1191             } catch (RemoteException e) {
1192                 throw e.rethrowFromSystemServer();
1193             }
1194         }
1195         return false;
1196     }
1197 
1198     /**
1199      * Gets the packages that are currently projecting.
1200      *
1201      * @param projectionType the {@link ProjectionType}s to consider when computing which packages
1202      *                       are projecting. Use {@link #PROJECTION_TYPE_ALL} to get all projecting
1203      *                       packages.
1204      *
1205      * @hide
1206      */
1207     @SystemApi
1208     @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE)
1209     @NonNull
getProjectingPackages(@rojectionType int projectionType)1210     public Set<String> getProjectingPackages(@ProjectionType int projectionType) {
1211         if (sGlobals != null) {
1212             try {
1213                 return new ArraySet<>(sGlobals.mService.getProjectingPackages(projectionType));
1214             } catch (RemoteException e) {
1215                 throw e.rethrowFromSystemServer();
1216             }
1217         }
1218         return Set.of();
1219     }
1220 
1221     /**
1222      * Gets the {@link ProjectionType}s that are currently active.
1223      *
1224      * @hide
1225      */
1226     @SystemApi
1227     @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE)
getActiveProjectionTypes()1228     public @ProjectionType int getActiveProjectionTypes() {
1229         if (sGlobals != null) {
1230             try {
1231                 return sGlobals.mService.getActiveProjectionTypes();
1232             } catch (RemoteException e) {
1233                 throw e.rethrowFromSystemServer();
1234             }
1235         }
1236         return PROJECTION_TYPE_NONE;
1237     }
1238 
1239     /**
1240      * Configures the listener to receive callbacks when the packages projecting using the given
1241      * {@link ProjectionType}s change.
1242      *
1243      * @param projectionType one or more {@link ProjectionType}s to listen for changes regarding
1244      * @param executor an {@link Executor} on which to invoke the callbacks
1245      * @param listener the {@link OnProjectionStateChangedListener} to add
1246      *
1247      * @hide
1248      */
1249     @SystemApi
1250     @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE)
addOnProjectionStateChangedListener(@rojectionType int projectionType, @NonNull @CallbackExecutor Executor executor, @NonNull OnProjectionStateChangedListener listener)1251     public void addOnProjectionStateChangedListener(@ProjectionType int projectionType,
1252             @NonNull @CallbackExecutor Executor executor,
1253             @NonNull OnProjectionStateChangedListener listener) {
1254         synchronized (mLock) {
1255             if (mProjectionStateListenerMap.containsKey(listener)) {
1256                 Slog.i(TAG, "Attempted to add listener that was already added.");
1257                 return;
1258             }
1259             if (sGlobals != null) {
1260                 InnerListener innerListener = new InnerListener(executor, listener,
1261                         mOnProjectionStateChangedListenerResourceManager);
1262                 try {
1263                     sGlobals.mService.addOnProjectionStateChangedListener(
1264                             innerListener, projectionType);
1265                     mProjectionStateListenerMap.put(listener, innerListener);
1266                 } catch (RemoteException e) {
1267                     mOnProjectionStateChangedListenerResourceManager.remove(innerListener);
1268                     throw e.rethrowFromSystemServer();
1269                 }
1270             }
1271         }
1272     }
1273 
1274     /**
1275      * Removes the listener so it stops receiving updates for all {@link ProjectionType}s.
1276      *
1277      * @param listener the {@link OnProjectionStateChangedListener} to remove
1278      *
1279      * @hide
1280      */
1281     @SystemApi
1282     @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE)
removeOnProjectionStateChangedListener( @onNull OnProjectionStateChangedListener listener)1283     public void removeOnProjectionStateChangedListener(
1284             @NonNull OnProjectionStateChangedListener listener) {
1285         synchronized (mLock) {
1286             InnerListener innerListener = mProjectionStateListenerMap.get(listener);
1287             if (innerListener == null) {
1288                 Slog.i(TAG, "Attempted to remove listener that was not added.");
1289                 return;
1290             }
1291             if (sGlobals != null) {
1292                 try {
1293                     sGlobals.mService.removeOnProjectionStateChangedListener(innerListener);
1294                 } catch (RemoteException e) {
1295                     throw e.rethrowFromSystemServer();
1296                 }
1297             }
1298             mProjectionStateListenerMap.remove(listener);
1299             mOnProjectionStateChangedListenerResourceManager.remove(innerListener);
1300         }
1301     }
1302 
1303     private static class InnerListener extends IOnProjectionStateChangedListener.Stub {
1304         private final WeakReference<OnProjectionStateChangedListenerResourceManager>
1305                 mResourceManager;
1306 
InnerListener(@onNull Executor executor, @NonNull OnProjectionStateChangedListener outerListener, @NonNull OnProjectionStateChangedListenerResourceManager resourceManager)1307         private InnerListener(@NonNull Executor executor,
1308                 @NonNull OnProjectionStateChangedListener outerListener,
1309                 @NonNull OnProjectionStateChangedListenerResourceManager resourceManager) {
1310             resourceManager.put(this, executor, outerListener);
1311             mResourceManager = new WeakReference<>(resourceManager);
1312         }
1313 
1314         @Override
onProjectionStateChanged(int activeProjectionTypes, List<String> projectingPackages)1315         public void onProjectionStateChanged(int activeProjectionTypes,
1316                 List<String> projectingPackages) {
1317             OnProjectionStateChangedListenerResourceManager resourceManager =
1318                     mResourceManager.get();
1319             if (resourceManager == null) {
1320                 Slog.w(TAG, "Can't execute onProjectionStateChanged, resource manager is gone.");
1321                 return;
1322             }
1323 
1324             OnProjectionStateChangedListener outerListener = resourceManager.getOuterListener(this);
1325             Executor executor = resourceManager.getExecutor(this);
1326             if (outerListener == null || executor == null) {
1327                 Slog.w(TAG, "Can't execute onProjectionStatechanged, references are null.");
1328                 return;
1329             }
1330 
1331             executor.execute(PooledLambda.obtainRunnable(
1332                     OnProjectionStateChangedListener::onProjectionStateChanged,
1333                     outerListener,
1334                     activeProjectionTypes,
1335                     new ArraySet<>(projectingPackages)).recycleOnUse());
1336         }
1337     }
1338 
1339     /**
1340      * Wrapper class that ensures we don't leak {@link Activity} or other large {@link Context} in
1341      * which this {@link UiModeManager} resides if/when it ends without unregistering associated
1342      * callback objects.
1343      */
1344     private static class OnProjectionStateChangedListenerResourceManager {
1345         private final Map<InnerListener, OnProjectionStateChangedListener> mOuterListenerMap =
1346                 new ArrayMap<>(1);
1347         private final Map<InnerListener, Executor> mExecutorMap = new ArrayMap<>(1);
1348 
put(@onNull InnerListener innerListener, @NonNull Executor executor, OnProjectionStateChangedListener outerListener)1349         void put(@NonNull InnerListener innerListener, @NonNull Executor executor,
1350                 OnProjectionStateChangedListener outerListener) {
1351             mOuterListenerMap.put(innerListener, outerListener);
1352             mExecutorMap.put(innerListener, executor);
1353         }
1354 
remove(InnerListener innerListener)1355         void remove(InnerListener innerListener) {
1356             mOuterListenerMap.remove(innerListener);
1357             mExecutorMap.remove(innerListener);
1358         }
1359 
getOuterListener(@onNull InnerListener innerListener)1360         OnProjectionStateChangedListener getOuterListener(@NonNull InnerListener innerListener) {
1361             return mOuterListenerMap.get(innerListener);
1362         }
1363 
getExecutor(@onNull InnerListener innerListener)1364         Executor getExecutor(@NonNull InnerListener innerListener) {
1365             return mExecutorMap.get(innerListener);
1366         }
1367     }
1368 
1369     /**
1370      * Returns the color contrast for the user.
1371      * <p>
1372      * <strong>Note:</strong> You need to query this only if your application is
1373      * doing its own rendering and does not rely on the material rendering pipeline.
1374      * </p>
1375      * @return The color contrast, float in [-1, 1] where
1376      * <ul>
1377      *     <li> &nbsp; 0 corresponds to the default contrast </li>
1378      *     <li>       -1 corresponds to the minimum contrast </li>
1379      *     <li> &nbsp; 1 corresponds to the maximum contrast </li>
1380      * </ul>
1381      */
1382     @FloatRange(from = -1.0f, to = 1.0f)
getContrast()1383     public float getContrast() {
1384         return sGlobals.getContrast();
1385     }
1386 
1387     /**
1388      * Registers a {@link ContrastChangeListener} for the current user.
1389      *
1390      * @param executor The executor on which the listener should be called back.
1391      * @param listener The listener.
1392      */
addContrastChangeListener( @onNull @allbackExecutor Executor executor, @NonNull ContrastChangeListener listener)1393     public void addContrastChangeListener(
1394             @NonNull @CallbackExecutor Executor executor,
1395             @NonNull ContrastChangeListener listener) {
1396         Objects.requireNonNull(executor);
1397         Objects.requireNonNull(listener);
1398         sGlobals.addContrastChangeListener(listener, executor);
1399     }
1400 
1401     /**
1402      * Unregisters a {@link ContrastChangeListener} for the current user.
1403      * If the listener was not registered, does nothing and returns.
1404      *
1405      * @param listener The listener to unregister.
1406      */
removeContrastChangeListener(@onNull ContrastChangeListener listener)1407     public void removeContrastChangeListener(@NonNull ContrastChangeListener listener) {
1408         Objects.requireNonNull(listener);
1409         sGlobals.removeContrastChangeListener(listener);
1410     }
1411 }
1412