1a8f951462791a16f47e8c07e552232f31dcefac5RoboErik/*
2a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Copyright (C) 2014 The Android Open Source Project
3a8f951462791a16f47e8c07e552232f31dcefac5RoboErik *
4a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Licensed under the Apache License, Version 2.0 (the "License");
5a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * you may not use this file except in compliance with the License.
6a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * You may obtain a copy of the License at
7a8f951462791a16f47e8c07e552232f31dcefac5RoboErik *
8a8f951462791a16f47e8c07e552232f31dcefac5RoboErik *      http://www.apache.org/licenses/LICENSE-2.0
9a8f951462791a16f47e8c07e552232f31dcefac5RoboErik *
10a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Unless required by applicable law or agreed to in writing, software
11a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * distributed under the License is distributed on an "AS IS" BASIS,
12a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * See the License for the specific language governing permissions and
14a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * limitations under the License.
15a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */
16a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
17a8f951462791a16f47e8c07e552232f31dcefac5RoboErikpackage com.android.server.media;
18a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
1930be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport android.app.ActivityManager;
2030be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport android.app.ActivityManagerNative;
21d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErikimport android.media.session.MediaController.PlaybackInfo;
22a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport android.media.session.PlaybackState;
2342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.session.MediaSession;
2430be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport android.os.RemoteException;
25a5b02329209be355eafadbdf9ee685ffa58d3148RoboErikimport android.os.UserHandle;
26a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
27a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.io.PrintWriter;
28a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.util.ArrayList;
2930be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport java.util.List;
30a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
31a8f951462791a16f47e8c07e552232f31dcefac5RoboErik/**
32a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Keeps track of media sessions and their priority for notifications, media
3301a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown * button dispatch, etc.
34a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */
35a8f951462791a16f47e8c07e552232f31dcefac5RoboErikpublic class MediaSessionStack {
36a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
37a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * These are states that usually indicate the user took an action and should
38a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * bump priority regardless of the old state.
39a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
40a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private static final int[] ALWAYS_PRIORITY_STATES = {
4179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_FAST_FORWARDING,
4279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_REWINDING,
4379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
4479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_SKIPPING_TO_NEXT };
45a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
46a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * These are states that usually indicate the user took an action if they
47a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * were entered from a non-priority state.
48a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
49a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private static final int[] TRANSITION_PRIORITY_STATES = {
5079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_BUFFERING,
5179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_CONNECTING,
5279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_PLAYING };
53a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
54a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
55a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
564646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private MediaSessionRecord mGlobalPrioritySession;
574646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
58870c5a6593f723211a4a90766be9191c69f38570RoboErik    // The last record that either entered one of the playing states or was
59870c5a6593f723211a4a90766be9191c69f38570RoboErik    // added.
60870c5a6593f723211a4a90766be9191c69f38570RoboErik    private MediaSessionRecord mLastInterestingRecord;
61a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private MediaSessionRecord mCachedButtonReceiver;
62a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private MediaSessionRecord mCachedDefault;
63b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    private MediaSessionRecord mCachedVolumeDefault;
64a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private ArrayList<MediaSessionRecord> mCachedActiveList;
65a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private ArrayList<MediaSessionRecord> mCachedTransportControlList;
66a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
67a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
6830be970a8ecd984ace75354e00a8d969577d18e9Insun Kang     * Checks if a media session is created from the most recent app.
6930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang     *
7030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang     * @param record A media session record to be examined.
7130be970a8ecd984ace75354e00a8d969577d18e9Insun Kang     * @return true if the media session's package name equals to the most recent app, false
7230be970a8ecd984ace75354e00a8d969577d18e9Insun Kang     * otherwise.
7330be970a8ecd984ace75354e00a8d969577d18e9Insun Kang     */
7430be970a8ecd984ace75354e00a8d969577d18e9Insun Kang    private static boolean isFromMostRecentApp(MediaSessionRecord record) {
7530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        if (ActivityManager.getCurrentUser() != record.getUserId()) {
7630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang            return false;
7730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        }
7830be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        try {
7930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang            List<ActivityManager.RecentTaskInfo> tasks =
8030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                    ActivityManagerNative.getDefault().getRecentTasks(1,
8130be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                            ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
8230be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                            ActivityManager.RECENT_IGNORE_UNAVAILABLE |
8330be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                            ActivityManager.RECENT_INCLUDE_PROFILES |
849536c809dda20bd01c3d5c30df046b271ba2ec4fJeff Sharkey                            ActivityManager.RECENT_WITH_EXCLUDED, record.getUserId()).getList();
8530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang            if (tasks != null && !tasks.isEmpty()) {
8630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                ActivityManager.RecentTaskInfo recentTask = tasks.get(0);
8730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                if (recentTask.baseIntent != null)
8830be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                    return recentTask.baseIntent.getComponent().getPackageName()
8930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang                            .equals(record.getPackageName());
9030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang            }
9130be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        } catch (RemoteException e) {
9230be970a8ecd984ace75354e00a8d969577d18e9Insun Kang            return false;
9330be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        }
9430be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        return false;
9530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang    }
9630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang
9730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang    /**
98a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Add a record to the priority tracker.
99a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
100a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record to add.
101a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
102a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void addSession(MediaSessionRecord record) {
103a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mSessions.add(record);
104a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        clearCache();
10530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        if (isFromMostRecentApp(record)) {
10630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang            mLastInterestingRecord = record;
10730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang        }
108a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
109a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
110a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
111a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Remove a record from the priority tracker.
112a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
113a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record to remove.
114a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
115a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void removeSession(MediaSessionRecord record) {
116a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mSessions.remove(record);
1174646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        if (record == mGlobalPrioritySession) {
1184646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            mGlobalPrioritySession = null;
1194646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
120a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        clearCache();
121a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
122a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
123a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
124a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Notify the priority tracker that a session's state changed.
125a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
126a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record that changed.
127a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param oldState Its old playback state.
128a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param newState Its new playback state.
1292e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik     * @return true if the priority order was updated, false otherwise.
130a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
1312e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    public boolean onPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
132a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (shouldUpdatePriority(oldState, newState)) {
133a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mSessions.remove(record);
134a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mSessions.add(0, record);
135a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            clearCache();
136870c5a6593f723211a4a90766be9191c69f38570RoboErik            // This becomes the last interesting record since it entered a
137870c5a6593f723211a4a90766be9191c69f38570RoboErik            // playing state
138870c5a6593f723211a4a90766be9191c69f38570RoboErik            mLastInterestingRecord = record;
1392e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            return true;
14023b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik        } else if (!MediaSession.isActiveState(newState)) {
14123b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik            // Just clear the volume cache when a state goes inactive
142b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            mCachedVolumeDefault = null;
143a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
1442e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        return false;
145a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
146a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
147a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
148a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Handle any stack changes that need to occur in response to a session
149a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * state change. TODO add the old and new session state as params
150a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
151a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record that changed.
152a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
153a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void onSessionStateChange(MediaSessionRecord record) {
1544d26598677095e74e30fc12f46a769cac800f946RoboErik        if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
1554d26598677095e74e30fc12f46a769cac800f946RoboErik            mGlobalPrioritySession = record;
1564d26598677095e74e30fc12f46a769cac800f946RoboErik        }
157a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        // For now just clear the cache. Eventually we'll selectively clear
158a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        // depending on what changed.
159a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        clearCache();
160a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
161a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
162a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
163a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the current priority sorted list of active sessions. The most
164a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * important session is at index 0 and the least important at size - 1.
165a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
166a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
167a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return All the active sessions in priority order.
168a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
169a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
170a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedActiveList == null) {
171a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            mCachedActiveList = getPriorityListLocked(true, 0, userId);
172a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
173a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return mCachedActiveList;
174a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
175a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
176a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
177a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the current priority sorted list of active sessions that use
178a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * transport controls. The most important session is at index 0 and the
179a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * least important at size -1.
180a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
181a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
182a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return All the active sessions that handle transport controls in
183a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *         priority order.
184a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
185a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) {
186a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedTransportControlList == null) {
187a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mCachedTransportControlList = getPriorityListLocked(true,
18842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik                    MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS, userId);
189a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
190a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return mCachedTransportControlList;
191a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
192a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
193a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
194a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the highest priority active session.
195a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
196a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
197a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return The current highest priority session or null.
198a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
199a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public MediaSessionRecord getDefaultSession(int userId) {
200a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedDefault != null) {
201a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return mCachedDefault;
202a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
203a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
204a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (records.size() > 0) {
205a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return records.get(0);
206a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
207a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return null;
208a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
209a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
210a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
211a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the highest priority session that can handle media buttons.
212a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
213a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
214870c5a6593f723211a4a90766be9191c69f38570RoboErik     * @param includeNotPlaying Return a non-playing session if nothing else is
215870c5a6593f723211a4a90766be9191c69f38570RoboErik     *            available
216a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return The default media button session or null.
217a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
218870c5a6593f723211a4a90766be9191c69f38570RoboErik    public MediaSessionRecord getDefaultMediaButtonSession(int userId, boolean includeNotPlaying) {
2194646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
2204646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            return mGlobalPrioritySession;
2214646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
222a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedButtonReceiver != null) {
223a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return mCachedButtonReceiver;
224a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
225a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
22642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);
227a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (records.size() > 0) {
228870c5a6593f723211a4a90766be9191c69f38570RoboErik            MediaSessionRecord record = records.get(0);
229870c5a6593f723211a4a90766be9191c69f38570RoboErik            if (record.isPlaybackActive(false)) {
230870c5a6593f723211a4a90766be9191c69f38570RoboErik                // Since we're going to send a button event to this record make
231870c5a6593f723211a4a90766be9191c69f38570RoboErik                // it the last interesting one.
232870c5a6593f723211a4a90766be9191c69f38570RoboErik                mLastInterestingRecord = record;
233870c5a6593f723211a4a90766be9191c69f38570RoboErik                mCachedButtonReceiver = record;
234870c5a6593f723211a4a90766be9191c69f38570RoboErik            } else if (mLastInterestingRecord != null) {
235870c5a6593f723211a4a90766be9191c69f38570RoboErik                if (records.contains(mLastInterestingRecord)) {
236870c5a6593f723211a4a90766be9191c69f38570RoboErik                    mCachedButtonReceiver = mLastInterestingRecord;
237870c5a6593f723211a4a90766be9191c69f38570RoboErik                } else {
238870c5a6593f723211a4a90766be9191c69f38570RoboErik                    // That record is no longer used. Clear its reference.
239870c5a6593f723211a4a90766be9191c69f38570RoboErik                    mLastInterestingRecord = null;
240870c5a6593f723211a4a90766be9191c69f38570RoboErik                }
241870c5a6593f723211a4a90766be9191c69f38570RoboErik            }
242870c5a6593f723211a4a90766be9191c69f38570RoboErik            if (includeNotPlaying && mCachedButtonReceiver == null) {
243870c5a6593f723211a4a90766be9191c69f38570RoboErik                // If we really want a record and we didn't find one yet use the
244870c5a6593f723211a4a90766be9191c69f38570RoboErik                // highest priority session even if it's not playing.
245870c5a6593f723211a4a90766be9191c69f38570RoboErik                mCachedButtonReceiver = record;
246870c5a6593f723211a4a90766be9191c69f38570RoboErik            }
247a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
248a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return mCachedButtonReceiver;
249a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
250a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
251b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    public MediaSessionRecord getDefaultVolumeSession(int userId) {
252b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
253b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            return mGlobalPrioritySession;
254b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
255b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        if (mCachedVolumeDefault != null) {
256b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            return mCachedVolumeDefault;
257b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
258b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
259b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        int size = records.size();
260b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        for (int i = 0; i < size; i++) {
261b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            MediaSessionRecord record = records.get(i);
262b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            if (record.isPlaybackActive(false)) {
263b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                mCachedVolumeDefault = record;
264b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                return record;
265b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            }
266b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
267b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        return null;
268b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    }
269b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik
27019c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public MediaSessionRecord getDefaultRemoteSession(int userId) {
27119c9518f6a817d53d5234de0020313cab6950b2fRoboErik        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
27219c9518f6a817d53d5234de0020313cab6950b2fRoboErik
27319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        int size = records.size();
27419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        for (int i = 0; i < size; i++) {
27519c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaSessionRecord record = records.get(i);
276d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik            if (record.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
27719c9518f6a817d53d5234de0020313cab6950b2fRoboErik                return record;
27819c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
27919c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
28019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        return null;
28119c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
28219c9518f6a817d53d5234de0020313cab6950b2fRoboErik
283de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik    public boolean isGlobalPriorityActive() {
284de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik        return mGlobalPrioritySession == null ? false : mGlobalPrioritySession.isActive();
285de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik    }
286de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik
287a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public void dump(PrintWriter pw, String prefix) {
288a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
289a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                UserHandle.USER_ALL);
290a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int count = sortedSessions.size();
2914d26598677095e74e30fc12f46a769cac800f946RoboErik        pw.println(prefix + "Global priority session is " + mGlobalPrioritySession);
292a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
293a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        String indent = prefix + "  ";
294a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        for (int i = 0; i < count; i++) {
295a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            MediaSessionRecord record = sortedSessions.get(i);
296a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            record.dump(pw, indent);
297a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            pw.println();
298a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
299a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
300a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
301a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
302a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get a priority sorted list of sessions. Can filter to only return active
303a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * sessions or sessions with specific flags.
304a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
305a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param activeOnly True to only return active sessions, false to return
306a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *            all sessions.
307a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param withFlags Only return sessions with all the specified flags set. 0
308a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *            returns all sessions.
309a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
310a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     *            will return sessions for all users.
311a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return The priority sorted list of sessions.
312a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
313a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
314a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            int userId) {
315a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
316a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int lastLocalIndex = 0;
317a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int lastActiveIndex = 0;
318a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int lastPublishedIndex = 0;
319a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
320a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int size = mSessions.size();
321a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        for (int i = 0; i < size; i++) {
322a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            final MediaSessionRecord session = mSessions.get(i);
323a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
324a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
325a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                // Filter out sessions for the wrong user
326a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                continue;
327a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            }
328a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if ((session.getFlags() & withFlags) != withFlags) {
329a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                // Filter out sessions with the wrong flags
330a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                continue;
331a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
332a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if (!session.isActive()) {
333a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                if (!activeOnly) {
334a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // If we're getting unpublished as well always put them at
335a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // the end
336a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    result.add(session);
337a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                }
338a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                continue;
339a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
340a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
341a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if (session.isSystemPriority()) {
342a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // System priority sessions are special and always go at the
343a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // front. We expect there to only be one of these at a time.
344a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                result.add(0, session);
345a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastLocalIndex++;
346a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastActiveIndex++;
347a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastPublishedIndex++;
348b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            } else if (session.isPlaybackActive(true)) {
34901a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown                // TODO this with real local route check
35001a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown                if (true) {
351a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // Active local sessions get top priority
352a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    result.add(lastLocalIndex, session);
353a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastLocalIndex++;
354a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastActiveIndex++;
355a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastPublishedIndex++;
356a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                } else {
357a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // Then active remote sessions
358a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    result.add(lastActiveIndex, session);
359a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastActiveIndex++;
360a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastPublishedIndex++;
361a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                }
362a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            } else {
363a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // inactive sessions go at the end in order of whoever last did
364a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // something.
365a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                result.add(lastPublishedIndex, session);
366a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastPublishedIndex++;
367a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
368a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
369a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
370a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return result;
371a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
372a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
373a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private boolean shouldUpdatePriority(int oldState, int newState) {
374a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (containsState(newState, ALWAYS_PRIORITY_STATES)) {
375a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return true;
376a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
377a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (!containsState(oldState, TRANSITION_PRIORITY_STATES)
378a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                && containsState(newState, TRANSITION_PRIORITY_STATES)) {
379a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return true;
380a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
381a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return false;
382a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
383a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
384a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private boolean containsState(int state, int[] states) {
385a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        for (int i = 0; i < states.length; i++) {
386a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if (states[i] == state) {
387a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                return true;
388a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
389a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
390a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return false;
391a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
392a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
393a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private void clearCache() {
394a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedDefault = null;
395b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        mCachedVolumeDefault = null;
396a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedButtonReceiver = null;
397a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedActiveList = null;
398a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedTransportControlList = null;
399a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
400a8f951462791a16f47e8c07e552232f31dcefac5RoboErik}
401