MediaSessionStack.java revision b69ffd4dc2c8fa85e0064151141ebeee90de471e
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
19a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport android.media.session.PlaybackState;
2042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.session.MediaSession;
21a5b02329209be355eafadbdf9ee685ffa58d3148RoboErikimport android.os.UserHandle;
22a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
23a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.io.PrintWriter;
24a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.util.ArrayList;
25a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
26a8f951462791a16f47e8c07e552232f31dcefac5RoboErik/**
27a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Keeps track of media sessions and their priority for notifications, media
28a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * button routing, etc.
29a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */
30a8f951462791a16f47e8c07e552232f31dcefac5RoboErikpublic class MediaSessionStack {
31a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
32a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * These are states that usually indicate the user took an action and should
33a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * bump priority regardless of the old state.
34a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
35a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private static final int[] ALWAYS_PRIORITY_STATES = {
3679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_FAST_FORWARDING,
3779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_REWINDING,
3879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
3979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_SKIPPING_TO_NEXT };
40a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
41a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * These are states that usually indicate the user took an action if they
42a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * were entered from a non-priority state.
43a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
44a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private static final int[] TRANSITION_PRIORITY_STATES = {
4579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_BUFFERING,
4679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_CONNECTING,
4779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            PlaybackState.STATE_PLAYING };
48a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
49a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
50a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
514646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private MediaSessionRecord mGlobalPrioritySession;
524646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
53a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private MediaSessionRecord mCachedButtonReceiver;
54a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private MediaSessionRecord mCachedDefault;
55b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    private MediaSessionRecord mCachedVolumeDefault;
56a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private ArrayList<MediaSessionRecord> mCachedActiveList;
57a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private ArrayList<MediaSessionRecord> mCachedTransportControlList;
58a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
59a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
60a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Add a record to the priority tracker.
61a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
62a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record to add.
63a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
64a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void addSession(MediaSessionRecord record) {
65a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mSessions.add(record);
6642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
674646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            mGlobalPrioritySession = record;
684646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
69a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        clearCache();
70a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
71a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
72a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
73a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Remove a record from the priority tracker.
74a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
75a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record to remove.
76a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
77a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void removeSession(MediaSessionRecord record) {
78a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mSessions.remove(record);
794646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        if (record == mGlobalPrioritySession) {
804646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            mGlobalPrioritySession = null;
814646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
82a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        clearCache();
83a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
84a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
85a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
86a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Notify the priority tracker that a session's state changed.
87a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
88a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record that changed.
89a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param oldState Its old playback state.
90a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param newState Its new playback state.
91a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
92a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void onPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
93a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (shouldUpdatePriority(oldState, newState)) {
94a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mSessions.remove(record);
95a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mSessions.add(0, record);
96a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            clearCache();
97b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        } else if (newState == PlaybackState.STATE_PAUSED) {
98b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            // Just clear the volume cache in this case
99b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            mCachedVolumeDefault = null;
100a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
101a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
102a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
103a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
104a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Handle any stack changes that need to occur in response to a session
105a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * state change. TODO add the old and new session state as params
106a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
107a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param record The record that changed.
108a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
109a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void onSessionStateChange(MediaSessionRecord record) {
110a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        // For now just clear the cache. Eventually we'll selectively clear
111a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        // depending on what changed.
112a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        clearCache();
113a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
114a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
115a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
116a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the current priority sorted list of active sessions. The most
117a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * important session is at index 0 and the least important at size - 1.
118a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
119a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
120a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return All the active sessions in priority order.
121a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
122a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
123a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedActiveList == null) {
124a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            mCachedActiveList = getPriorityListLocked(true, 0, userId);
125a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
126a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return mCachedActiveList;
127a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
128a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
129a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
130a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the current priority sorted list of active sessions that use
131a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * transport controls. The most important session is at index 0 and the
132a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * least important at size -1.
133a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
134a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
135a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return All the active sessions that handle transport controls in
136a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *         priority order.
137a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
138a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) {
139a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedTransportControlList == null) {
140a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mCachedTransportControlList = getPriorityListLocked(true,
14142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik                    MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS, userId);
142a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
143a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return mCachedTransportControlList;
144a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
145a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
146a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
147a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the highest priority active session.
148a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
149a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
150a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return The current highest priority session or null.
151a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
152a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public MediaSessionRecord getDefaultSession(int userId) {
153a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedDefault != null) {
154a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return mCachedDefault;
155a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
156a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
157a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (records.size() > 0) {
158a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return records.get(0);
159a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
160a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return null;
161a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
162a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
163a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
164a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get the highest priority session that can handle media buttons.
165a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
166a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to check.
167a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return The default media button session or null.
168a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
169a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public MediaSessionRecord getDefaultMediaButtonSession(int userId) {
1704646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
1714646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            return mGlobalPrioritySession;
1724646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
173a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (mCachedButtonReceiver != null) {
174a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return mCachedButtonReceiver;
175a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
176a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
17742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);
178a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (records.size() > 0) {
179a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mCachedButtonReceiver = records.get(0);
180a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
181a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return mCachedButtonReceiver;
182a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
183a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
184b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    public MediaSessionRecord getDefaultVolumeSession(int userId) {
185b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
186b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            return mGlobalPrioritySession;
187b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
188b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        if (mCachedVolumeDefault != null) {
189b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            return mCachedVolumeDefault;
190b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
191b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
192b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        int size = records.size();
193b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        for (int i = 0; i < size; i++) {
194b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            MediaSessionRecord record = records.get(i);
195b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            if (record.isPlaybackActive(false)) {
196b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                mCachedVolumeDefault = record;
197b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                return record;
198b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            }
199b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
200b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        return null;
201b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    }
202b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik
203a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    public void dump(PrintWriter pw, String prefix) {
204a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
205a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                UserHandle.USER_ALL);
206a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int count = sortedSessions.size();
207a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
208a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        String indent = prefix + "  ";
209a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        for (int i = 0; i < count; i++) {
210a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            MediaSessionRecord record = sortedSessions.get(i);
211a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            record.dump(pw, indent);
212a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            pw.println();
213a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
214a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
215a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
216a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    /**
217a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * Get a priority sorted list of sessions. Can filter to only return active
218a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * sessions or sessions with specific flags.
219a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *
220a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param activeOnly True to only return active sessions, false to return
221a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *            all sessions.
222a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @param withFlags Only return sessions with all the specified flags set. 0
223a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     *            returns all sessions.
224a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
225a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     *            will return sessions for all users.
226a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     * @return The priority sorted list of sessions.
227a8f951462791a16f47e8c07e552232f31dcefac5RoboErik     */
228a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
229a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            int userId) {
230a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
231a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int lastLocalIndex = 0;
232a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int lastActiveIndex = 0;
233a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int lastPublishedIndex = 0;
234a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
235a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        int size = mSessions.size();
236a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        for (int i = 0; i < size; i++) {
237a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            final MediaSessionRecord session = mSessions.get(i);
238a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
239a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
240a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                // Filter out sessions for the wrong user
241a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                continue;
242a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            }
243a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if ((session.getFlags() & withFlags) != withFlags) {
244a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                // Filter out sessions with the wrong flags
245a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                continue;
246a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
247a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if (!session.isActive()) {
248a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                if (!activeOnly) {
249a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // If we're getting unpublished as well always put them at
250a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // the end
251a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    result.add(session);
252a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                }
253a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                continue;
254a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
255a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
256a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if (session.isSystemPriority()) {
257a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // System priority sessions are special and always go at the
258a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // front. We expect there to only be one of these at a time.
259a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                result.add(0, session);
260a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastLocalIndex++;
261a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastActiveIndex++;
262a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastPublishedIndex++;
263b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            } else if (session.isPlaybackActive(true)) {
264a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // TODO replace getRoute() == null with real local route check
265a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                if(session.getRoute() == null) {
266a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // Active local sessions get top priority
267a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    result.add(lastLocalIndex, session);
268a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastLocalIndex++;
269a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastActiveIndex++;
270a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastPublishedIndex++;
271a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                } else {
272a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    // Then active remote sessions
273a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    result.add(lastActiveIndex, session);
274a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastActiveIndex++;
275a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    lastPublishedIndex++;
276a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                }
277a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            } else {
278a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // inactive sessions go at the end in order of whoever last did
279a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                // something.
280a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                result.add(lastPublishedIndex, session);
281a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                lastPublishedIndex++;
282a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
283a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
284a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
285a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return result;
286a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
287a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
288a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private boolean shouldUpdatePriority(int oldState, int newState) {
289a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (containsState(newState, ALWAYS_PRIORITY_STATES)) {
290a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return true;
291a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
292a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        if (!containsState(oldState, TRANSITION_PRIORITY_STATES)
293a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                && containsState(newState, TRANSITION_PRIORITY_STATES)) {
294a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            return true;
295a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
296a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return false;
297a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
298a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
299a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private boolean containsState(int state, int[] states) {
300a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        for (int i = 0; i < states.length; i++) {
301a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            if (states[i] == state) {
302a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                return true;
303a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            }
304a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
305a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return false;
306a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
307a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
308a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private void clearCache() {
309a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedDefault = null;
310b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        mCachedVolumeDefault = null;
311a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedButtonReceiver = null;
312a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedActiveList = null;
313a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mCachedTransportControlList = null;
314a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
315a8f951462791a16f47e8c07e552232f31dcefac5RoboErik}
316