1 /*
2  * Copyright (C) 2007 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 android.Manifest;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemApi;
25 import android.annotation.SystemService;
26 import android.annotation.TestApi;
27 import android.annotation.UserIdInt;
28 import android.app.compat.CompatChanges;
29 import android.compat.annotation.ChangeId;
30 import android.compat.annotation.EnabledSince;
31 import android.compat.annotation.LoggingOnly;
32 import android.compat.annotation.UnsupportedAppUsage;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.graphics.drawable.Icon;
37 import android.media.INearbyMediaDevicesProvider;
38 import android.media.INearbyMediaDevicesUpdateCallback;
39 import android.media.MediaRoute2Info;
40 import android.media.NearbyDevice;
41 import android.media.NearbyMediaDevicesProvider;
42 import android.os.Binder;
43 import android.os.Build;
44 import android.os.Bundle;
45 import android.os.IBinder;
46 import android.os.RemoteException;
47 import android.os.ServiceManager;
48 import android.os.UserHandle;
49 import android.util.Pair;
50 import android.util.Slog;
51 import android.view.KeyEvent;
52 import android.view.View;
53 
54 import com.android.internal.compat.IPlatformCompat;
55 import com.android.internal.statusbar.AppClipsServiceConnector;
56 import com.android.internal.statusbar.IAddTileResultCallback;
57 import com.android.internal.statusbar.IStatusBarService;
58 import com.android.internal.statusbar.IUndoMediaTransferCallback;
59 import com.android.internal.statusbar.NotificationVisibility;
60 
61 import java.lang.annotation.Retention;
62 import java.lang.annotation.RetentionPolicy;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Objects;
67 import java.util.Set;
68 import java.util.concurrent.Executor;
69 import java.util.function.Consumer;
70 
71 /**
72  * Allows an app to control the status bar.
73  */
74 @SystemService(Context.STATUS_BAR_SERVICE)
75 public class StatusBarManager {
76     // LINT.IfChange
77     /** @hide */
78     public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
79     /** @hide */
80     public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
81     /** @hide */
82     public static final int DISABLE_NOTIFICATION_ALERTS
83             = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
84 
85     /** @hide */
86     @Deprecated
87     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
88     public static final int DISABLE_NOTIFICATION_TICKER
89             = View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
90     /** @hide */
91     public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
92     /** @hide */
93     public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
94     /** @hide */
95     public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
96     /** @hide */
97     public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
98     /** @hide */
99     public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
100     /** @hide */
101     public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;
102 
103     /** @hide */
104     public static final int DISABLE_ONGOING_CALL_CHIP = View.STATUS_BAR_DISABLE_ONGOING_CALL_CHIP;
105 
106     /** @hide */
107     @Deprecated
108     public static final int DISABLE_NAVIGATION =
109             View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;
110 
111     /** @hide */
112     public static final int DISABLE_NONE = 0x00000000;
113 
114     /** @hide */
115     public static final int DISABLE_MASK = DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS
116             | DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER
117             | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
118             | DISABLE_SEARCH | DISABLE_ONGOING_CALL_CHIP;
119 
120     /** @hide */
121     @IntDef(flag = true, prefix = {"DISABLE_"}, value = {
122             DISABLE_NONE,
123             DISABLE_EXPAND,
124             DISABLE_NOTIFICATION_ICONS,
125             DISABLE_NOTIFICATION_ALERTS,
126             DISABLE_NOTIFICATION_TICKER,
127             DISABLE_SYSTEM_INFO,
128             DISABLE_HOME,
129             DISABLE_RECENT,
130             DISABLE_BACK,
131             DISABLE_CLOCK,
132             DISABLE_SEARCH,
133             DISABLE_ONGOING_CALL_CHIP
134     })
135     @Retention(RetentionPolicy.SOURCE)
136     public @interface DisableFlags {}
137 
138     /**
139      * Flag to disable quick settings.
140      *
141      * Setting this flag disables quick settings completely, but does not disable expanding the
142      * notification shade.
143      */
144     /** @hide */
145     public static final int DISABLE2_QUICK_SETTINGS = 1;
146     /** @hide */
147     public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
148     /** @hide */
149     public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
150     /** @hide */
151     public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3;
152     /** @hide */
153     public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4;
154 
155     /** @hide */
156     public static final int DISABLE2_NONE = 0x00000000;
157 
158     /** @hide */
159     public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
160             | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS | DISABLE2_ROTATE_SUGGESTIONS;
161 
162     /** @hide */
163     @IntDef(flag = true, prefix = { "DISABLE2_" }, value = {
164             DISABLE2_NONE,
165             DISABLE2_MASK,
166             DISABLE2_QUICK_SETTINGS,
167             DISABLE2_SYSTEM_ICONS,
168             DISABLE2_NOTIFICATION_SHADE,
169             DISABLE2_GLOBAL_ACTIONS,
170             DISABLE2_ROTATE_SUGGESTIONS
171     })
172     @Retention(RetentionPolicy.SOURCE)
173     public @interface Disable2Flags {}
174     // LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt)
175 
176     private static final String TAG = "StatusBarManager";
177 
178     /**
179      * Default disable flags for setup
180      *
181      * @hide
182      */
183     public static final int DEFAULT_SETUP_DISABLE_FLAGS = DISABLE_NOTIFICATION_ALERTS
184             | DISABLE_HOME | DISABLE_EXPAND | DISABLE_RECENT | DISABLE_CLOCK | DISABLE_SEARCH;
185 
186     /**
187      * Default disable2 flags for setup
188      *
189      * @hide
190      */
191     public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_NONE;
192 
193     /**
194      * disable flags to be applied when the device is sim-locked.
195      */
196     private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND;
197 
198     /** @hide */
199     public static final int NAVIGATION_HINT_BACK_ALT      = 1 << 0;
200     /** @hide */
201     public static final int NAVIGATION_HINT_IME_SHOWN     = 1 << 1;
202     /** @hide */
203     public static final int NAVIGATION_HINT_IME_SWITCHER_SHOWN = 1 << 2;
204 
205     /** @hide */
206     public static final int WINDOW_STATUS_BAR = 1;
207     /** @hide */
208     public static final int WINDOW_NAVIGATION_BAR = 2;
209 
210     /** @hide */
211     @IntDef(flag = true, prefix = { "WINDOW_" }, value = {
212         WINDOW_STATUS_BAR,
213         WINDOW_NAVIGATION_BAR
214     })
215     @Retention(RetentionPolicy.SOURCE)
216     public @interface WindowType {}
217 
218     /** @hide */
219     public static final int WINDOW_STATE_SHOWING = 0;
220     /** @hide */
221     public static final int WINDOW_STATE_HIDING = 1;
222     /** @hide */
223     public static final int WINDOW_STATE_HIDDEN = 2;
224 
225     /** @hide */
226     @IntDef(flag = true, prefix = { "WINDOW_STATE_" }, value = {
227             WINDOW_STATE_SHOWING,
228             WINDOW_STATE_HIDING,
229             WINDOW_STATE_HIDDEN
230     })
231     @Retention(RetentionPolicy.SOURCE)
232     public @interface WindowVisibleState {}
233 
234     /** @hide */
235     public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
236     /** @hide */
237     public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
238     /** @hide */
239     public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2;
240     /** @hide */
241     public static final int CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE = 3;
242 
243     /**
244      * Broadcast action: sent to apps that hold the status bar permission when
245      * KeyguardManager#setPrivateNotificationsAllowed() is changed.
246      *
247      * Extras: #EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
248      * @hide
249      */
250     public static final String ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED
251             = "android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED";
252 
253     /**
254      * Boolean, the latest value of KeyguardManager#getPrivateNotificationsAllowed()
255      * @hide
256      */
257     public static final String EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
258             = "android.app.extra.KM_PRIVATE_NOTIFS_ALLOWED";
259 
260     /**
261      * Session flag for {@link #registerSessionListener} indicating the listener
262      * is interested in sessions on the keygaurd.
263      * Keyguard Session Boundaries:
264      *     START_SESSION: device starts going to sleep OR the keyguard is newly shown
265      *     END_SESSION: device starts going to sleep OR keyguard is no longer showing
266      * @hide
267      */
268     public static final int SESSION_KEYGUARD = 1 << 0;
269 
270     /**
271      * Session flag for {@link #registerSessionListener} indicating the current session
272      * is interested in session on the biometric prompt.
273      * @hide
274      */
275     public static final int SESSION_BIOMETRIC_PROMPT = 1 << 1;
276 
277     /** @hide */
278     public static final Set<Integer> ALL_SESSIONS = Set.of(
279             SESSION_KEYGUARD,
280             SESSION_BIOMETRIC_PROMPT
281     );
282 
283     /** @hide */
284     @Retention(RetentionPolicy.SOURCE)
285     @IntDef(flag = true, prefix = { "SESSION_KEYGUARD" }, value = {
286             SESSION_KEYGUARD,
287             SESSION_BIOMETRIC_PROMPT,
288     })
289     public @interface SessionFlags {}
290 
291     /**
292      * Response indicating that the tile was not added.
293      */
294     public static final int TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED = 0;
295     /**
296      * Response indicating that the tile was already added and the user was not prompted.
297      */
298     public static final int TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED = 1;
299     /**
300      * Response indicating that the tile was added.
301      */
302     public static final int TILE_ADD_REQUEST_RESULT_TILE_ADDED = 2;
303     /** @hide */
304     public static final int TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED = 3;
305 
306     /**
307      * Values greater or equal to this value indicate an error in the request.
308      */
309     private static final int TILE_ADD_REQUEST_FIRST_ERROR_CODE = 1000;
310 
311     /**
312      * Indicates that this package does not match that of the
313      * {@link android.service.quicksettings.TileService}.
314      */
315     public static final int TILE_ADD_REQUEST_ERROR_MISMATCHED_PACKAGE =
316             TILE_ADD_REQUEST_FIRST_ERROR_CODE;
317     /**
318      * Indicates that there's a request in progress for this package.
319      */
320     public static final int TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS =
321             TILE_ADD_REQUEST_FIRST_ERROR_CODE + 1;
322     /**
323      * Indicates that the component does not match an enabled exported
324      * {@link android.service.quicksettings.TileService} for the current user.
325      */
326     public static final int TILE_ADD_REQUEST_ERROR_BAD_COMPONENT =
327             TILE_ADD_REQUEST_FIRST_ERROR_CODE + 2;
328     /**
329      * Indicates that the user is not the current user.
330      */
331     public static final int TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER =
332             TILE_ADD_REQUEST_FIRST_ERROR_CODE + 3;
333     /**
334      * Indicates that the requesting application is not in the foreground.
335      */
336     public static final int TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND =
337             TILE_ADD_REQUEST_FIRST_ERROR_CODE + 4;
338     /**
339      * The request could not be processed because no fulfilling service was found. This could be
340      * a temporary issue (for example, SystemUI has crashed).
341      */
342     public static final int TILE_ADD_REQUEST_ERROR_NO_STATUS_BAR_SERVICE =
343             TILE_ADD_REQUEST_FIRST_ERROR_CODE + 5;
344 
345     /** @hide */
346     @IntDef(prefix = {"TILE_ADD_REQUEST"}, value = {
347             TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
348             TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED,
349             TILE_ADD_REQUEST_RESULT_TILE_ADDED,
350             TILE_ADD_REQUEST_ERROR_MISMATCHED_PACKAGE,
351             TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS,
352             TILE_ADD_REQUEST_ERROR_BAD_COMPONENT,
353             TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER,
354             TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND,
355             TILE_ADD_REQUEST_ERROR_NO_STATUS_BAR_SERVICE
356     })
357     @Retention(RetentionPolicy.SOURCE)
358     public @interface RequestResult {}
359 
360     /**
361      * Constant for {@link #setNavBarMode(int)} indicating the default navbar mode.
362      *
363      * @hide
364      */
365     @SystemApi
366     public static final int NAV_BAR_MODE_DEFAULT = 0;
367 
368     /**
369      * Constant for {@link #setNavBarMode(int)} indicating kids navbar mode.
370      *
371      * <p>When used, back and home icons will change drawables and layout, recents will be hidden,
372      * and enables the setting to force navbar visible, even when apps are in immersive mode.
373      *
374      * @hide
375      */
376     @SystemApi
377     public static final int NAV_BAR_MODE_KIDS = 1;
378 
379     /** @hide */
380     @IntDef(prefix = {"NAV_BAR_MODE_"}, value = {
381             NAV_BAR_MODE_DEFAULT,
382             NAV_BAR_MODE_KIDS
383     })
384     @Retention(RetentionPolicy.SOURCE)
385     public @interface NavBarMode {}
386 
387     /**
388      * State indicating that this sender device is close to a receiver device, so the user can
389      * potentially *start* a cast to the receiver device if the user moves their device a bit
390      * closer.
391      * <p>
392      * Important notes:
393      * <ul>
394      *     <li>This state represents that the device is close enough to inform the user that
395      *     transferring is an option, but the device is *not* close enough to actually initiate a
396      *     transfer yet.</li>
397      *     <li>This state is for *starting* a cast. It should be used when this device is currently
398      *     playing media locally and the media should be transferred to be played on the receiver
399      *     device instead.</li>
400      * </ul>
401      *
402      * @hide
403      */
404     @SystemApi
405     public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0;
406 
407     /**
408      * State indicating that this sender device is close to a receiver device, so the user can
409      * potentially *end* a cast on the receiver device if the user moves this device a bit closer.
410      * <p>
411      * Important notes:
412      * <ul>
413      *     <li>This state represents that the device is close enough to inform the user that
414      *     transferring is an option, but the device is *not* close enough to actually initiate a
415      *     transfer yet.</li>
416      *     <li>This state is for *ending* a cast. It should be used when media is currently being
417      *     played on the receiver device and the media should be transferred to play locally
418      *     instead.</li>
419      * </ul>
420      *
421      * @hide
422      */
423     @SystemApi
424     public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1;
425 
426     /**
427      * State indicating that a media transfer from this sender device to a receiver device has been
428      * started.
429      * <p>
430      * Important note: This state is for *starting* a cast. It should be used when this device is
431      * currently playing media locally and the media has started being transferred to the receiver
432      * device instead.
433      *
434      * @hide
435      */
436     @SystemApi
437     public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2;
438 
439     /**
440      * State indicating that a media transfer from the receiver and back to this sender device
441      * has been started.
442      * <p>
443      * Important note: This state is for *ending* a cast. It should be used when media is currently
444      * being played on the receiver device and the media has started being transferred to play
445      * locally instead.
446      *
447      * @hide
448      */
449     @SystemApi
450     public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3;
451 
452     /**
453      * State indicating that a media transfer from this sender device to a receiver device has
454      * finished successfully.
455      * <p>
456      * Important note: This state is for *starting* a cast. It should be used when this device had
457      * previously been playing media locally and the media has successfully been transferred to the
458      * receiver device instead.
459      *
460      * @hide
461      */
462     @SystemApi
463     public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4;
464 
465     /**
466      * State indicating that a media transfer from the receiver and back to this sender device has
467      * finished successfully.
468      * <p>
469      * Important note: This state is for *ending* a cast. It should be used when media was
470      * previously being played on the receiver device and has been successfully transferred to play
471      * locally on this device instead.
472      *
473      * @hide
474      */
475     @SystemApi
476     public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5;
477 
478     /**
479      * State indicating that the attempted transfer to the receiver device has failed.
480      *
481      * @hide
482      */
483     @SystemApi
484     public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6;
485 
486     /**
487      * State indicating that the attempted transfer back to this device has failed.
488      *
489      * @hide
490      */
491     @SystemApi
492     public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7;
493 
494     /**
495      * State indicating that this sender device is no longer close to the receiver device.
496      *
497      * @hide
498      */
499     @SystemApi
500     public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8;
501 
502     /** @hide */
503     @IntDef(prefix = {"MEDIA_TRANSFER_SENDER_STATE_"}, value = {
504             MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
505             MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
506             MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
507             MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
508             MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
509             MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
510             MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
511             MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
512             MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
513     })
514     @Retention(RetentionPolicy.SOURCE)
515     public @interface MediaTransferSenderState {}
516 
517     /**
518      * State indicating that this receiver device is close to a sender device, so the user can
519      * potentially start or end a cast to the receiver device if the user moves the sender device a
520      * bit closer.
521      * <p>
522      * Important note: This state represents that the device is close enough to inform the user that
523      * transferring is an option, but the device is *not* close enough to actually initiate a
524      * transfer yet.
525      *
526      * @hide
527      */
528     @SystemApi
529     public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0;
530 
531     /**
532      * State indicating that this receiver device is no longer close to the sender device.
533      *
534      * @hide
535      */
536     @SystemApi
537     public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1;
538 
539     /**
540      * State indicating that media transfer to this receiver device is succeeded.
541      *
542      * @hide
543      */
544     @SystemApi
545     public static final int MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 2;
546 
547     /**
548      * State indicating that media transfer to this receiver device is failed.
549      *
550      * @hide
551      */
552     @SystemApi
553     public static final int MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED = 3;
554 
555     /** @hide */
556     @IntDef(prefix = {"MEDIA_TRANSFER_RECEIVER_STATE_"}, value = {
557             MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
558             MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
559             MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
560             MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
561     })
562     @Retention(RetentionPolicy.SOURCE)
563     public @interface MediaTransferReceiverState {}
564 
565     /**
566      * A map from a provider registered in
567      * {@link #registerNearbyMediaDevicesProvider(NearbyMediaDevicesProvider)} to the wrapper
568      * around the provider that was created internally. We need the wrapper to make the provider
569      * binder-compatible, and we need to store a reference to the wrapper so that when the provider
570      * is un-registered, we un-register the saved wrapper instance.
571      */
572     private final Map<NearbyMediaDevicesProvider, NearbyMediaDevicesProviderWrapper>
573             nearbyMediaDevicesProviderMap = new HashMap<>();
574 
575     /**
576      * Media controls based on {@link android.app.Notification.MediaStyle} notifications will have
577      * actions based on the media session's {@link android.media.session.PlaybackState}, rather than
578      * the notification's actions.
579      *
580      * These actions will be:
581      * - Play/Pause (depending on whether the current state is a playing state)
582      * - Previous (if declared), or a custom action if the slot is not reserved with
583      *   {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV}
584      * - Next (if declared), or a custom action if the slot is not reserved with
585      *   {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT}
586      * - Custom action
587      * - Custom action
588      *
589      * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
590      * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
591      */
592     @ChangeId
593     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
594     private static final long MEDIA_CONTROL_SESSION_ACTIONS = 203800354L;
595 
596     /**
597      * Media controls based on {@link android.app.Notification.MediaStyle} notifications should
598      * include a non-empty title, either in the {@link android.media.MediaMetadata} or
599      * notification title.
600      */
601     @ChangeId
602     @LoggingOnly
603     private static final long MEDIA_CONTROL_BLANK_TITLE = 274775190L;
604 
605     @UnsupportedAppUsage
606     private Context mContext;
607     private IStatusBarService mService;
608     @UnsupportedAppUsage
609     private IBinder mToken = new Binder();
610 
611     private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
612             ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
613 
614     @UnsupportedAppUsage
StatusBarManager(Context context)615     StatusBarManager(Context context) {
616         mContext = context;
617     }
618 
619     @UnsupportedAppUsage
getService()620     private synchronized IStatusBarService getService() {
621         if (mService == null) {
622             mService = IStatusBarService.Stub.asInterface(
623                     ServiceManager.getService(Context.STATUS_BAR_SERVICE));
624             if (mService == null) {
625                 Slog.w(TAG, "warning: no STATUS_BAR_SERVICE");
626             }
627         }
628         return mService;
629     }
630 
631     /**
632      * Disable some features in the status bar.  Pass the bitwise-or of the DISABLE_* flags.
633      * To re-enable everything, pass {@link #DISABLE_NONE}.
634      *
635      * @hide
636      */
637     @UnsupportedAppUsage
disable(int what)638     public void disable(int what) {
639         try {
640             final int userId = Binder.getCallingUserHandle().getIdentifier();
641             final IStatusBarService svc = getService();
642             if (svc != null) {
643                 svc.disableForUser(what, mToken, mContext.getPackageName(), userId);
644             }
645         } catch (RemoteException ex) {
646             throw ex.rethrowFromSystemServer();
647         }
648     }
649 
650     /**
651      * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
652      * To re-enable everything, pass {@link #DISABLE_NONE}.
653      *
654      * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
655      *
656      * @hide
657      */
disable2(@isable2Flags int what)658     public void disable2(@Disable2Flags int what) {
659         try {
660             final int userId = Binder.getCallingUserHandle().getIdentifier();
661             final IStatusBarService svc = getService();
662             if (svc != null) {
663                 svc.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
664             }
665         } catch (RemoteException ex) {
666             throw ex.rethrowFromSystemServer();
667         }
668     }
669 
670     /**
671      * Simulate notification click for testing
672      *
673      * @hide
674      */
675     @TestApi
clickNotification(@ullable String key, int rank, int count, boolean visible)676     public void clickNotification(@Nullable String key, int rank, int count, boolean visible) {
677         clickNotificationInternal(key, rank, count, visible);
678     }
679 
clickNotificationInternal(String key, int rank, int count, boolean visible)680     private void clickNotificationInternal(String key, int rank, int count, boolean visible) {
681         try {
682             final IStatusBarService svc = getService();
683             if (svc != null) {
684                 svc.onNotificationClick(key,
685                         NotificationVisibility.obtain(key, rank, count, visible));
686             }
687         } catch (RemoteException ex) {
688             throw ex.rethrowFromSystemServer();
689         }
690     }
691 
692     /**
693      * Simulate notification feedback for testing
694      *
695      * @hide
696      */
697     @TestApi
sendNotificationFeedback(@ullable String key, @Nullable Bundle feedback)698     public void sendNotificationFeedback(@Nullable String key, @Nullable Bundle feedback) {
699         try {
700             final IStatusBarService svc = getService();
701             if (svc != null) {
702                 svc.onNotificationFeedbackReceived(key, feedback);
703             }
704         } catch (RemoteException ex) {
705             throw ex.rethrowFromSystemServer();
706         }
707     }
708 
709     /**
710      * Expand the notifications panel.
711      *
712      * @hide
713      */
714     @UnsupportedAppUsage
715     @TestApi
expandNotificationsPanel()716     public void expandNotificationsPanel() {
717         try {
718             final IStatusBarService svc = getService();
719             if (svc != null) {
720                 svc.expandNotificationsPanel();
721             }
722         } catch (RemoteException ex) {
723             throw ex.rethrowFromSystemServer();
724         }
725     }
726 
727     /**
728      * Collapse the notifications and settings panels.
729      *
730      * Starting in Android {@link Build.VERSION_CODES.S}, apps targeting SDK level {@link
731      * Build.VERSION_CODES.S} or higher will need {@link android.Manifest.permission.STATUS_BAR}
732      * permission to call this API.
733      *
734      * @hide
735      */
736     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
737     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "This operation"
738             + " is not allowed anymore, please see {@link android.content"
739             + ".Intent#ACTION_CLOSE_SYSTEM_DIALOGS} for more details.")
740     @TestApi
collapsePanels()741     public void collapsePanels() {
742 
743         try {
744             final IStatusBarService svc = getService();
745             if (svc != null) {
746                 svc.collapsePanels();
747             }
748         } catch (RemoteException ex) {
749             throw ex.rethrowFromSystemServer();
750         }
751     }
752 
753     /**
754      * Toggles the notification panel.
755      *
756      * @hide
757      */
758     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
759     @TestApi
togglePanel()760     public void togglePanel() {
761         try {
762             final IStatusBarService svc = getService();
763             if (svc != null) {
764                 svc.togglePanel();
765             }
766         } catch (RemoteException ex) {
767             throw ex.rethrowFromSystemServer();
768         }
769     }
770 
771     /**
772      * Sends system keys to the status bar.
773      *
774      * @hide
775      */
776     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
777     @TestApi
handleSystemKey(@onNull KeyEvent key)778     public void handleSystemKey(@NonNull KeyEvent key) {
779         try {
780             final IStatusBarService svc = getService();
781             if (svc != null) {
782                 svc.handleSystemKey(key);
783             }
784         } catch (RemoteException ex) {
785             throw ex.rethrowFromSystemServer();
786         }
787     }
788 
789     /**
790      * Gets the last handled system key. A system key is a KeyEvent that the
791      * {@link com.android.server.policy.PhoneWindowManager} sends directly to the
792      * status bar, rather than forwarding to apps. If a key has never been sent to the
793      * status bar, will return -1.
794      *
795      * @return the keycode of the last KeyEvent that has been sent to the system.
796      * @hide
797      */
798     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
799     @TestApi
getLastSystemKey()800     public int getLastSystemKey() {
801         try {
802             final IStatusBarService svc = getService();
803             if (svc != null) {
804                 return svc.getLastSystemKey();
805             }
806         } catch (RemoteException ex) {
807             throw ex.rethrowFromSystemServer();
808         }
809         return -1;
810     }
811 
812     /**
813      * Expand the settings panel.
814      *
815      * @hide
816      */
817     @UnsupportedAppUsage
expandSettingsPanel()818     public void expandSettingsPanel() {
819         expandSettingsPanel(null);
820     }
821 
822     /**
823      * Expand the settings panel and open a subPanel. If the subpanel is null or does not have a
824      * corresponding tile, the QS panel is simply expanded
825      *
826      * @hide
827      */
828     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
expandSettingsPanel(@ullable String subPanel)829     public void expandSettingsPanel(@Nullable String subPanel) {
830         try {
831             final IStatusBarService svc = getService();
832             if (svc != null) {
833                 svc.expandSettingsPanel(subPanel);
834             }
835         } catch (RemoteException ex) {
836             throw ex.rethrowFromSystemServer();
837         }
838     }
839 
840     /** @hide */
841     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setIcon(String slot, int iconId, int iconLevel, String contentDescription)842     public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
843         try {
844             final IStatusBarService svc = getService();
845             if (svc != null) {
846                 svc.setIcon(slot, mContext.getPackageName(), iconId, iconLevel,
847                     contentDescription);
848             }
849         } catch (RemoteException ex) {
850             throw ex.rethrowFromSystemServer();
851         }
852     }
853 
854     /** @hide */
855     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
removeIcon(String slot)856     public void removeIcon(String slot) {
857         try {
858             final IStatusBarService svc = getService();
859             if (svc != null) {
860                 svc.removeIcon(slot);
861             }
862         } catch (RemoteException ex) {
863             throw ex.rethrowFromSystemServer();
864         }
865     }
866 
867     /** @hide */
868     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setIconVisibility(String slot, boolean visible)869     public void setIconVisibility(String slot, boolean visible) {
870         try {
871             final IStatusBarService svc = getService();
872             if (svc != null) {
873                 svc.setIconVisibility(slot, visible);
874             }
875         } catch (RemoteException ex) {
876             throw ex.rethrowFromSystemServer();
877         }
878     }
879 
880     /**
881      * Enable or disable status bar elements (notifications, clock) which are inappropriate during
882      * device setup.
883      *
884      * @param disabled whether to apply or remove the disabled flags
885      *
886      * @hide
887      */
888     @SystemApi
889     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
setDisabledForSetup(boolean disabled)890     public void setDisabledForSetup(boolean disabled) {
891         try {
892             final int userId = Binder.getCallingUserHandle().getIdentifier();
893             final IStatusBarService svc = getService();
894             if (svc != null) {
895                 svc.disableForUser(disabled ? DEFAULT_SETUP_DISABLE_FLAGS : DISABLE_NONE,
896                         mToken, mContext.getPackageName(), userId);
897                 svc.disable2ForUser(disabled ? DEFAULT_SETUP_DISABLE2_FLAGS : DISABLE2_NONE,
898                         mToken, mContext.getPackageName(), userId);
899             }
900         } catch (RemoteException ex) {
901             throw ex.rethrowFromSystemServer();
902         }
903     }
904 
905     /**
906      * Enable or disable expansion of the status bar. When the device is SIM-locked, the status
907      * bar should not be expandable.
908      *
909      * @param disabled If {@code true}, the status bar will be set to non-expandable. If
910      *                 {@code false}, re-enables expansion of the status bar.
911      * @hide
912      */
913     @TestApi
914     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
915     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
setExpansionDisabledForSimNetworkLock(boolean disabled)916     public void setExpansionDisabledForSimNetworkLock(boolean disabled) {
917         try {
918             final int userId = Binder.getCallingUserHandle().getIdentifier();
919             final IStatusBarService svc = getService();
920             if (svc != null) {
921                 svc.disableForUser(disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE,
922                         mToken, mContext.getPackageName(), userId);
923             }
924         } catch (RemoteException ex) {
925             throw ex.rethrowFromSystemServer();
926         }
927     }
928 
929     /**
930      * Get this app's currently requested disabled components
931      *
932      * @return a new DisableInfo
933      *
934      * @hide
935      */
936     @SystemApi
937     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
938     @NonNull
getDisableInfo()939     public DisableInfo getDisableInfo() {
940         try {
941             final int userId = Binder.getCallingUserHandle().getIdentifier();
942             final IStatusBarService svc = getService();
943             int[] flags = new int[] {0, 0};
944             if (svc != null) {
945                 flags = svc.getDisableFlags(mToken, userId);
946             }
947 
948             return new DisableInfo(flags[0], flags[1]);
949         } catch (RemoteException ex) {
950             throw ex.rethrowFromSystemServer();
951         }
952     }
953 
954     /**
955      * Sets an active {@link android.service.quicksettings.TileService} to listening state
956      *
957      * The {@code componentName}'s package must match the calling package.
958      *
959      * @param componentName the tile to set into listening state
960      * @see android.service.quicksettings.TileService#requestListeningState
961      * @hide
962      */
requestTileServiceListeningState(@onNull ComponentName componentName)963     public void requestTileServiceListeningState(@NonNull ComponentName componentName) {
964         Objects.requireNonNull(componentName);
965         try {
966             getService().requestTileServiceListeningState(componentName, mContext.getUserId());
967         } catch (RemoteException ex) {
968             throw ex.rethrowFromSystemServer();
969         }
970     }
971 
972     /**
973      * Request to the user to add a {@link android.service.quicksettings.TileService}
974      * to the set of current QS tiles.
975      * <p>
976      * Calling this will prompt the user to decide whether they want to add the shown
977      * {@link android.service.quicksettings.TileService} to their current tiles. The user can
978      * deny the request and the system can stop processing requests for a given
979      * {@link ComponentName} after a number of requests.
980      * <p>
981      * The request will show to the user information about the tile:
982      * <ul>
983      *     <li>Application name</li>
984      *     <li>Label for the tile</li>
985      *     <li>Icon for the tile</li>
986      * </ul>
987      * <p>
988      * The user for which this will be added is determined from the {@link Context} used to retrieve
989      * this service, and must match the current user. The requesting application must be in the
990      * foreground ({@link ActivityManager.RunningAppProcessInfo#IMPORTANCE_FOREGROUND}
991      * and the {@link android.service.quicksettings.TileService} must be exported.
992      *
993      * Note: the system can choose to auto-deny a request if the user has denied that specific
994      * request (user, ComponentName) enough times before.
995      *
996      * @param tileServiceComponentName {@link ComponentName} of the
997      *        {@link android.service.quicksettings.TileService} for the request.
998      * @param tileLabel label of the tile to show to the user.
999      * @param icon icon to use in the tile shown to the user.
1000      * @param resultExecutor an executor to run the callback on
1001      * @param resultCallback callback to indicate the result of the request.
1002      *
1003      * @see android.service.quicksettings.TileService
1004      */
requestAddTileService( @onNull ComponentName tileServiceComponentName, @NonNull CharSequence tileLabel, @NonNull Icon icon, @NonNull Executor resultExecutor, @NonNull Consumer<Integer> resultCallback )1005     public void requestAddTileService(
1006             @NonNull ComponentName tileServiceComponentName,
1007             @NonNull CharSequence tileLabel,
1008             @NonNull Icon icon,
1009             @NonNull Executor resultExecutor,
1010             @NonNull Consumer<Integer> resultCallback
1011     ) {
1012         Objects.requireNonNull(tileServiceComponentName);
1013         Objects.requireNonNull(tileLabel);
1014         Objects.requireNonNull(icon);
1015         Objects.requireNonNull(resultExecutor);
1016         Objects.requireNonNull(resultCallback);
1017         if (!tileServiceComponentName.getPackageName().equals(mContext.getPackageName())) {
1018             resultExecutor.execute(
1019                     () -> resultCallback.accept(TILE_ADD_REQUEST_ERROR_MISMATCHED_PACKAGE));
1020             return;
1021         }
1022         int userId = mContext.getUserId();
1023         RequestResultCallback callbackProxy = new RequestResultCallback(resultExecutor,
1024                 resultCallback);
1025         IStatusBarService svc = getService();
1026         try {
1027             svc.requestAddTile(
1028                     tileServiceComponentName,
1029                     tileLabel,
1030                     icon,
1031                     userId,
1032                     callbackProxy
1033             );
1034         } catch (RemoteException ex) {
1035             ex.rethrowFromSystemServer();
1036         }
1037     }
1038 
1039     /**
1040      * @hide
1041      * @param packageName
1042      */
1043     @TestApi
cancelRequestAddTile(@onNull String packageName)1044     public void cancelRequestAddTile(@NonNull String packageName) {
1045         Objects.requireNonNull(packageName);
1046         IStatusBarService svc = getService();
1047         try {
1048             svc.cancelRequestAddTile(packageName);
1049         } catch (RemoteException e) {
1050             e.rethrowFromSystemServer();
1051         }
1052     }
1053 
1054     /**
1055      * Sets or removes the navigation bar mode.
1056      *
1057      * @param navBarMode the mode of the navigation bar to be set.
1058      *
1059      * @hide
1060      */
1061     @SystemApi
1062     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
setNavBarMode(@avBarMode int navBarMode)1063     public void setNavBarMode(@NavBarMode int navBarMode) {
1064         if (navBarMode != NAV_BAR_MODE_DEFAULT && navBarMode != NAV_BAR_MODE_KIDS) {
1065             throw new IllegalArgumentException("Supplied navBarMode not supported: " + navBarMode);
1066         }
1067 
1068         try {
1069             final IStatusBarService svc = getService();
1070             if (svc != null) {
1071                 svc.setNavBarMode(navBarMode);
1072             }
1073         } catch (RemoteException e) {
1074             throw e.rethrowFromSystemServer();
1075         }
1076     }
1077 
1078     /**
1079      * Gets the navigation bar mode. Returns default value if no mode is set.
1080      *
1081      * @hide
1082      */
1083     @SystemApi
1084     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
getNavBarMode()1085     public @NavBarMode int getNavBarMode() {
1086         int navBarMode = NAV_BAR_MODE_DEFAULT;
1087         try {
1088             final IStatusBarService svc = getService();
1089             if (svc != null) {
1090                 navBarMode = svc.getNavBarMode();
1091             }
1092         } catch (RemoteException e) {
1093             throw e.rethrowFromSystemServer();
1094         }
1095         return navBarMode;
1096     }
1097 
1098     /**
1099      * Notifies the system of a new media tap-to-transfer state for the <b>sender</b> device.
1100      *
1101      * <p>The callback should only be provided for the {@link
1102      * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED} or {@link
1103      * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED} states, since those are the
1104      * only states where an action can be un-done.
1105      *
1106      * @param displayState the new state for media tap-to-transfer.
1107      * @param routeInfo the media route information for the media being transferred.
1108      * @param undoExecutor an executor to run the callback on and must be provided if the
1109      *                     callback is non-null.
1110      * @param undoCallback a callback that will be triggered if the user elects to undo a media
1111      *                     transfer.
1112      *
1113      * @throws IllegalArgumentException if an undo callback is provided for states that are not a
1114      *   succeeded state.
1115      * @throws IllegalArgumentException if an executor is not provided when a callback is.
1116      *
1117      * @hide
1118      */
1119     @SystemApi
1120     @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
updateMediaTapToTransferSenderDisplay( @ediaTransferSenderState int displayState, @NonNull MediaRoute2Info routeInfo, @Nullable Executor undoExecutor, @Nullable Runnable undoCallback )1121     public void updateMediaTapToTransferSenderDisplay(
1122             @MediaTransferSenderState int displayState,
1123             @NonNull MediaRoute2Info routeInfo,
1124             @Nullable Executor undoExecutor,
1125             @Nullable Runnable undoCallback
1126     ) {
1127         Objects.requireNonNull(routeInfo);
1128         if (displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED
1129                 && displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED
1130                 && undoCallback != null) {
1131             throw new IllegalArgumentException(
1132                     "The undoCallback should only be provided when the state is a "
1133                             + "transfer succeeded state");
1134         }
1135         if (undoCallback != null && undoExecutor == null) {
1136             throw new IllegalArgumentException(
1137                     "You must pass an executor when you pass an undo callback");
1138         }
1139         IStatusBarService svc = getService();
1140         try {
1141             UndoCallback callbackProxy = null;
1142             if (undoExecutor != null) {
1143                 callbackProxy = new UndoCallback(undoExecutor, undoCallback);
1144             }
1145             svc.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, callbackProxy);
1146         } catch (RemoteException e) {
1147             e.rethrowFromSystemServer();
1148         }
1149     }
1150 
1151     /**
1152      * Notifies the system of a new media tap-to-transfer state for the <b>receiver</b> device.
1153      *
1154      * @param displayState the new state for media tap-to-transfer.
1155      * @param routeInfo the media route information for the media being transferred.
1156      * @param appIcon the icon of the app playing the media.
1157      * @param appName the name of the app playing the media.
1158      *
1159      * @hide
1160      */
1161     @SystemApi
1162     @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
updateMediaTapToTransferReceiverDisplay( @ediaTransferReceiverState int displayState, @NonNull MediaRoute2Info routeInfo, @Nullable Icon appIcon, @Nullable CharSequence appName)1163     public void updateMediaTapToTransferReceiverDisplay(
1164             @MediaTransferReceiverState int displayState,
1165             @NonNull MediaRoute2Info routeInfo,
1166             @Nullable Icon appIcon,
1167             @Nullable CharSequence appName) {
1168         Objects.requireNonNull(routeInfo);
1169         IStatusBarService svc = getService();
1170         try {
1171             svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo, appIcon, appName);
1172         } catch (RemoteException e) {
1173             e.rethrowFromSystemServer();
1174         }
1175     }
1176 
1177     /**
1178      * Registers a provider that notifies callbacks about the status of nearby devices that are able
1179      * to play media.
1180      * <p>
1181      * If multiple providers are registered, all the providers will be used for nearby device
1182      * information.
1183      * <p>
1184      * @param provider the nearby device information provider to register
1185      * <p>
1186      * @hide
1187      */
1188     @SystemApi
1189     @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
registerNearbyMediaDevicesProvider( @onNull NearbyMediaDevicesProvider provider )1190     public void registerNearbyMediaDevicesProvider(
1191             @NonNull NearbyMediaDevicesProvider provider
1192     ) {
1193         Objects.requireNonNull(provider);
1194         if (nearbyMediaDevicesProviderMap.containsKey(provider)) {
1195             return;
1196         }
1197         try {
1198             final IStatusBarService svc = getService();
1199             NearbyMediaDevicesProviderWrapper providerWrapper =
1200                     new NearbyMediaDevicesProviderWrapper(provider);
1201             nearbyMediaDevicesProviderMap.put(provider, providerWrapper);
1202             svc.registerNearbyMediaDevicesProvider(providerWrapper);
1203         } catch (RemoteException e) {
1204             throw e.rethrowFromSystemServer();
1205         }
1206     }
1207 
1208    /**
1209      * Unregisters a provider that gives information about nearby devices that are able to play
1210      * media.
1211      * <p>
1212      * See {@link registerNearbyMediaDevicesProvider}.
1213      * <p>
1214      * @param provider the nearby device information provider to unregister
1215      * <p>
1216      * @hide
1217      */
1218     @SystemApi
1219     @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
unregisterNearbyMediaDevicesProvider( @onNull NearbyMediaDevicesProvider provider )1220     public void unregisterNearbyMediaDevicesProvider(
1221             @NonNull NearbyMediaDevicesProvider provider
1222     ) {
1223         Objects.requireNonNull(provider);
1224         if (!nearbyMediaDevicesProviderMap.containsKey(provider)) {
1225             return;
1226         }
1227         try {
1228             final IStatusBarService svc = getService();
1229             NearbyMediaDevicesProviderWrapper providerWrapper =
1230                     nearbyMediaDevicesProviderMap.get(provider);
1231             nearbyMediaDevicesProviderMap.remove(provider);
1232             svc.unregisterNearbyMediaDevicesProvider(providerWrapper);
1233         } catch (RemoteException e) {
1234             throw e.rethrowFromSystemServer();
1235         }
1236     }
1237 
1238     /**
1239      * Checks whether the given package should use session-based actions for its media controls.
1240      *
1241      * @param packageName App posting media controls
1242      * @param user Current user handle
1243      * @return true if the app supports session actions
1244      *
1245      * @hide
1246      */
1247     @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
1248             android.Manifest.permission.LOG_COMPAT_CHANGE})
useMediaSessionActionsForApp(String packageName, UserHandle user)1249     public static boolean useMediaSessionActionsForApp(String packageName, UserHandle user) {
1250         return CompatChanges.isChangeEnabled(MEDIA_CONTROL_SESSION_ACTIONS, packageName, user);
1251     }
1252 
1253     /**
1254      * Log that the given package has posted media controls with a blank title
1255      *
1256      * @param packageName App posting media controls
1257      * @param userId Current user ID
1258      * @throws RuntimeException if there is an error reporting the change
1259      *
1260      * @hide
1261      */
logBlankMediaTitle(String packageName, @UserIdInt int userId)1262     public void logBlankMediaTitle(String packageName, @UserIdInt int userId)
1263             throws RuntimeException {
1264         try {
1265             mPlatformCompat.reportChangeByPackageName(MEDIA_CONTROL_BLANK_TITLE, packageName,
1266                         userId);
1267         } catch (RemoteException e) {
1268             throw e.rethrowFromSystemServer();
1269         }
1270     }
1271 
1272     /**
1273      * Checks whether the supplied activity can {@link Activity#startActivityForResult(Intent, int)}
1274      * a system activity that captures content on the screen to take a screenshot.
1275      *
1276      * <p>Note: The result should not be cached.
1277      *
1278      * <p>The system activity displays an editing tool that allows user to edit the screenshot, save
1279      * it on device, and return the edited screenshot as {@link android.net.Uri} to the calling
1280      * activity. User interaction is required to return the edited screenshot to the calling
1281      * activity.
1282      *
1283      * <p>When {@code true}, callers can use {@link Activity#startActivityForResult(Intent, int)}
1284      * to start start the content capture activity using
1285      * {@link Intent#ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE}.
1286      *
1287      * @param activity Calling activity
1288      * @return true if the activity supports launching the capture content activity for note.
1289      *
1290      * @see Intent#ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE
1291      * @see Manifest.permission#LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE
1292      * @see android.app.role.RoleManager#ROLE_NOTES
1293      */
1294     @RequiresPermission(Manifest.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE)
canLaunchCaptureContentActivityForNote(@onNull Activity activity)1295     public boolean canLaunchCaptureContentActivityForNote(@NonNull Activity activity) {
1296         Objects.requireNonNull(activity);
1297         IBinder activityToken = activity.getActivityToken();
1298         int taskId = ActivityClient.getInstance().getTaskForActivity(activityToken, false);
1299         return new AppClipsServiceConnector(mContext)
1300                 .canLaunchCaptureContentActivityForNote(taskId);
1301     }
1302 
1303     /** @hide */
windowStateToString(int state)1304     public static String windowStateToString(int state) {
1305         if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
1306         if (state == WINDOW_STATE_HIDDEN) return "WINDOW_STATE_HIDDEN";
1307         if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING";
1308         return "WINDOW_STATE_UNKNOWN";
1309     }
1310 
1311     /**
1312      * DisableInfo describes this app's requested state of the StatusBar with regards to which
1313      * components are enabled/disabled
1314      *
1315      * @hide
1316      */
1317     @SystemApi
1318     public static final class DisableInfo {
1319 
1320         private boolean mStatusBarExpansion;
1321         private boolean mNavigateHome;
1322         private boolean mNotificationPeeking;
1323         private boolean mRecents;
1324         private boolean mSearch;
1325         private boolean mSystemIcons;
1326         private boolean mClock;
1327         private boolean mNotificationIcons;
1328         private boolean mRotationSuggestion;
1329 
1330         /** @hide */
DisableInfo(int flags1, int flags2)1331         public DisableInfo(int flags1, int flags2) {
1332             mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0;
1333             mNavigateHome = (flags1 & DISABLE_HOME) != 0;
1334             mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0;
1335             mRecents = (flags1 & DISABLE_RECENT) != 0;
1336             mSearch = (flags1 & DISABLE_SEARCH) != 0;
1337             mSystemIcons = (flags1 & DISABLE_SYSTEM_INFO) != 0;
1338             mClock = (flags1 & DISABLE_CLOCK) != 0;
1339             mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0;
1340             mRotationSuggestion = (flags2 & DISABLE2_ROTATE_SUGGESTIONS) != 0;
1341         }
1342 
1343         /** @hide */
DisableInfo()1344         public DisableInfo() {}
1345 
1346         /**
1347          * @return {@code true} if expanding the notification shade is disabled
1348          *
1349          * @hide
1350          */
1351         @SystemApi
isStatusBarExpansionDisabled()1352         public boolean isStatusBarExpansionDisabled() {
1353             return mStatusBarExpansion;
1354         }
1355 
1356         /** * @hide */
setStatusBarExpansionDisabled(boolean disabled)1357         public void setStatusBarExpansionDisabled(boolean disabled) {
1358             mStatusBarExpansion = disabled;
1359         }
1360 
1361         /**
1362          * @return {@code true} if navigation home is disabled
1363          *
1364          * @hide
1365          */
1366         @SystemApi
isNavigateToHomeDisabled()1367         public boolean isNavigateToHomeDisabled() {
1368             return mNavigateHome;
1369         }
1370 
1371         /** * @hide */
setNagivationHomeDisabled(boolean disabled)1372         public void setNagivationHomeDisabled(boolean disabled) {
1373             mNavigateHome = disabled;
1374         }
1375 
1376         /**
1377          * @return {@code true} if notification peeking (heads-up notification) is disabled
1378          *
1379          * @hide
1380          */
1381         @SystemApi
isNotificationPeekingDisabled()1382         public boolean isNotificationPeekingDisabled() {
1383             return mNotificationPeeking;
1384         }
1385 
1386         /** @hide */
setNotificationPeekingDisabled(boolean disabled)1387         public void setNotificationPeekingDisabled(boolean disabled) {
1388             mNotificationPeeking = disabled;
1389         }
1390 
1391         /**
1392          * @return {@code true} if mRecents/overview is disabled
1393          *
1394          * @hide
1395          */
1396         @SystemApi
isRecentsDisabled()1397         public boolean isRecentsDisabled() {
1398             return mRecents;
1399         }
1400 
1401         /**  @hide */
setRecentsDisabled(boolean disabled)1402         public void setRecentsDisabled(boolean disabled) {
1403             mRecents = disabled;
1404         }
1405 
1406         /**
1407          * @return {@code true} if mSearch is disabled
1408          *
1409          * @hide
1410          */
1411         @SystemApi
isSearchDisabled()1412         public boolean isSearchDisabled() {
1413             return mSearch;
1414         }
1415 
1416         /** @hide */
setSearchDisabled(boolean disabled)1417         public void setSearchDisabled(boolean disabled) {
1418             mSearch = disabled;
1419         }
1420 
1421         /**
1422          * @return {@code true} if system icons are disabled
1423          *
1424          * @hide
1425          */
areSystemIconsDisabled()1426         public boolean areSystemIconsDisabled() {
1427             return mSystemIcons;
1428         }
1429 
1430         /** * @hide */
setSystemIconsDisabled(boolean disabled)1431         public void setSystemIconsDisabled(boolean disabled) {
1432             mSystemIcons = disabled;
1433         }
1434 
1435         /**
1436          * @return {@code true} if the clock icon is disabled
1437          *
1438          * @hide
1439          */
isClockDisabled()1440         public boolean isClockDisabled() {
1441             return mClock;
1442         }
1443 
1444         /** * @hide */
setClockDisabled(boolean disabled)1445         public void setClockDisabled(boolean disabled) {
1446             mClock = disabled;
1447         }
1448 
1449         /**
1450          * @return {@code true} if notification icons are disabled
1451          *
1452          * @hide
1453          */
areNotificationIconsDisabled()1454         public boolean areNotificationIconsDisabled() {
1455             return mNotificationIcons;
1456         }
1457 
1458         /** * @hide */
setNotificationIconsDisabled(boolean disabled)1459         public void setNotificationIconsDisabled(boolean disabled) {
1460             mNotificationIcons = disabled;
1461         }
1462 
1463         /**
1464          * Returns whether the rotation suggestion is disabled.
1465          *
1466          * @hide
1467          */
1468         @TestApi
isRotationSuggestionDisabled()1469         public boolean isRotationSuggestionDisabled() {
1470             return mRotationSuggestion;
1471         }
1472 
1473         /**
1474          * @return {@code true} if no components are disabled (default state)
1475          * @hide
1476          */
1477         @SystemApi
areAllComponentsEnabled()1478         public boolean areAllComponentsEnabled() {
1479             return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
1480                     && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons
1481                     && !mRotationSuggestion;
1482         }
1483 
1484         /** @hide */
setEnableAll()1485         public void setEnableAll() {
1486             mStatusBarExpansion = false;
1487             mNavigateHome = false;
1488             mNotificationPeeking = false;
1489             mRecents = false;
1490             mSearch = false;
1491             mSystemIcons = false;
1492             mClock = false;
1493             mNotificationIcons = false;
1494             mRotationSuggestion = false;
1495         }
1496 
1497         /**
1498          * @return {@code true} if all status bar components are disabled
1499          *
1500          * @hide
1501          */
areAllComponentsDisabled()1502         public boolean areAllComponentsDisabled() {
1503             return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
1504                     && mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons
1505                     && mRotationSuggestion;
1506         }
1507 
1508         /** @hide */
setDisableAll()1509         public void setDisableAll() {
1510             mStatusBarExpansion = true;
1511             mNavigateHome = true;
1512             mNotificationPeeking = true;
1513             mRecents = true;
1514             mSearch = true;
1515             mSystemIcons = true;
1516             mClock = true;
1517             mNotificationIcons = true;
1518             mRotationSuggestion = true;
1519         }
1520 
1521         @NonNull
1522         @Override
toString()1523         public String toString() {
1524             StringBuilder sb = new StringBuilder();
1525             sb.append("DisableInfo: ");
1526             sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled");
1527             sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled");
1528             sb.append(" mNotificationPeeking=")
1529                     .append(mNotificationPeeking ? "disabled" : "enabled");
1530             sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled");
1531             sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled");
1532             sb.append(" mSystemIcons=").append(mSystemIcons ? "disabled" : "enabled");
1533             sb.append(" mClock=").append(mClock ? "disabled" : "enabled");
1534             sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled");
1535             sb.append(" mRotationSuggestion=").append(mRotationSuggestion ? "disabled" : "enabled");
1536 
1537             return sb.toString();
1538 
1539         }
1540 
1541         /**
1542          * Convert a DisableInfo to equivalent flags
1543          * @return a pair of equivalent disable flags
1544          *
1545          * @hide
1546          */
toFlags()1547         public Pair<Integer, Integer> toFlags() {
1548             int disable1 = DISABLE_NONE;
1549             int disable2 = DISABLE2_NONE;
1550 
1551             if (mStatusBarExpansion) disable1 |= DISABLE_EXPAND;
1552             if (mNavigateHome) disable1 |= DISABLE_HOME;
1553             if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS;
1554             if (mRecents) disable1 |= DISABLE_RECENT;
1555             if (mSearch) disable1 |= DISABLE_SEARCH;
1556             if (mSystemIcons) disable1 |= DISABLE_SYSTEM_INFO;
1557             if (mClock) disable1 |= DISABLE_CLOCK;
1558             if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS;
1559             if (mRotationSuggestion) disable2 |= DISABLE2_ROTATE_SUGGESTIONS;
1560 
1561             return new Pair<Integer, Integer>(disable1, disable2);
1562         }
1563     }
1564 
1565     /**
1566      * @hide
1567      */
1568     static final class RequestResultCallback extends IAddTileResultCallback.Stub {
1569 
1570         @NonNull
1571         private final Executor mExecutor;
1572         @NonNull
1573         private final Consumer<Integer> mCallback;
1574 
RequestResultCallback(@onNull Executor executor, @NonNull Consumer<Integer> callback)1575         RequestResultCallback(@NonNull Executor executor, @NonNull Consumer<Integer> callback) {
1576             mExecutor = executor;
1577             mCallback = callback;
1578         }
1579 
1580         @Override
onTileRequest(int userResponse)1581         public void onTileRequest(int userResponse) {
1582             mExecutor.execute(() -> mCallback.accept(userResponse));
1583         }
1584     }
1585 
1586     /**
1587      * @hide
1588      */
1589     static final class UndoCallback extends IUndoMediaTransferCallback.Stub {
1590         @NonNull
1591         private final Executor mExecutor;
1592         @NonNull
1593         private final Runnable mCallback;
1594 
UndoCallback(@onNull Executor executor, @NonNull Runnable callback)1595         UndoCallback(@NonNull Executor executor, @NonNull Runnable callback) {
1596             mExecutor = executor;
1597             mCallback = callback;
1598         }
1599 
1600         @Override
onUndoTriggered()1601         public void onUndoTriggered() {
1602             final long callingIdentity = Binder.clearCallingIdentity();
1603             try {
1604                 mExecutor.execute(mCallback);
1605             } finally {
1606                 restoreCallingIdentity(callingIdentity);
1607             }
1608         }
1609     }
1610 
1611     /**
1612      * @hide
1613      */
1614     static final class NearbyMediaDevicesProviderWrapper extends INearbyMediaDevicesProvider.Stub {
1615         @NonNull
1616         private final NearbyMediaDevicesProvider mProvider;
1617         // Because we're wrapping a {@link NearbyMediaDevicesProvider} in a binder-compatible
1618         // interface, we also need to wrap the callbacks that the provider receives. We use
1619         // this map to keep track of the original callback and the wrapper callback so that
1620         // unregistering the callback works correctly.
1621         @NonNull
1622         private final Map<INearbyMediaDevicesUpdateCallback, Consumer<List<NearbyDevice>>>
1623                 mRegisteredCallbacks = new HashMap<>();
1624 
NearbyMediaDevicesProviderWrapper(@onNull NearbyMediaDevicesProvider provider)1625         NearbyMediaDevicesProviderWrapper(@NonNull NearbyMediaDevicesProvider provider) {
1626             mProvider = provider;
1627         }
1628 
1629         @Override
registerNearbyDevicesCallback( @onNull INearbyMediaDevicesUpdateCallback callback)1630         public void registerNearbyDevicesCallback(
1631                 @NonNull INearbyMediaDevicesUpdateCallback callback) {
1632             Consumer<List<NearbyDevice>> callbackAsConsumer = nearbyDevices -> {
1633                 try {
1634                     callback.onDevicesUpdated(nearbyDevices);
1635                 } catch (RemoteException ex) {
1636                     throw ex.rethrowFromSystemServer();
1637                 }
1638             };
1639 
1640             mRegisteredCallbacks.put(callback, callbackAsConsumer);
1641             mProvider.registerNearbyDevicesCallback(callbackAsConsumer);
1642         }
1643 
1644         @Override
unregisterNearbyDevicesCallback( @onNull INearbyMediaDevicesUpdateCallback callback)1645         public void unregisterNearbyDevicesCallback(
1646                 @NonNull INearbyMediaDevicesUpdateCallback callback) {
1647             if (!mRegisteredCallbacks.containsKey(callback)) {
1648                 return;
1649             }
1650             mProvider.unregisterNearbyDevicesCallback(mRegisteredCallbacks.get(callback));
1651             mRegisteredCallbacks.remove(callback);
1652         }
1653     }
1654 }
1655