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