Home | History | Annotate | Download | only in session
      1 /*
      2  * Copyright (C) 2014 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.media.session;
     18 
     19 import android.annotation.CallbackExecutor;
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.annotation.RequiresPermission;
     23 import android.annotation.SystemApi;
     24 import android.annotation.SystemService;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.media.AudioManager;
     28 import android.media.IRemoteVolumeController;
     29 import android.media.ISessionTokensListener;
     30 import android.media.MediaSession2;
     31 import android.media.MediaSessionService2;
     32 import android.media.SessionToken2;
     33 import android.media.browse.MediaBrowser;
     34 import android.os.Bundle;
     35 import android.os.Handler;
     36 import android.os.IBinder;
     37 import android.os.RemoteException;
     38 import android.os.ResultReceiver;
     39 import android.os.ServiceManager;
     40 import android.os.UserHandle;
     41 import android.service.media.MediaBrowserService;
     42 import android.service.notification.NotificationListenerService;
     43 import android.text.TextUtils;
     44 import android.util.ArrayMap;
     45 import android.util.Log;
     46 import android.view.KeyEvent;
     47 
     48 import java.util.ArrayList;
     49 import java.util.Collections;
     50 import java.util.List;
     51 import java.util.Objects;
     52 import java.util.concurrent.Executor;
     53 
     54 /**
     55  * Provides support for interacting with {@link MediaSession media sessions}
     56  * that applications have published to express their ongoing media playback
     57  * state.
     58  *
     59  * @see MediaSession
     60  * @see MediaController
     61  */
     62 @SystemService(Context.MEDIA_SESSION_SERVICE)
     63 public final class MediaSessionManager {
     64     private static final String TAG = "SessionManager";
     65 
     66     /**
     67      * Used by IOnMediaKeyListener to indicate that the media key event isn't handled.
     68      * @hide
     69      */
     70     public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0;
     71 
     72     /**
     73      * Used by IOnMediaKeyListener to indicate that the media key event is handled.
     74      * @hide
     75      */
     76     public static final int RESULT_MEDIA_KEY_HANDLED = 1;
     77 
     78     private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
     79             = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
     80     private final ArrayMap<OnSessionTokensChangedListener, SessionTokensChangedWrapper>
     81             mSessionTokensListener = new ArrayMap<>();
     82     private final Object mLock = new Object();
     83     private final ISessionManager mService;
     84 
     85     private Context mContext;
     86 
     87     private CallbackImpl mCallback;
     88     private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener;
     89     private OnMediaKeyListenerImpl mOnMediaKeyListener;
     90 
     91     /**
     92      * @hide
     93      */
     94     public MediaSessionManager(Context context) {
     95         // Consider rewriting like DisplayManagerGlobal
     96         // Decide if we need context
     97         mContext = context;
     98         IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE);
     99         mService = ISessionManager.Stub.asInterface(b);
    100     }
    101 
    102     /**
    103      * Create a new session in the system and get the binder for it.
    104      *
    105      * @param tag A short name for debugging purposes.
    106      * @return The binder object from the system
    107      * @hide
    108      */
    109     public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
    110             @NonNull String tag, int userId) throws RemoteException {
    111         return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
    112     }
    113 
    114     /**
    115      * Get a list of controllers for all ongoing sessions. The controllers will
    116      * be provided in priority order with the most important controller at index
    117      * 0.
    118      * <p>
    119      * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL
    120      * permission be held by the calling app. You may also retrieve this list if
    121      * your app is an enabled notification listener using the
    122      * {@link NotificationListenerService} APIs, in which case you must pass the
    123      * {@link ComponentName} of your enabled listener.
    124      *
    125      * @param notificationListener The enabled notification listener component.
    126      *            May be null.
    127      * @return A list of controllers for ongoing sessions.
    128      */
    129     public @NonNull List<MediaController> getActiveSessions(
    130             @Nullable ComponentName notificationListener) {
    131         return getActiveSessionsForUser(notificationListener, UserHandle.myUserId());
    132     }
    133 
    134     /**
    135      * Get active sessions for a specific user. To retrieve actions for a user
    136      * other than your own you must hold the
    137      * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission
    138      * in addition to any other requirements. If you are an enabled notification
    139      * listener you may only get sessions for the users you are enabled for.
    140      *
    141      * @param notificationListener The enabled notification listener component.
    142      *            May be null.
    143      * @param userId The user id to fetch sessions for.
    144      * @return A list of controllers for ongoing sessions.
    145      * @hide
    146      */
    147     public @NonNull List<MediaController> getActiveSessionsForUser(
    148             @Nullable ComponentName notificationListener, int userId) {
    149         ArrayList<MediaController> controllers = new ArrayList<MediaController>();
    150         try {
    151             List<IBinder> binders = mService.getSessions(notificationListener, userId);
    152             int size = binders.size();
    153             for (int i = 0; i < size; i++) {
    154                 MediaController controller = new MediaController(mContext, ISessionController.Stub
    155                         .asInterface(binders.get(i)));
    156                 controllers.add(controller);
    157             }
    158         } catch (RemoteException e) {
    159             Log.e(TAG, "Failed to get active sessions: ", e);
    160         }
    161         return controllers;
    162     }
    163 
    164     /**
    165      * Add a listener to be notified when the list of active sessions
    166      * changes.This requires the
    167      * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
    168      * the calling app. You may also retrieve this list if your app is an
    169      * enabled notification listener using the
    170      * {@link NotificationListenerService} APIs, in which case you must pass the
    171      * {@link ComponentName} of your enabled listener. Updates will be posted to
    172      * the thread that registered the listener.
    173      *
    174      * @param sessionListener The listener to add.
    175      * @param notificationListener The enabled notification listener component.
    176      *            May be null.
    177      */
    178     public void addOnActiveSessionsChangedListener(
    179             @NonNull OnActiveSessionsChangedListener sessionListener,
    180             @Nullable ComponentName notificationListener) {
    181         addOnActiveSessionsChangedListener(sessionListener, notificationListener, null);
    182     }
    183 
    184     /**
    185      * Add a listener to be notified when the list of active sessions
    186      * changes.This requires the
    187      * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
    188      * the calling app. You may also retrieve this list if your app is an
    189      * enabled notification listener using the
    190      * {@link NotificationListenerService} APIs, in which case you must pass the
    191      * {@link ComponentName} of your enabled listener. Updates will be posted to
    192      * the handler specified or to the caller's thread if the handler is null.
    193      *
    194      * @param sessionListener The listener to add.
    195      * @param notificationListener The enabled notification listener component.
    196      *            May be null.
    197      * @param handler The handler to post events to.
    198      */
    199     public void addOnActiveSessionsChangedListener(
    200             @NonNull OnActiveSessionsChangedListener sessionListener,
    201             @Nullable ComponentName notificationListener, @Nullable Handler handler) {
    202         addOnActiveSessionsChangedListener(sessionListener, notificationListener,
    203                 UserHandle.myUserId(), handler);
    204     }
    205 
    206     /**
    207      * Add a listener to be notified when the list of active sessions
    208      * changes.This requires the
    209      * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
    210      * the calling app. You may also retrieve this list if your app is an
    211      * enabled notification listener using the
    212      * {@link NotificationListenerService} APIs, in which case you must pass the
    213      * {@link ComponentName} of your enabled listener.
    214      *
    215      * @param sessionListener The listener to add.
    216      * @param notificationListener The enabled notification listener component.
    217      *            May be null.
    218      * @param userId The userId to listen for changes on.
    219      * @param handler The handler to post updates on.
    220      * @hide
    221      */
    222     public void addOnActiveSessionsChangedListener(
    223             @NonNull OnActiveSessionsChangedListener sessionListener,
    224             @Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) {
    225         if (sessionListener == null) {
    226             throw new IllegalArgumentException("listener may not be null");
    227         }
    228         if (handler == null) {
    229             handler = new Handler();
    230         }
    231         synchronized (mLock) {
    232             if (mListeners.get(sessionListener) != null) {
    233                 Log.w(TAG, "Attempted to add session listener twice, ignoring.");
    234                 return;
    235             }
    236             SessionsChangedWrapper wrapper = new SessionsChangedWrapper(mContext, sessionListener,
    237                     handler);
    238             try {
    239                 mService.addSessionsListener(wrapper.mStub, notificationListener, userId);
    240                 mListeners.put(sessionListener, wrapper);
    241             } catch (RemoteException e) {
    242                 Log.e(TAG, "Error in addOnActiveSessionsChangedListener.", e);
    243             }
    244         }
    245     }
    246 
    247     /**
    248      * Stop receiving active sessions updates on the specified listener.
    249      *
    250      * @param listener The listener to remove.
    251      */
    252     public void removeOnActiveSessionsChangedListener(
    253             @NonNull OnActiveSessionsChangedListener listener) {
    254         if (listener == null) {
    255             throw new IllegalArgumentException("listener may not be null");
    256         }
    257         synchronized (mLock) {
    258             SessionsChangedWrapper wrapper = mListeners.remove(listener);
    259             if (wrapper != null) {
    260                 try {
    261                     mService.removeSessionsListener(wrapper.mStub);
    262                 } catch (RemoteException e) {
    263                     Log.e(TAG, "Error in removeOnActiveSessionsChangedListener.", e);
    264                 } finally {
    265                     wrapper.release();
    266                 }
    267             }
    268         }
    269     }
    270 
    271     /**
    272      * Set the remote volume controller to receive volume updates on. Only for
    273      * use by system UI.
    274      *
    275      * @param rvc The volume controller to receive updates on.
    276      * @hide
    277      */
    278     public void setRemoteVolumeController(IRemoteVolumeController rvc) {
    279         try {
    280             mService.setRemoteVolumeController(rvc);
    281         } catch (RemoteException e) {
    282             Log.e(TAG, "Error in setRemoteVolumeController.", e);
    283         }
    284     }
    285 
    286     /**
    287      * Send a media key event. The receiver will be selected automatically.
    288      *
    289      * @param keyEvent The KeyEvent to send.
    290      * @hide
    291      */
    292     public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent) {
    293         dispatchMediaKeyEvent(keyEvent, false);
    294     }
    295 
    296     /**
    297      * Send a media key event. The receiver will be selected automatically.
    298      *
    299      * @param keyEvent The KeyEvent to send.
    300      * @param needWakeLock True if a wake lock should be held while sending the key.
    301      * @hide
    302      */
    303     public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean needWakeLock) {
    304         dispatchMediaKeyEventInternal(false, keyEvent, needWakeLock);
    305     }
    306 
    307     /**
    308      * Send a media key event as system component. The receiver will be selected automatically.
    309      * <p>
    310      * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or
    311      * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key
    312      * from the hardware devices.
    313      *
    314      * @param keyEvent The KeyEvent to send.
    315      * @hide
    316      */
    317     public void dispatchMediaKeyEventAsSystemService(KeyEvent keyEvent) {
    318         dispatchMediaKeyEventInternal(true, keyEvent, false);
    319     }
    320 
    321     private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
    322             boolean needWakeLock) {
    323         try {
    324             mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
    325                     needWakeLock);
    326         } catch (RemoteException e) {
    327             Log.e(TAG, "Failed to send key event.", e);
    328         }
    329     }
    330 
    331     /**
    332      * Send a volume key event. The receiver will be selected automatically.
    333      *
    334      * @param keyEvent The volume KeyEvent to send.
    335      * @hide
    336      */
    337     public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int stream, boolean musicOnly) {
    338         dispatchVolumeKeyEventInternal(false, keyEvent, stream, musicOnly);
    339     }
    340 
    341     /**
    342      * Dispatches the volume button event as system service to the session. This only effects the
    343      * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
    344      * check done by the system service.
    345      * <p>
    346      * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or
    347      * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key
    348      * from the hardware devices.
    349      *
    350      * @param keyEvent The KeyEvent to send.
    351      * @hide
    352      */
    353     public void dispatchVolumeKeyEventAsSystemService(@NonNull KeyEvent keyEvent, int streamType) {
    354         dispatchVolumeKeyEventInternal(true, keyEvent, streamType, false);
    355     }
    356 
    357     private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
    358             int stream, boolean musicOnly) {
    359         try {
    360             mService.dispatchVolumeKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
    361                     stream, musicOnly);
    362         } catch (RemoteException e) {
    363             Log.e(TAG, "Failed to send volume key event.", e);
    364         }
    365     }
    366 
    367     /**
    368      * Dispatch an adjust volume request to the system. It will be sent to the
    369      * most relevant audio stream or media session. The direction must be one of
    370      * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE},
    371      * {@link AudioManager#ADJUST_SAME}.
    372      *
    373      * @param suggestedStream The stream to fall back to if there isn't a
    374      *            relevant stream
    375      * @param direction The direction to adjust volume in.
    376      * @param flags Any flags to include with the volume change.
    377      * @hide
    378      */
    379     public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
    380         try {
    381             mService.dispatchAdjustVolume(mContext.getPackageName(), suggestedStream, direction,
    382                     flags);
    383         } catch (RemoteException e) {
    384             Log.e(TAG, "Failed to send adjust volume.", e);
    385         }
    386     }
    387 
    388     /**
    389      * Checks whether the remote user is a trusted app.
    390      * <p>
    391      * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL
    392      * permission or has an enabled notification listener.
    393      *
    394      * @param userInfo The remote user info from either
    395      *            {@link MediaSession#getCurrentControllerInfo()} or
    396      *            {@link MediaBrowserService#getCurrentBrowserInfo()}.
    397      * @return {@code true} if the remote user is trusted and its package name matches with the UID.
    398      *            {@code false} otherwise.
    399      */
    400     public boolean isTrustedForMediaControl(@NonNull RemoteUserInfo userInfo) {
    401         if (userInfo == null) {
    402             throw new IllegalArgumentException("userInfo may not be null");
    403         }
    404         if (userInfo.getPackageName() == null) {
    405             return false;
    406         }
    407         try {
    408             return mService.isTrusted(
    409                     userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
    410         } catch (RemoteException e) {
    411             Log.wtf(TAG, "Cannot communicate with the service.", e);
    412         }
    413         return false;
    414     }
    415 
    416     /**
    417      * Called when a {@link MediaSession2} is created.
    418      * @hide
    419      */
    420     public boolean createSession2(@NonNull SessionToken2 token) {
    421         if (token == null) {
    422             return false;
    423         }
    424         try {
    425             return mService.createSession2(token.toBundle());
    426         } catch (RemoteException e) {
    427             Log.wtf(TAG, "Cannot communicate with the service.", e);
    428         }
    429         return false;
    430     }
    431 
    432     /**
    433      * Called when a {@link MediaSession2} is destroyed.
    434      * @hide
    435      */
    436     public void destroySession2(@NonNull SessionToken2 token) {
    437         if (token == null) {
    438             return;
    439         }
    440         try {
    441             mService.destroySession2(token.toBundle());
    442         } catch (RemoteException e) {
    443             Log.wtf(TAG, "Cannot communicate with the service.", e);
    444         }
    445     }
    446 
    447     /**
    448      * @hide
    449      * Get {@link List} of {@link SessionToken2} whose sessions are active now. This list represents
    450      * active sessions regardless of whether they're {@link MediaSession2} or
    451      * {@link MediaSessionService2}.
    452      * <p>
    453      * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
    454      * calling app. You may also retrieve this list if your app is an enabled notification listener
    455      * using the {@link NotificationListenerService} APIs.
    456      *
    457      * @return list of tokens
    458      */
    459     public List<SessionToken2> getActiveSessionTokens() {
    460         try {
    461             List<Bundle> bundles = mService.getSessionTokens(
    462                     /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
    463                     mContext.getPackageName());
    464             return toTokenList(bundles);
    465         } catch (RemoteException e) {
    466             Log.wtf(TAG, "Cannot communicate with the service.", e);
    467             return Collections.emptyList();
    468         }
    469     }
    470 
    471     /**
    472      * @hide
    473      * Get {@link List} of {@link SessionToken2} for {@link MediaSessionService2} regardless of their
    474      * activeness. This list represents media apps that support background playback.
    475      * <p>
    476      * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
    477      * calling app. You may also retrieve this list if your app is an enabled notification listener
    478      * using the {@link NotificationListenerService} APIs.
    479      *
    480      * @return list of tokens
    481      */
    482     public List<SessionToken2> getSessionServiceTokens() {
    483         try {
    484             List<Bundle> bundles = mService.getSessionTokens(
    485                     /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
    486                     mContext.getPackageName());
    487             return toTokenList(bundles);
    488         } catch (RemoteException e) {
    489             Log.wtf(TAG, "Cannot communicate with the service.", e);
    490             return Collections.emptyList();
    491         }
    492     }
    493 
    494     /**
    495      * @hide
    496      * Get all {@link SessionToken2}s. This is the combined list of {@link #getActiveSessionTokens()}
    497      * and {@link #getSessionServiceTokens}.
    498      * <p>
    499      * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
    500      * calling app. You may also retrieve this list if your app is an enabled notification listener
    501      * using the {@link NotificationListenerService} APIs.
    502      *
    503      * @return list of tokens
    504      * @see #getActiveSessionTokens
    505      * @see #getSessionServiceTokens
    506      */
    507     public List<SessionToken2> getAllSessionTokens() {
    508         try {
    509             List<Bundle> bundles = mService.getSessionTokens(
    510                     /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
    511                     mContext.getPackageName());
    512             return toTokenList(bundles);
    513         } catch (RemoteException e) {
    514             Log.wtf(TAG, "Cannot communicate with the service.", e);
    515             return Collections.emptyList();
    516         }
    517     }
    518 
    519     /**
    520      * @hide
    521      * Add a listener to be notified when the {@link #getAllSessionTokens()} changes.
    522      * <p>
    523      * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
    524      * calling app. You may also retrieve this list if your app is an enabled notification listener
    525      * using the {@link NotificationListenerService} APIs.
    526      *
    527      * @param executor executor to run this command
    528      * @param listener The listener to add.
    529      */
    530     public void addOnSessionTokensChangedListener(@NonNull @CallbackExecutor Executor executor,
    531             @NonNull OnSessionTokensChangedListener listener) {
    532         addOnSessionTokensChangedListener(UserHandle.myUserId(), executor, listener);
    533     }
    534 
    535     /**
    536      * Add a listener to be notified when the {@link #getAllSessionTokens()} changes.
    537      * <p>
    538      * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
    539      * calling app. You may also retrieve this list if your app is an enabled notification listener
    540      * using the {@link NotificationListenerService} APIs.
    541      *
    542      * @param userId The userId to listen for changes on.
    543      * @param executor executor to run this command
    544      * @param listener The listener to add.
    545      * @hide
    546      */
    547     public void addOnSessionTokensChangedListener(int userId,
    548             @NonNull @CallbackExecutor Executor executor,
    549             @NonNull OnSessionTokensChangedListener listener) {
    550         if (executor == null) {
    551             throw new IllegalArgumentException("executor may not be null");
    552         }
    553         if (listener == null) {
    554             throw new IllegalArgumentException("listener may not be null");
    555         }
    556         synchronized (mLock) {
    557             if (mSessionTokensListener.get(listener) != null) {
    558                 Log.w(TAG, "Attempted to add session listener twice, ignoring.");
    559                 return;
    560             }
    561             SessionTokensChangedWrapper wrapper = new SessionTokensChangedWrapper(
    562                     mContext, executor, listener);
    563             try {
    564                 mService.addSessionTokensListener(wrapper.mStub, userId, mContext.getPackageName());
    565                 mSessionTokensListener.put(listener, wrapper);
    566             } catch (RemoteException e) {
    567                 Log.e(TAG, "Error in addSessionTokensListener.", e);
    568             }
    569         }
    570     }
    571 
    572     /**
    573      * @hide
    574      * Stop receiving session token updates on the specified listener.
    575      *
    576      * @param listener The listener to remove.
    577      */
    578     public void removeOnSessionTokensChangedListener(
    579             @NonNull OnSessionTokensChangedListener listener) {
    580         if (listener == null) {
    581             throw new IllegalArgumentException("listener may not be null");
    582         }
    583         synchronized (mLock) {
    584             SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener);
    585             if (wrapper != null) {
    586                 try {
    587                     mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName());
    588                 } catch (RemoteException e) {
    589                     Log.e(TAG, "Error in removeSessionTokensListener.", e);
    590                 } finally {
    591                     wrapper.release();
    592                 }
    593             }
    594         }
    595     }
    596 
    597     private static List<SessionToken2> toTokenList(List<Bundle> bundles) {
    598         List<SessionToken2> tokens = new ArrayList<>();
    599         if (bundles != null) {
    600             for (int i = 0; i < bundles.size(); i++) {
    601                 SessionToken2 token = SessionToken2.fromBundle(bundles.get(i));
    602                 if (token != null) {
    603                     tokens.add(token);
    604                 }
    605             }
    606         }
    607         return tokens;
    608     }
    609 
    610     /**
    611      * Check if the global priority session is currently active. This can be
    612      * used to decide if media keys should be sent to the session or to the app.
    613      *
    614      * @hide
    615      */
    616     public boolean isGlobalPriorityActive() {
    617         try {
    618             return mService.isGlobalPriorityActive();
    619         } catch (RemoteException e) {
    620             Log.e(TAG, "Failed to check if the global priority is active.", e);
    621         }
    622         return false;
    623     }
    624 
    625     /**
    626      * Set the volume key long-press listener. While the listener is set, the listener
    627      * gets the volume key long-presses instead of changing volume.
    628      *
    629      * <p>System can only have a single volume key long-press listener.
    630      *
    631      * @param listener The volume key long-press listener. {@code null} to reset.
    632      * @param handler The handler on which the listener should be invoked, or {@code null}
    633      *            if the listener should be invoked on the calling thread's looper.
    634      * @hide
    635      */
    636     @SystemApi
    637     @RequiresPermission(android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER)
    638     public void setOnVolumeKeyLongPressListener(
    639             OnVolumeKeyLongPressListener listener, @Nullable Handler handler) {
    640         synchronized (mLock) {
    641             try {
    642                 if (listener == null) {
    643                     mOnVolumeKeyLongPressListener = null;
    644                     mService.setOnVolumeKeyLongPressListener(null);
    645                 } else {
    646                     if (handler == null) {
    647                         handler = new Handler();
    648                     }
    649                     mOnVolumeKeyLongPressListener =
    650                             new OnVolumeKeyLongPressListenerImpl(listener, handler);
    651                     mService.setOnVolumeKeyLongPressListener(mOnVolumeKeyLongPressListener);
    652                 }
    653             } catch (RemoteException e) {
    654                 Log.e(TAG, "Failed to set volume key long press listener", e);
    655             }
    656         }
    657     }
    658 
    659     /**
    660      * Set the media key listener. While the listener is set, the listener
    661      * gets the media key before any other media sessions but after the global priority session.
    662      * If the listener handles the key (i.e. returns {@code true}),
    663      * other sessions will not get the event.
    664      *
    665      * <p>System can only have a single media key listener.
    666      *
    667      * @param listener The media key listener. {@code null} to reset.
    668      * @param handler The handler on which the listener should be invoked, or {@code null}
    669      *            if the listener should be invoked on the calling thread's looper.
    670      * @hide
    671      */
    672     @SystemApi
    673     @RequiresPermission(android.Manifest.permission.SET_MEDIA_KEY_LISTENER)
    674     public void setOnMediaKeyListener(OnMediaKeyListener listener, @Nullable Handler handler) {
    675         synchronized (mLock) {
    676             try {
    677                 if (listener == null) {
    678                     mOnMediaKeyListener = null;
    679                     mService.setOnMediaKeyListener(null);
    680                 } else {
    681                     if (handler == null) {
    682                         handler = new Handler();
    683                     }
    684                     mOnMediaKeyListener = new OnMediaKeyListenerImpl(listener, handler);
    685                     mService.setOnMediaKeyListener(mOnMediaKeyListener);
    686                 }
    687             } catch (RemoteException e) {
    688                 Log.e(TAG, "Failed to set media key listener", e);
    689             }
    690         }
    691     }
    692 
    693     /**
    694      * Set a {@link Callback}.
    695      *
    696      * <p>System can only have a single callback, and the callback can only be set by
    697      * Bluetooth service process.
    698      *
    699      * @param callback A {@link Callback}. {@code null} to reset.
    700      * @param handler The handler on which the callback should be invoked, or {@code null}
    701      *            if the callback should be invoked on the calling thread's looper.
    702      * @hide
    703      */
    704     public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
    705         synchronized (mLock) {
    706             try {
    707                 if (callback == null) {
    708                     mCallback = null;
    709                     mService.setCallback(null);
    710                 } else {
    711                     if (handler == null) {
    712                         handler = new Handler();
    713                     }
    714                     mCallback = new CallbackImpl(callback, handler);
    715                     mService.setCallback(mCallback);
    716                 }
    717             } catch (RemoteException e) {
    718                 Log.e(TAG, "Failed to set media key callback", e);
    719             }
    720         }
    721     }
    722 
    723     /**
    724      * Listens for changes to the list of active sessions. This can be added
    725      * using {@link #addOnActiveSessionsChangedListener}.
    726      */
    727     public interface OnActiveSessionsChangedListener {
    728         public void onActiveSessionsChanged(@Nullable List<MediaController> controllers);
    729     }
    730 
    731     /**
    732      * @hide
    733      * Listens for changes to the {@link #getAllSessionTokens()}. This can be added
    734      * using {@link #addOnActiveSessionsChangedListener}.
    735      */
    736     public interface OnSessionTokensChangedListener {
    737         void onSessionTokensChanged(@NonNull List<SessionToken2> tokens);
    738     }
    739 
    740     /**
    741      * Listens the volume key long-presses.
    742      * @hide
    743      */
    744     @SystemApi
    745     public interface OnVolumeKeyLongPressListener {
    746         /**
    747          * Called when the volume key is long-pressed.
    748          * <p>This will be called for both down and up events.
    749          */
    750         void onVolumeKeyLongPress(KeyEvent event);
    751     }
    752 
    753     /**
    754      * Listens the media key.
    755      * @hide
    756      */
    757     @SystemApi
    758     public interface OnMediaKeyListener {
    759         /**
    760          * Called when the media key is pressed.
    761          * <p>If the listener consumes the initial down event (i.e. ACTION_DOWN with
    762          * repeat count zero), it must also comsume all following key events.
    763          * (i.e. ACTION_DOWN with repeat count more than zero, and ACTION_UP).
    764          * <p>If it takes more than 1s to return, the key event will be sent to
    765          * other media sessions.
    766          */
    767         boolean onMediaKey(KeyEvent event);
    768     }
    769 
    770     /**
    771      * Callbacks for the media session service.
    772      *
    773      * <p>Called when a media key event is dispatched or the addressed player is changed.
    774      * The addressed player is either the media session or the media button receiver that will
    775      * receive media key events.
    776      * @hide
    777      */
    778     public static abstract class Callback {
    779         /**
    780          * Called when a media key event is dispatched to the media session
    781          * through the media session service.
    782          *
    783          * @param event Dispatched media key event.
    784          * @param sessionToken The media session's token.
    785          */
    786         public abstract void onMediaKeyEventDispatched(KeyEvent event,
    787                 MediaSession.Token sessionToken);
    788 
    789         /**
    790          * Called when a media key event is dispatched to the media button receiver
    791          * through the media session service.
    792          * <p>MediaSessionService may broadcast key events to the media button receiver
    793          * when reviving playback after the media session is released.
    794          *
    795          * @param event Dispatched media key event.
    796          * @param mediaButtonReceiver The media button receiver.
    797          */
    798         public abstract void onMediaKeyEventDispatched(KeyEvent event,
    799                 ComponentName mediaButtonReceiver);
    800 
    801         /**
    802          * Called when the addressed player is changed to a media session.
    803          * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after
    804          * {@link #setCallback} if the addressed player exists.
    805          *
    806          * @param sessionToken The media session's token.
    807          */
    808         public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken);
    809 
    810         /**
    811          * Called when the addressed player is changed to the media button receiver.
    812          * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after
    813          * {@link #setCallback} if the addressed player exists.
    814          *
    815          * @param mediaButtonReceiver The media button receiver.
    816          */
    817         public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
    818     }
    819 
    820     /**
    821      * Information of a remote user of {@link MediaSession} or {@link MediaBrowserService}.
    822      * This can be used to decide whether the remote user is trusted app, and also differentiate
    823      * caller of {@link MediaSession} and {@link MediaBrowserService} callbacks.
    824      * <p>
    825      * See {@link #equals(Object)} to take a look at how it differentiate media controller.
    826      *
    827      * @see #isTrustedForMediaControl(RemoteUserInfo)
    828      */
    829     public static final class RemoteUserInfo {
    830         private final String mPackageName;
    831         private final int mPid;
    832         private final int mUid;
    833         private final IBinder mCallerBinder;
    834 
    835         public RemoteUserInfo(@NonNull String packageName, int pid, int uid) {
    836             this(packageName, pid, uid, null);
    837         }
    838 
    839         /**
    840          * @hide
    841          */
    842         public RemoteUserInfo(String packageName, int pid, int uid, IBinder callerBinder) {
    843             mPackageName = packageName;
    844             mPid = pid;
    845             mUid = uid;
    846             mCallerBinder = callerBinder;
    847         }
    848 
    849         /**
    850          * @return package name of the controller
    851          */
    852         public String getPackageName() {
    853             return mPackageName;
    854         }
    855 
    856         /**
    857          * @return pid of the controller
    858          */
    859         public int getPid() {
    860             return mPid;
    861         }
    862 
    863         /**
    864          * @return uid of the controller
    865          */
    866         public int getUid() {
    867             return mUid;
    868         }
    869 
    870         /**
    871          * Returns equality of two RemoteUserInfo. Two RemoteUserInfos are the same only if they're
    872          * sent to the same controller (either {@link MediaController} or
    873          * {@link MediaBrowser}. If it's not nor one of them is triggered by the key presses, they
    874          * would be considered as different one.
    875          * <p>
    876          * If you only want to compare the caller's package, compare them with the
    877          * {@link #getPackageName()}, {@link #getPid()}, and/or {@link #getUid()} directly.
    878          *
    879          * @param obj the reference object with which to compare.
    880          * @return {@code true} if equals, {@code false} otherwise
    881          */
    882         @Override
    883         public boolean equals(Object obj) {
    884             if (!(obj instanceof RemoteUserInfo)) {
    885                 return false;
    886             }
    887             if (this == obj) {
    888                 return true;
    889             }
    890             RemoteUserInfo otherUserInfo = (RemoteUserInfo) obj;
    891             return (mCallerBinder == null || otherUserInfo.mCallerBinder == null) ? false
    892                     : mCallerBinder.equals(otherUserInfo.mCallerBinder);
    893         }
    894 
    895         @Override
    896         public int hashCode() {
    897             return Objects.hash(mPackageName, mPid, mUid);
    898         }
    899     }
    900 
    901     private static final class SessionsChangedWrapper {
    902         private Context mContext;
    903         private OnActiveSessionsChangedListener mListener;
    904         private Handler mHandler;
    905 
    906         public SessionsChangedWrapper(Context context, OnActiveSessionsChangedListener listener,
    907                 Handler handler) {
    908             mContext = context;
    909             mListener = listener;
    910             mHandler = handler;
    911         }
    912 
    913         private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
    914             @Override
    915             public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) {
    916                 final Handler handler = mHandler;
    917                 if (handler != null) {
    918                     handler.post(new Runnable() {
    919                         @Override
    920                         public void run() {
    921                             final Context context = mContext;
    922                             if (context != null) {
    923                                 ArrayList<MediaController> controllers = new ArrayList<>();
    924                                 int size = tokens.size();
    925                                 for (int i = 0; i < size; i++) {
    926                                     controllers.add(new MediaController(context, tokens.get(i)));
    927                                 }
    928                                 final OnActiveSessionsChangedListener listener = mListener;
    929                                 if (listener != null) {
    930                                     listener.onActiveSessionsChanged(controllers);
    931                                 }
    932                             }
    933                         }
    934                     });
    935                 }
    936             }
    937         };
    938 
    939         private void release() {
    940             mListener = null;
    941             mContext = null;
    942             mHandler = null;
    943         }
    944     }
    945 
    946     private static final class SessionTokensChangedWrapper {
    947         private Context mContext;
    948         private Executor mExecutor;
    949         private OnSessionTokensChangedListener mListener;
    950 
    951         public SessionTokensChangedWrapper(Context context, Executor executor,
    952                 OnSessionTokensChangedListener listener) {
    953             mContext = context;
    954             mExecutor = executor;
    955             mListener = listener;
    956         }
    957 
    958         private final ISessionTokensListener.Stub mStub = new ISessionTokensListener.Stub() {
    959             @Override
    960             public void onSessionTokensChanged(final List<Bundle> bundles) {
    961                 final Executor executor = mExecutor;
    962                 if (executor != null) {
    963                     executor.execute(() -> {
    964                         final Context context = mContext;
    965                         final OnSessionTokensChangedListener listener = mListener;
    966                         if (context != null && listener != null) {
    967                             listener.onSessionTokensChanged(toTokenList(bundles));
    968                         }
    969                     });
    970                 }
    971             }
    972         };
    973 
    974         private void release() {
    975             mListener = null;
    976             mContext = null;
    977             mExecutor = null;
    978         }
    979     }
    980 
    981     private static final class OnVolumeKeyLongPressListenerImpl
    982             extends IOnVolumeKeyLongPressListener.Stub {
    983         private OnVolumeKeyLongPressListener mListener;
    984         private Handler mHandler;
    985 
    986         public OnVolumeKeyLongPressListenerImpl(
    987                 OnVolumeKeyLongPressListener listener, Handler handler) {
    988             mListener = listener;
    989             mHandler = handler;
    990         }
    991 
    992         @Override
    993         public void onVolumeKeyLongPress(KeyEvent event) {
    994             if (mListener == null || mHandler == null) {
    995                 Log.w(TAG, "Failed to call volume key long-press listener." +
    996                         " Either mListener or mHandler is null");
    997                 return;
    998             }
    999             mHandler.post(new Runnable() {
   1000                 @Override
   1001                 public void run() {
   1002                     mListener.onVolumeKeyLongPress(event);
   1003                 }
   1004             });
   1005         }
   1006     }
   1007 
   1008     private static final class OnMediaKeyListenerImpl extends IOnMediaKeyListener.Stub {
   1009         private OnMediaKeyListener mListener;
   1010         private Handler mHandler;
   1011 
   1012         public OnMediaKeyListenerImpl(OnMediaKeyListener listener, Handler handler) {
   1013             mListener = listener;
   1014             mHandler = handler;
   1015         }
   1016 
   1017         @Override
   1018         public void onMediaKey(KeyEvent event, ResultReceiver result) {
   1019             if (mListener == null || mHandler == null) {
   1020                 Log.w(TAG, "Failed to call media key listener." +
   1021                         " Either mListener or mHandler is null");
   1022                 return;
   1023             }
   1024             mHandler.post(new Runnable() {
   1025                 @Override
   1026                 public void run() {
   1027                     boolean handled = mListener.onMediaKey(event);
   1028                     Log.d(TAG, "The media key listener is returned " + handled);
   1029                     if (result != null) {
   1030                         result.send(
   1031                                 handled ? RESULT_MEDIA_KEY_HANDLED : RESULT_MEDIA_KEY_NOT_HANDLED,
   1032                                 null);
   1033                     }
   1034                 }
   1035             });
   1036         }
   1037     }
   1038 
   1039     private static final class CallbackImpl extends ICallback.Stub {
   1040         private final Callback mCallback;
   1041         private final Handler mHandler;
   1042 
   1043         public CallbackImpl(@NonNull Callback callback, @NonNull Handler handler) {
   1044             mCallback = callback;
   1045             mHandler = handler;
   1046         }
   1047 
   1048         @Override
   1049         public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event,
   1050                 MediaSession.Token sessionToken) {
   1051             mHandler.post(new Runnable() {
   1052                 @Override
   1053                 public void run() {
   1054                     mCallback.onMediaKeyEventDispatched(event, sessionToken);
   1055                 }
   1056             });
   1057         }
   1058 
   1059         @Override
   1060         public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event,
   1061                 ComponentName mediaButtonReceiver) {
   1062             mHandler.post(new Runnable() {
   1063                 @Override
   1064                 public void run() {
   1065                     mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver);
   1066                 }
   1067             });
   1068         }
   1069 
   1070         @Override
   1071         public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) {
   1072             mHandler.post(new Runnable() {
   1073                 @Override
   1074                 public void run() {
   1075                     mCallback.onAddressedPlayerChanged(sessionToken);
   1076                 }
   1077             });
   1078         }
   1079 
   1080         @Override
   1081         public void onAddressedPlayerChangedToMediaButtonReceiver(
   1082                 ComponentName mediaButtonReceiver) {
   1083             mHandler.post(new Runnable() {
   1084                 @Override
   1085                 public void run() {
   1086                     mCallback.onAddressedPlayerChanged(mediaButtonReceiver);
   1087                 }
   1088             });
   1089         }
   1090     }
   1091 }
   1092