101fe661ae5da3739215d93922412df4b24c859a2RoboErik/* 201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Copyright (C) 2014 The Android Open Source Project 301fe661ae5da3739215d93922412df4b24c859a2RoboErik * 401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Licensed under the Apache License, Version 2.0 (the "License"); 501fe661ae5da3739215d93922412df4b24c859a2RoboErik * you may not use this file except in compliance with the License. 601fe661ae5da3739215d93922412df4b24c859a2RoboErik * You may obtain a copy of the License at 701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 801fe661ae5da3739215d93922412df4b24c859a2RoboErik * http://www.apache.org/licenses/LICENSE-2.0 901fe661ae5da3739215d93922412df4b24c859a2RoboErik * 1001fe661ae5da3739215d93922412df4b24c859a2RoboErik * Unless required by applicable law or agreed to in writing, software 1101fe661ae5da3739215d93922412df4b24c859a2RoboErik * distributed under the License is distributed on an "AS IS" BASIS, 1201fe661ae5da3739215d93922412df4b24c859a2RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1301fe661ae5da3739215d93922412df4b24c859a2RoboErik * See the License for the specific language governing permissions and 1401fe661ae5da3739215d93922412df4b24c859a2RoboErik * limitations under the License. 1501fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 1601fe661ae5da3739215d93922412df4b24c859a2RoboErik 172f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikpackage android.media.session; 1801fe661ae5da3739215d93922412df4b24c859a2RoboErik 19bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.NonNull; 20bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.Nullable; 21e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport android.content.ComponentName; 2201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.content.Context; 231ff5b1648a051e9650614f0c0f1b3f449777db81RoboErikimport android.media.AudioManager; 2419c9518f6a817d53d5234de0020313cab6950b2fRoboErikimport android.media.IRemoteVolumeController; 2507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErikimport android.media.session.ISessionManager; 260d194c5e2338b8a920c512e15a433cec5bd63612RoboErikimport android.os.Handler; 2701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.IBinder; 2801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException; 2901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.ServiceManager; 30a5b02329209be355eafadbdf9ee685ffa58d3148RoboErikimport android.os.UserHandle; 31e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport android.service.notification.NotificationListenerService; 32bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.text.TextUtils; 33a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErikimport android.util.ArrayMap; 3401fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log; 358a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.view.KeyEvent; 3601fe661ae5da3739215d93922412df4b24c859a2RoboErik 3701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList; 3801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.List; 3901fe661ae5da3739215d93922412df4b24c859a2RoboErik 4001fe661ae5da3739215d93922412df4b24c859a2RoboErik/** 4101a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown * Provides support for interacting with {@link MediaSession media sessions} 420d194c5e2338b8a920c512e15a433cec5bd63612RoboErik * that applications have published to express their ongoing media playback 430d194c5e2338b8a920c512e15a433cec5bd63612RoboErik * state. 4401fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p> 4501fe661ae5da3739215d93922412df4b24c859a2RoboErik * Use <code>Context.getSystemService(Context.MEDIA_SESSION_SERVICE)</code> to 4601fe661ae5da3739215d93922412df4b24c859a2RoboErik * get an instance of this class. 4701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 4842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * @see MediaSession 4942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * @see MediaController 5001fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 5142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikpublic final class MediaSessionManager { 5207c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik private static final String TAG = "SessionManager"; 5301fe661ae5da3739215d93922412df4b24c859a2RoboErik 54a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners 55a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>(); 56a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik private final Object mLock = new Object(); 5707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik private final ISessionManager mService; 5801fe661ae5da3739215d93922412df4b24c859a2RoboErik 5901fe661ae5da3739215d93922412df4b24c859a2RoboErik private Context mContext; 6001fe661ae5da3739215d93922412df4b24c859a2RoboErik 6101fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 6201fe661ae5da3739215d93922412df4b24c859a2RoboErik * @hide 6301fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 6442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik public MediaSessionManager(Context context) { 6501fe661ae5da3739215d93922412df4b24c859a2RoboErik // Consider rewriting like DisplayManagerGlobal 6601fe661ae5da3739215d93922412df4b24c859a2RoboErik // Decide if we need context 6701fe661ae5da3739215d93922412df4b24c859a2RoboErik mContext = context; 6801fe661ae5da3739215d93922412df4b24c859a2RoboErik IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE); 6907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik mService = ISessionManager.Stub.asInterface(b); 7001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 7101fe661ae5da3739215d93922412df4b24c859a2RoboErik 7201fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 738b4bffcac996b4083e720310a09d315ca1c4a000RoboErik * Create a new session in the system and get the binder for it. 7401fe661ae5da3739215d93922412df4b24c859a2RoboErik * 75bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown * @param tag A short name for debugging purposes. 768b4bffcac996b4083e720310a09d315ca1c4a000RoboErik * @return The binder object from the system 77a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @hide 78a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik */ 798b4bffcac996b4083e720310a09d315ca1c4a000RoboErik public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub, 808b4bffcac996b4083e720310a09d315ca1c4a000RoboErik @NonNull String tag, int userId) throws RemoteException { 818b4bffcac996b4083e720310a09d315ca1c4a000RoboErik return mService.createSession(mContext.getPackageName(), cbStub, tag, userId); 8201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 8301fe661ae5da3739215d93922412df4b24c859a2RoboErik 8401fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 8573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik * Get a list of controllers for all ongoing sessions. The controllers will 8673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik * be provided in priority order with the most important controller at index 8773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik * 0. 8873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik * <p> 8973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL 9073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik * permission be held by the calling app. You may also retrieve this list if 9173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik * your app is an enabled notification listener using the 92e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik * {@link NotificationListenerService} APIs, in which case you must pass the 93e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik * {@link ComponentName} of your enabled listener. 9401fe661ae5da3739215d93922412df4b24c859a2RoboErik * 95e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik * @param notificationListener The enabled notification listener component. 96e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik * May be null. 97bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown * @return A list of controllers for ongoing sessions. 9801fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 99bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown public @NonNull List<MediaController> getActiveSessions( 100bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown @Nullable ComponentName notificationListener) { 101a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik return getActiveSessionsForUser(notificationListener, UserHandle.myUserId()); 102a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik } 103a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik 104a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik /** 105a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * Get active sessions for a specific user. To retrieve actions for a user 106a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * other than your own you must hold the 107a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission 108a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * in addition to any other requirements. If you are an enabled notification 109a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * listener you may only get sessions for the users you are enabled for. 110a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * 111a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @param notificationListener The enabled notification listener component. 112a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * May be null. 113a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @param userId The user id to fetch sessions for. 114a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @return A list of controllers for ongoing sessions. 115a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @hide 116a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik */ 117bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown public @NonNull List<MediaController> getActiveSessionsForUser( 118bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown @Nullable ComponentName notificationListener, int userId) { 11942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik ArrayList<MediaController> controllers = new ArrayList<MediaController>(); 120e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik try { 121a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik List<IBinder> binders = mService.getSessions(notificationListener, userId); 1226b1bea09031ef24214f1806f9b7604302d824c62RoboErik int size = binders.size(); 1236b1bea09031ef24214f1806f9b7604302d824c62RoboErik for (int i = 0; i < size; i++) { 124031149cd5f22bd858142633c7a763450f42793f7RoboErik MediaController controller = new MediaController(mContext, ISessionController.Stub 125e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik .asInterface(binders.get(i))); 126e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik controllers.add(controller); 127e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik } 128e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik } catch (RemoteException e) { 129e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik Log.e(TAG, "Failed to get active sessions: ", e); 130e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik } 131e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik return controllers; 13201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 1338a2cfc309ab9126e90022916967c65a793c034f0RoboErik 1348a2cfc309ab9126e90022916967c65a793c034f0RoboErik /** 1352e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * Add a listener to be notified when the list of active sessions 1362e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * changes.This requires the 1372e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by 1382e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * the calling app. You may also retrieve this list if your app is an 1392e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * enabled notification listener using the 1402e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * {@link NotificationListenerService} APIs, in which case you must pass the 1410d194c5e2338b8a920c512e15a433cec5bd63612RoboErik * {@link ComponentName} of your enabled listener. Updates will be posted to 1420d194c5e2338b8a920c512e15a433cec5bd63612RoboErik * the thread that registered the listener. 1432e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * 1442e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * @param sessionListener The listener to add. 1452e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * @param notificationListener The enabled notification listener component. 1462e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * May be null. 14751fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik */ 148a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik public void addOnActiveSessionsChangedListener( 149a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik @NonNull OnActiveSessionsChangedListener sessionListener, 1500d194c5e2338b8a920c512e15a433cec5bd63612RoboErik @Nullable ComponentName notificationListener) { 151d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik addOnActiveSessionsChangedListener(sessionListener, notificationListener, null); 152d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik } 153d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik 154d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik /** 155d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * Add a listener to be notified when the list of active sessions 156d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * changes.This requires the 157d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by 158d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * the calling app. You may also retrieve this list if your app is an 159d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * enabled notification listener using the 160d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * {@link NotificationListenerService} APIs, in which case you must pass the 161d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * {@link ComponentName} of your enabled listener. Updates will be posted to 162d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * the handler specified or to the caller's thread if the handler is null. 163d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * 164d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * @param sessionListener The listener to add. 165d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * @param notificationListener The enabled notification listener component. 166d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * May be null. 167d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik * @param handler The handler to post events to. 168d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik */ 169d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik public void addOnActiveSessionsChangedListener( 170d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik @NonNull OnActiveSessionsChangedListener sessionListener, 171d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik @Nullable ComponentName notificationListener, @Nullable Handler handler) { 172a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik addOnActiveSessionsChangedListener(sessionListener, notificationListener, 173d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik UserHandle.myUserId(), handler); 17451fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik } 17551fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik 17651fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik /** 17751fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * Add a listener to be notified when the list of active sessions 17851fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * changes.This requires the 17951fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by 18051fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * the calling app. You may also retrieve this list if your app is an 18151fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * enabled notification listener using the 18251fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * {@link NotificationListenerService} APIs, in which case you must pass the 18351fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * {@link ComponentName} of your enabled listener. 18451fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * 18551fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * @param sessionListener The listener to add. 18651fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * @param notificationListener The enabled notification listener component. 18751fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik * May be null. 1882e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * @param userId The userId to listen for changes on. 1890d194c5e2338b8a920c512e15a433cec5bd63612RoboErik * @param handler The handler to post updates on. 1902e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * @hide 1912e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik */ 192a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik public void addOnActiveSessionsChangedListener( 193a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik @NonNull OnActiveSessionsChangedListener sessionListener, 1940d194c5e2338b8a920c512e15a433cec5bd63612RoboErik @Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) { 1952e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik if (sessionListener == null) { 1962e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik throw new IllegalArgumentException("listener may not be null"); 1972e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 1980d194c5e2338b8a920c512e15a433cec5bd63612RoboErik if (handler == null) { 1990d194c5e2338b8a920c512e15a433cec5bd63612RoboErik handler = new Handler(); 2000d194c5e2338b8a920c512e15a433cec5bd63612RoboErik } 201a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik synchronized (mLock) { 202a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik if (mListeners.get(sessionListener) != null) { 203a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik Log.w(TAG, "Attempted to add session listener twice, ignoring."); 204a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik return; 205a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik } 206a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik SessionsChangedWrapper wrapper = new SessionsChangedWrapper(sessionListener, handler); 207a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik try { 208a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik mService.addSessionsListener(wrapper.mStub, notificationListener, userId); 209a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik mListeners.put(sessionListener, wrapper); 210a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik } catch (RemoteException e) { 211a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik Log.e(TAG, "Error in addOnActiveSessionsChangedListener.", e); 212a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik } 2132e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 2142e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 2152e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik 2162e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik /** 2172e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * Stop receiving active sessions updates on the specified listener. 2182e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * 2192e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * @param listener The listener to remove. 2202e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik */ 221a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik public void removeOnActiveSessionsChangedListener( 222a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik @NonNull OnActiveSessionsChangedListener listener) { 2232e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik if (listener == null) { 2242e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik throw new IllegalArgumentException("listener may not be null"); 2252e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 226a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik synchronized (mLock) { 227a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik SessionsChangedWrapper wrapper = mListeners.remove(listener); 228a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik if (wrapper != null) { 229a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik try { 230a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik mService.removeSessionsListener(wrapper.mStub); 231a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik } catch (RemoteException e) { 232a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik Log.e(TAG, "Error in removeOnActiveSessionsChangedListener.", e); 233a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik } 234a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik } 2352e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 2362e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 2372e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik 2382e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik /** 23919c9518f6a817d53d5234de0020313cab6950b2fRoboErik * Set the remote volume controller to receive volume updates on. Only for 24019c9518f6a817d53d5234de0020313cab6950b2fRoboErik * use by system UI. 24119c9518f6a817d53d5234de0020313cab6950b2fRoboErik * 24219c9518f6a817d53d5234de0020313cab6950b2fRoboErik * @param rvc The volume controller to receive updates on. 24319c9518f6a817d53d5234de0020313cab6950b2fRoboErik * @hide 24419c9518f6a817d53d5234de0020313cab6950b2fRoboErik */ 24519c9518f6a817d53d5234de0020313cab6950b2fRoboErik public void setRemoteVolumeController(IRemoteVolumeController rvc) { 24619c9518f6a817d53d5234de0020313cab6950b2fRoboErik try { 24719c9518f6a817d53d5234de0020313cab6950b2fRoboErik mService.setRemoteVolumeController(rvc); 24819c9518f6a817d53d5234de0020313cab6950b2fRoboErik } catch (RemoteException e) { 24919c9518f6a817d53d5234de0020313cab6950b2fRoboErik Log.e(TAG, "Error in setRemoteVolumeController.", e); 25019c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 25119c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 25219c9518f6a817d53d5234de0020313cab6950b2fRoboErik 25319c9518f6a817d53d5234de0020313cab6950b2fRoboErik /** 2548a2cfc309ab9126e90022916967c65a793c034f0RoboErik * Send a media key event. The receiver will be selected automatically. 2558a2cfc309ab9126e90022916967c65a793c034f0RoboErik * 2568a2cfc309ab9126e90022916967c65a793c034f0RoboErik * @param keyEvent The KeyEvent to send. 2578a2cfc309ab9126e90022916967c65a793c034f0RoboErik * @hide 2588a2cfc309ab9126e90022916967c65a793c034f0RoboErik */ 259bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent) { 2608a2cfc309ab9126e90022916967c65a793c034f0RoboErik dispatchMediaKeyEvent(keyEvent, false); 2618a2cfc309ab9126e90022916967c65a793c034f0RoboErik } 2628a2cfc309ab9126e90022916967c65a793c034f0RoboErik 2638a2cfc309ab9126e90022916967c65a793c034f0RoboErik /** 2648a2cfc309ab9126e90022916967c65a793c034f0RoboErik * Send a media key event. The receiver will be selected automatically. 2658a2cfc309ab9126e90022916967c65a793c034f0RoboErik * 266bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown * @param keyEvent The KeyEvent to send. 267bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown * @param needWakeLock True if a wake lock should be held while sending the key. 2688a2cfc309ab9126e90022916967c65a793c034f0RoboErik * @hide 2698a2cfc309ab9126e90022916967c65a793c034f0RoboErik */ 270bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean needWakeLock) { 2718a2cfc309ab9126e90022916967c65a793c034f0RoboErik try { 2728a2cfc309ab9126e90022916967c65a793c034f0RoboErik mService.dispatchMediaKeyEvent(keyEvent, needWakeLock); 2738a2cfc309ab9126e90022916967c65a793c034f0RoboErik } catch (RemoteException e) { 2748a2cfc309ab9126e90022916967c65a793c034f0RoboErik Log.e(TAG, "Failed to send key event.", e); 2758a2cfc309ab9126e90022916967c65a793c034f0RoboErik } 2768a2cfc309ab9126e90022916967c65a793c034f0RoboErik } 277b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik 278b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik /** 27901a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown * Dispatch an adjust volume request to the system. It will be sent to the 2801ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik * most relevant audio stream or media session. The direction must be one of 2811ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, 2821ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik * {@link AudioManager#ADJUST_SAME}. 283b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik * 284b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik * @param suggestedStream The stream to fall back to if there isn't a 285b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik * relevant stream 2861ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik * @param direction The direction to adjust volume in. 287b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik * @param flags Any flags to include with the volume change. 288b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik * @hide 289b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik */ 2901ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) { 291b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik try { 2921ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik mService.dispatchAdjustVolume(suggestedStream, direction, flags); 293b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } catch (RemoteException e) { 294b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik Log.e(TAG, "Failed to send adjust volume.", e); 295b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 296b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 2972e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik 2982e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik /** 299de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik * Check if the global priority session is currently active. This can be 300de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik * used to decide if media keys should be sent to the session or to the app. 301de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik * 302de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik * @hide 303de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik */ 304de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik public boolean isGlobalPriorityActive() { 305de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik try { 306de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik return mService.isGlobalPriorityActive(); 307de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik } catch (RemoteException e) { 308de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik Log.e(TAG, "Failed to check if the global priority is active.", e); 309de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik } 310de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik return false; 311de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik } 312de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik 313de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik /** 3142e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * Listens for changes to the list of active sessions. This can be added 315a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik * using {@link #addOnActiveSessionsChangedListener}. 3162e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik */ 317a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik public interface OnActiveSessionsChangedListener { 318a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik public void onActiveSessionsChanged(@Nullable List<MediaController> controllers); 319a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik } 320031149cd5f22bd858142633c7a763450f42793f7RoboErik 321a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik private final class SessionsChangedWrapper { 322a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik private final OnActiveSessionsChangedListener mListener; 323a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik private final Handler mHandler; 3242e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik 325a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik public SessionsChangedWrapper(OnActiveSessionsChangedListener listener, Handler handler) { 326a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik mListener = listener; 3270d194c5e2338b8a920c512e15a433cec5bd63612RoboErik mHandler = handler; 3280d194c5e2338b8a920c512e15a433cec5bd63612RoboErik } 3290d194c5e2338b8a920c512e15a433cec5bd63612RoboErik 3302e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() { 3312e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik @Override 3320d194c5e2338b8a920c512e15a433cec5bd63612RoboErik public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) { 3330d194c5e2338b8a920c512e15a433cec5bd63612RoboErik if (mHandler != null) { 3340d194c5e2338b8a920c512e15a433cec5bd63612RoboErik mHandler.post(new Runnable() { 3350d194c5e2338b8a920c512e15a433cec5bd63612RoboErik @Override 3360d194c5e2338b8a920c512e15a433cec5bd63612RoboErik public void run() { 3370d194c5e2338b8a920c512e15a433cec5bd63612RoboErik ArrayList<MediaController> controllers 3380d194c5e2338b8a920c512e15a433cec5bd63612RoboErik = new ArrayList<MediaController>(); 3390d194c5e2338b8a920c512e15a433cec5bd63612RoboErik int size = tokens.size(); 3400d194c5e2338b8a920c512e15a433cec5bd63612RoboErik for (int i = 0; i < size; i++) { 3410d194c5e2338b8a920c512e15a433cec5bd63612RoboErik controllers.add(new MediaController(mContext, tokens.get(i))); 3420d194c5e2338b8a920c512e15a433cec5bd63612RoboErik } 343a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik mListener.onActiveSessionsChanged(controllers); 3440d194c5e2338b8a920c512e15a433cec5bd63612RoboErik } 3450d194c5e2338b8a920c512e15a433cec5bd63612RoboErik }); 3462e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 3472e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 3482e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik }; 3492e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik } 35001fe661ae5da3739215d93922412df4b24c859a2RoboErik} 351