Merge "Simplify the media button handling logic"
diff --git a/api/current.txt b/api/current.txt
index 369e571..b58f706 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24337,9 +24337,9 @@
     method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
     method public void setShuffleModeEnabled(boolean);
-    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
-    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
diff --git a/api/system-current.txt b/api/system-current.txt
index 95d6897..9e1c123 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26207,9 +26207,9 @@
     method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
     method public void setShuffleModeEnabled(boolean);
-    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
-    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
diff --git a/api/test-current.txt b/api/test-current.txt
index a436d1f..67d4586 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -24448,9 +24448,9 @@
     method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
     method public void setShuffleModeEnabled(boolean);
-    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
-    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index bee3f52..f10f442 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -77,13 +77,19 @@
     /**
      * Set this flag on the session to indicate that it can handle media button
      * events.
+     * @deprecated This flag is no longer used. All media sessions are expected to handle media
+     * button events now.
      */
+    @Deprecated
     public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
 
     /**
      * Set this flag on the session to indicate that it handles transport
      * control commands through its {@link Callback}.
+     * @deprecated This flag is no longer used. All media sessions are expected to handle transport
+     * controls now.
      */
+    @Deprecated
     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 333d27b..c139fa8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6555,7 +6555,7 @@
 
     public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
         final boolean isPrivileged =
-                (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
+                (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.MODIFY_AUDIO_ROUTING));
         mPlaybackMonitor.registerPlaybackCallback(pcdb, isPrivileged);
     }
@@ -6566,7 +6566,7 @@
 
     public List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
         final boolean isPrivileged =
-                (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
+                (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.MODIFY_AUDIO_ROUTING));
         return mPlaybackMonitor.getActivePlaybackConfigurations(isPrivileged);
     }
diff --git a/services/core/java/com/android/server/media/AudioPlaybackMonitor.java b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java
new file mode 100644
index 0000000..c6dc11c
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Context;
+import android.media.AudioManager.AudioPlaybackCallback;
+import android.media.AudioPlaybackConfiguration;
+import android.media.IAudioService;
+import android.media.IPlaybackConfigDispatcher;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.IntArray;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Monitors changes in audio playback and notify the newly started audio playback through the
+ * {@link OnAudioPlaybackStartedListener}.
+ */
+class AudioPlaybackMonitor extends IPlaybackConfigDispatcher.Stub {
+    private static boolean DEBUG = MediaSessionService.DEBUG;
+    private static String TAG = "AudioPlaybackMonitor";
+
+    /**
+     * Called when audio playback is started for a given UID.
+     */
+    interface OnAudioPlaybackStartedListener {
+        void onAudioPlaybackStarted(int uid);
+    }
+
+    private final Object mLock = new Object();
+    private final Context mContext;
+    private final OnAudioPlaybackStartedListener mListener;
+
+    private Set<Integer> mActiveAudioPlaybackPlayerInterfaceIds = new HashSet<>();
+    private Set<Integer> mActiveAudioPlaybackClientUids = new HashSet<>();
+
+    // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)
+    // The UID whose audio playback becomes active at the last comes first.
+    // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
+    private final IntArray mSortedAudioPlaybackClientUids = new IntArray();
+
+    AudioPlaybackMonitor(Context context, IAudioService audioService,
+            OnAudioPlaybackStartedListener listener) {
+        mContext = context;
+        mListener = listener;
+        try {
+            audioService.registerPlaybackCallback(this);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failed to register playback callback", e);
+        }
+    }
+
+    /**
+     * Called when the {@link AudioPlaybackConfiguration} is updated.
+     * <p>If an app starts audio playback, the app's local media session will be the media button
+     * session. If the app has multiple media sessions, the playback active local session will be
+     * picked.
+     *
+     * @param configs List of the current audio playback configuration
+     */
+    @Override
+    public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Set<Integer> newActiveAudioPlaybackPlayerInterfaceIds = new HashSet<>();
+            List<Integer> newActiveAudioPlaybackClientUids = new ArrayList<>();
+            synchronized (mLock) {
+                mActiveAudioPlaybackClientUids.clear();
+                for (AudioPlaybackConfiguration config : configs) {
+                    // Ignore inactive (i.e. not playing) or PLAYER_TYPE_JAM_SOUNDPOOL
+                    // (i.e. playback from the SoundPool class which is only for sound effects)
+                    // playback.
+                    // Note that we shouldn't ignore PLAYER_TYPE_UNKNOWN because it might be OEM
+                    // specific audio/video players.
+                    if (!config.isActive()
+                            || config.getPlayerType()
+                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+                        continue;
+                    }
+                    mActiveAudioPlaybackClientUids.add(config.getClientUid());
+
+                    newActiveAudioPlaybackPlayerInterfaceIds.add(config.getPlayerInterfaceId());
+                    if (!mActiveAudioPlaybackPlayerInterfaceIds.contains(
+                            config.getPlayerInterfaceId())) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Found a new active media playback. " +
+                                    AudioPlaybackConfiguration.toLogFriendlyString(config));
+                        }
+                        // New active audio playback.
+                        newActiveAudioPlaybackClientUids.add(config.getClientUid());
+                        int index = mSortedAudioPlaybackClientUids.indexOf(config.getClientUid());
+                        if (index == 0) {
+                            // It's the lastly played music app already. Skip updating.
+                            continue;
+                        } else if (index > 0) {
+                            mSortedAudioPlaybackClientUids.remove(index);
+                        }
+                        mSortedAudioPlaybackClientUids.add(0, config.getClientUid());
+                    }
+                }
+                mActiveAudioPlaybackPlayerInterfaceIds.clear();
+                mActiveAudioPlaybackPlayerInterfaceIds = newActiveAudioPlaybackPlayerInterfaceIds;
+            }
+            for (int uid : newActiveAudioPlaybackClientUids) {
+                mListener.onAudioPlaybackStarted(uid);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an
+     * audio/video) The UID whose audio playback becomes active at the last comes first.
+     */
+    public IntArray getSortedAudioPlaybackClientUids() {
+        IntArray sortedAudioPlaybackClientUids = new IntArray();
+        synchronized (mLock) {
+            sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);
+        }
+        return sortedAudioPlaybackClientUids;
+    }
+
+    /**
+     * Returns if the audio playback is active for the uid.
+     */
+    public boolean isPlaybackActive(int uid) {
+        synchronized (mLock) {
+            return mActiveAudioPlaybackClientUids.contains(uid);
+        }
+    }
+
+    /**
+     * Cleans up the sorted list of audio playback client UIDs with given {@param
+     * mediaButtonSessionUid}.
+     * <p>UIDs whose audio playback started after the media button session's audio playback
+     * cannot be the lastly played media app. So they won't needed anymore.
+     *
+     * @param mediaButtonSessionUid UID of the media button session.
+     */
+    public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) {
+        synchronized (mLock) {
+            int userId = UserHandle.getUserId(mediaButtonSessionUid);
+            for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) {
+                if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) {
+                    break;
+                }
+                if (userId == UserHandle.getUserId(mSortedAudioPlaybackClientUids.get(i))) {
+                    // Clean up unnecessary UIDs.
+                    // It doesn't need to be managed profile aware because it's just to prevent
+                    // the list from increasing indefinitely. The media button session updating
+                    // shouldn't be affected by cleaning up.
+                    mSortedAudioPlaybackClientUids.remove(i);
+                }
+            }
+        }
+    }
+
+    /**
+     * Dumps {@link AudioPlaybackMonitor}.
+     */
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mLock) {
+            pw.println(prefix + "Audio playback (lastly played comes first)");
+            String indent = prefix + "  ";
+            for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) {
+                int uid = mSortedAudioPlaybackClientUids.get(i);
+                pw.print(indent + "uid=" + uid + " packages=");
+                String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+                if (packages != null && packages.length > 0) {
+                    for (int j = 0; j < packages.length; j++) {
+                        pw.print(packages[j] + " ");
+                    }
+                }
+                pw.println();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 20663a0..7f75c83 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -66,12 +66,6 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
-     * The length of time a session will still be considered active after
-     * pausing in ms.
-     */
-    private static final int ACTIVE_BUFFER = 30000;
-
-    /**
      * The amount of time we'll send an assumed volume after the last volume
      * command before reverting to the last reported volume.
      */
@@ -109,7 +103,6 @@
     private int mRatingType;
     private int mRepeatMode;
     private boolean mShuffleModeEnabled;
-    private long mLastActiveTime;
     // End TransportPerformer fields
 
     // Volume handling fields
@@ -130,7 +123,7 @@
     private String mCallingPackage;
 
     public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
-            ISessionCallback cb, String tag, MediaSessionService service, Handler handler) {
+            ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) {
         mOwnerPid = ownerPid;
         mOwnerUid = ownerUid;
         mUserId = userId;
@@ -140,7 +133,7 @@
         mSession = new SessionStub();
         mSessionCb = new SessionCb(cb);
         mService = service;
-        mHandler = new MessageHandler(handler.getLooper());
+        mHandler = new MessageHandler(handlerLooper);
         mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
         mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
@@ -211,6 +204,15 @@
     }
 
     /**
+     * Get the UID this session was created for.
+     *
+     * @return The UID for this session.
+     */
+    public int getUid() {
+        return mOwnerUid;
+    }
+
+    /**
      * Get the user id this session was created for.
      *
      * @return The user id for this session.
@@ -244,7 +246,7 @@
     public void adjustVolume(int direction, int flags, String packageName, int uid,
             boolean useSuggested) {
         int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
-        if (isPlaybackActive(false) || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
+        if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
         }
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
@@ -320,25 +322,13 @@
     }
 
     /**
-     * Check if the session is currently performing playback. This will also
-     * return true if the session was recently paused.
+     * Check if the session is currently performing playback.
      *
-     * @param includeRecentlyActive True if playback that was recently paused
-     *            should count, false if it shouldn't.
      * @return True if the session is performing playback, false otherwise.
      */
-    public boolean isPlaybackActive(boolean includeRecentlyActive) {
-        int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
-        if (MediaSession.isActiveState(state)) {
-            return true;
-        }
-        if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) {
-            long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
-            if (inactiveTime < ACTIVE_BUFFER) {
-                return true;
-            }
-        }
-        return false;
+    public boolean isPlaybackActive() {
+        int state = mPlaybackState == null ? PlaybackState.STATE_NONE : mPlaybackState.getState();
+        return MediaSession.isActiveState(state);
     }
 
     /**
@@ -456,7 +446,7 @@
 
     @Override
     public String toString() {
-        return mPackageName + "/" + mTag + " (uid=" + mUserId + ")";
+        return mPackageName + "/" + mTag + " (userId=" + mUserId + ")";
     }
 
     private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
@@ -817,6 +807,12 @@
         @Override
         public void setMediaButtonReceiver(PendingIntent pi) {
             mMediaButtonReceiver = pi;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
@@ -842,15 +838,19 @@
 
         @Override
         public void setPlaybackState(PlaybackState state) {
-            int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState();
-            int newState = state == null ? 0 : state.getState();
-            if (MediaSession.isActiveState(oldState) && newState == PlaybackState.STATE_PAUSED) {
-                mLastActiveTime = SystemClock.elapsedRealtime();
-            }
+            int oldState = mPlaybackState == null
+                    ? PlaybackState.STATE_NONE : mPlaybackState.getState();
+            int newState = state == null
+                    ? PlaybackState.STATE_NONE : state.getState();
             synchronized (mLock) {
                 mPlaybackState = state;
             }
-            mService.onSessionPlaystateChange(MediaSessionRecord.this, oldState, newState);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mService.onSessionPlaystateChanged(MediaSessionRecord.this, oldState, newState);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
             mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE);
         }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index ea9128f..4bf9d8f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -57,11 +57,13 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.speech.RecognizerIntent;
 import android.text.TextUtils;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -85,7 +87,7 @@
  */
 public class MediaSessionService extends SystemService implements Monitor {
     private static final String TAG = "MediaSessionService";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     // Leave log for key event always.
     private static final boolean DEBUG_KEY_EVENT = true;
 
@@ -114,6 +116,7 @@
     // It's always not null after the MediaSessionService is started.
     private FullUserRecord mCurrentFullUserRecord;
     private MediaSessionRecord mGlobalPrioritySession;
+    private AudioPlaybackMonitor mAudioPlaybackMonitor;
 
     // Used to notify system UI when remote volume was changed. TODO find a
     // better way to handle this.
@@ -134,6 +137,19 @@
         mKeyguardManager =
                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
         mAudioService = getAudioService();
+        mAudioPlaybackMonitor = new AudioPlaybackMonitor(getContext(), mAudioService,
+                new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() {
+                    @Override
+                    public void onAudioPlaybackStarted(int uid) {
+                        synchronized (mLock) {
+                            FullUserRecord user =
+                                    getFullUserRecordLocked(UserHandle.getUserId(uid));
+                            if (user != null) {
+                                user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+                            }
+                        }
+                    }
+                });
         mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mContentResolver = getContext().getContentResolver();
         mSettingsObserver = new SettingsObserver();
@@ -161,9 +177,10 @@
             user.mPriorityStack.onSessionStateChange(record);
             if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
                 mGlobalPrioritySession = record;
+                user.pushAddressedPlayerChangedLocked();
             }
+            mHandler.postSessionsChanged(record.getUserId());
         }
-        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
     }
 
     /**
@@ -180,18 +197,14 @@
         }
     }
 
-    public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
-        boolean updateSessions = false;
+    public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
         synchronized (mLock) {
             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
             if (user == null || !user.mPriorityStack.contains(record)) {
                 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
                 return;
             }
-            updateSessions = user.mPriorityStack.onPlaystateChange(record, oldState, newState);
-        }
-        if (updateSessions) {
-            mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
+            user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
         }
     }
 
@@ -332,6 +345,9 @@
         }
         if (mGlobalPrioritySession == session) {
             mGlobalPrioritySession = null;
+            if (session.isActive() && user != null) {
+                user.pushAddressedPlayerChangedLocked();
+            }
         }
 
         try {
@@ -340,8 +356,7 @@
             // ignore exceptions while destroying a session.
         }
         session.onDestroy();
-
-        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
+        mHandler.postSessionsChanged(session.getUserId());
     }
 
     private void enforcePackageName(String packageName, int uid) {
@@ -461,7 +476,7 @@
         }
 
         final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
-                callerPackageName, cb, tag, this, mHandler);
+                callerPackageName, cb, tag, this, mHandler.getLooper());
         try {
             cb.asBinder().linkToDeath(session, 0);
         } catch (RemoteException e) {
@@ -469,8 +484,7 @@
         }
 
         user.addSessionLocked(session);
-
-        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
+        mHandler.postSessionsChanged(userId);
 
         if (DEBUG) {
             Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
@@ -496,10 +510,6 @@
             }
             List<MediaSessionRecord> records = user.mPriorityStack.getActiveSessions(userId);
             int size = records.size();
-            if (size > 0 && records.get(0).isPlaybackActive(false)) {
-                user.rememberMediaButtonReceiverLocked(records.get(0));
-            }
-            user.pushAddressedPlayerChangedLocked();
             ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
             for (int i = 0; i < size; i++) {
                 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
@@ -536,6 +546,22 @@
         }
     }
 
+    /**
+     * Called when the media button receiver for the {@param record} is changed.
+     *
+     * @param record the media session whose media button receiver is updated.
+     */
+    public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            MediaSessionRecord mediaButtonSession =
+                    user.mPriorityStack.getMediaButtonSession();
+            if (record == mediaButtonSession) {
+                user.rememberMediaButtonReceiverLocked(mediaButtonSession);
+            }
+        }
+    }
+
     private String getCallingPackageName(int uid) {
         String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
@@ -568,10 +594,10 @@
      * place makes more sense and increases the readability.</p>
      * <p>The contents of this object is guarded by {@link #mLock}.
      */
-    final class FullUserRecord {
+    final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
         private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
         private final int mFullUserId;
-        private final MediaSessionStack mPriorityStack = new MediaSessionStack();
+        private final MediaSessionStack mPriorityStack;
         private PendingIntent mLastMediaButtonReceiver;
         private ComponentName mRestoredMediaButtonReceiver;
         private int mRestoredMediaButtonReceiverUserId;
@@ -588,6 +614,7 @@
 
         public FullUserRecord(int fullUserId) {
             mFullUserId = fullUserId;
+            mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this);
             // Restore the remembered media button receiver before the boot.
             String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
                     Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
@@ -603,15 +630,14 @@
         }
 
         public void destroySessionsForUserLocked(int userId) {
-            List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, 0, userId);
+            List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
             for (MediaSessionRecord session : sessions) {
                 MediaSessionService.this.destroySessionLocked(session);
             }
         }
 
         public void addSessionLocked(MediaSessionRecord session) {
-            mPriorityStack.addSession(session,
-                    mFullUserId == mFullUserIds.get(session.getUserId()));
+            mPriorityStack.addSession(session);
         }
 
         public void removeSessionLocked(MediaSessionRecord session) {
@@ -642,21 +668,41 @@
             mPriorityStack.dump(pw, indent);
         }
 
-        // Remember the media button receiver and keep it in the persistent storage.
-        private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
+        @Override
+        public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
+                MediaSessionRecord newMediaButtonSession) {
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "Media button session will be changed to " + newMediaButtonSession);
+            }
+            synchronized (mLock) {
+                if (oldMediaButtonSession != null) {
+                    mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
+                }
+                if (newMediaButtonSession != null) {
+                    rememberMediaButtonReceiverLocked(newMediaButtonSession);
+                    mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
+                }
+                pushAddressedPlayerChangedLocked();
+            }
+        }
+
+        // Remember media button receiver and keep it in the persistent storage.
+        public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
             PendingIntent receiver = record.getMediaButtonReceiver();
-            if (receiver == null) {
-                return;
-            }
             mLastMediaButtonReceiver = receiver;
-            ComponentName component = receiver.getIntent().getComponent();
-            if (component != null && record.getPackageName().equals(component.getPackageName())) {
-                String componentName = component.flattenToString();
-                Settings.Secure.putStringForUser(mContentResolver,
-                        Settings.System.MEDIA_BUTTON_RECEIVER,
-                        componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
-                        mFullUserId);
+            mRestoredMediaButtonReceiver = null;
+            String componentName = "";
+            if (receiver != null) {
+                ComponentName component = receiver.getIntent().getComponent();
+                if (component != null
+                        && record.getPackageName().equals(component.getPackageName())) {
+                    componentName = component.flattenToString();
+                }
             }
+            Settings.Secure.putStringForUser(mContentResolver,
+                    Settings.System.MEDIA_BUTTON_RECEIVER,
+                    componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
+                    mFullUserId);
         }
 
         private void pushAddressedPlayerChangedLocked() {
@@ -682,14 +728,8 @@
         }
 
         private MediaSessionRecord getMediaButtonSessionLocked() {
-            if (isGlobalPriorityActiveLocked()) {
-                return mGlobalPrioritySession;
-            }
-            // If we don't have a media button receiver to fall back on
-            // include non-playing sessions for dispatching.
-            boolean useNotPlayingSessions = (mLastMediaButtonReceiver == null
-                    && mRestoredMediaButtonReceiver == null);
-            return mPriorityStack.getDefaultMediaButtonSession(useNotPlayingSessions);
+            return isGlobalPriorityActiveLocked()
+                    ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
         }
     }
 
@@ -1262,6 +1302,7 @@
                 for (int i = 0; i < count; i++) {
                     mUserRecords.valueAt(i).dumpLocked(pw, "");
                 }
+                mAudioPlaybackMonitor.dump(pw, "");
             }
         }
 
@@ -1390,7 +1431,7 @@
                         PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
                         if (DEBUG_KEY_EVENT) {
                             Log.d(TAG, "Sending " + keyEvent
-                                    + " to the last known pendingIntent " + receiver);
+                                    + " to the last known PendingIntent " + receiver);
                         }
                         receiver.send(getContext(),
                                 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
@@ -1413,7 +1454,8 @@
                         }
                         mediaButtonIntent.setComponent(receiver);
                         getContext().sendBroadcastAsUser(mediaButtonIntent,
-                                UserHandle.of(mCurrentFullUserRecord.mRestoredMediaButtonReceiverUserId));
+                                UserHandle.of(mCurrentFullUserRecord
+                                      .mRestoredMediaButtonReceiverUserId));
                         if (mCurrentFullUserRecord.mCallback != null) {
                             mCurrentFullUserRecord.mCallback
                                     .onMediaKeyEventDispatchedToMediaButtonReceiver(
@@ -1426,23 +1468,6 @@
                 } catch (RemoteException e) {
                     Log.w(TAG, "Failed to send callback", e);
                 }
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Sending media key ordered broadcast");
-                }
-                if (needWakeLock) {
-                    mMediaEventWakeLock.acquire();
-                }
-                // Fallback to legacy behavior
-                Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
-                keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-                if (needWakeLock) {
-                    keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
-                }
-                // Send broadcast only to the full user.
-                getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
-                        null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
             }
         }
 
@@ -1637,12 +1662,13 @@
     final class MessageHandler extends Handler {
         private static final int MSG_SESSIONS_CHANGED = 1;
         private static final int MSG_VOLUME_INITIAL_DOWN = 2;
+        private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
 
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_SESSIONS_CHANGED:
-                    pushSessionsChanged(msg.arg1);
+                    pushSessionsChanged((int) msg.obj);
                     break;
                 case MSG_VOLUME_INITIAL_DOWN:
                     synchronized (mLock) {
@@ -1657,8 +1683,15 @@
             }
         }
 
-        public void post(int what, int arg1, int arg2) {
-            obtainMessage(what, arg1, arg2).sendToTarget();
+        public void postSessionsChanged(int userId) {
+            // Use object instead of the arguments when posting message to remove pending requests.
+            Integer userIdInteger = mIntegerCache.get(userId);
+            if (userIdInteger == null) {
+                userIdInteger = Integer.valueOf(userId);
+                mIntegerCache.put(userId, userIdInteger);
+            }
+            removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
+            obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
         }
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 8b80734..b0d8adc 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -16,12 +16,13 @@
 
 package com.android.server.media;
 
-import android.app.ActivityManager;
 import android.media.session.MediaController.PlaybackInfo;
-import android.media.session.PlaybackState;
 import android.media.session.MediaSession;
-import android.os.RemoteException;
+import android.media.session.PlaybackState;
+import android.os.Debug;
 import android.os.UserHandle;
+import android.util.IntArray;
+import android.util.Log;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -33,6 +34,20 @@
  * <p>This class isn't thread-safe. The caller should take care of the synchronization.
  */
 class MediaSessionStack {
+    private static final boolean DEBUG = MediaSessionService.DEBUG;
+    private static final String TAG = "MediaSessionStack";
+
+    /**
+     * Listens the change in the media button session.
+     */
+    interface OnMediaButtonSessionChangedListener {
+        /**
+         * Called when the media button session is changed.
+         */
+        void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
+                MediaSessionRecord newMediaButtonSession);
+    }
+
     /**
      * These are states that usually indicate the user took an action and should
      * bump priority regardless of the old state.
@@ -51,57 +66,45 @@
             PlaybackState.STATE_CONNECTING,
             PlaybackState.STATE_PLAYING };
 
-    private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
+    /**
+     * Sorted list of the media sessions.
+     * The session of which PlaybackState is changed to ALWAYS_PRIORITY_STATES or
+     * TRANSITION_PRIORITY_STATES comes first.
+     * @see #shouldUpdatePriority
+     */
+    private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
 
-    // The last record that either entered one of the playing states or was
-    // added.
-    private MediaSessionRecord mLastInterestingRecord;
-    private MediaSessionRecord mCachedButtonReceiver;
+    private final AudioPlaybackMonitor mAudioPlaybackMonitor;
+    private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener;
+
+    /**
+     * The media button session which receives media key events.
+     * It could be null if the previous media buttion session is released.
+     */
+    private MediaSessionRecord mMediaButtonSession;
+
     private MediaSessionRecord mCachedDefault;
     private MediaSessionRecord mCachedVolumeDefault;
     private ArrayList<MediaSessionRecord> mCachedActiveList;
-    private ArrayList<MediaSessionRecord> mCachedTransportControlList;
 
-    /**
-     * Checks if a media session is created from the most recent app.
-     *
-     * @param record A media session record to be examined.
-     * @return {@code true} if the media session's package name equals to the most recent app, false
-     *            otherwise.
-     */
-    private static boolean isFromMostRecentApp(MediaSessionRecord record) {
-        try {
-            List<ActivityManager.RecentTaskInfo> tasks =
-                    ActivityManager.getService().getRecentTasks(1,
-                            ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS |
-                            ActivityManager.RECENT_IGNORE_UNAVAILABLE |
-                            ActivityManager.RECENT_INCLUDE_PROFILES |
-                            ActivityManager.RECENT_WITH_EXCLUDED, record.getUserId()).getList();
-            if (tasks != null && !tasks.isEmpty()) {
-                ActivityManager.RecentTaskInfo recentTask = tasks.get(0);
-                if (recentTask.userId == record.getUserId() && recentTask.baseIntent != null) {
-                    return recentTask.baseIntent.getComponent().getPackageName()
-                            .equals(record.getPackageName());
-                }
-            }
-        } catch (RemoteException e) {
-            return false;
-        }
-        return false;
+    MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) {
+        mAudioPlaybackMonitor = monitor;
+        mOnMediaButtonSessionChangedListener = listener;
     }
 
     /**
      * Add a record to the priority tracker.
      *
      * @param record The record to add.
-     * @param fromForegroundUser {@code true} if the session is created by the foreground user.
      */
-    public void addSession(MediaSessionRecord record, boolean fromForegroundUser) {
+    public void addSession(MediaSessionRecord record) {
         mSessions.add(record);
         clearCache();
-        if (fromForegroundUser && isFromMostRecentApp(record)) {
-            mLastInterestingRecord = record;
-        }
+
+        // Update the media button session.
+        // The added session could be the session from the package with the audio playback.
+        // This can happen if an app starts audio playback before creating media session.
+        updateMediaButtonSessionIfNeeded();
     }
 
     /**
@@ -111,6 +114,11 @@
      */
     public void removeSession(MediaSessionRecord record) {
         mSessions.remove(record);
+        if (mMediaButtonSession == record) {
+            // When the media button session is gone, try to find the alternative media session
+            // in the media button session app.
+            onMediaSessionChangeInMediaButtonSessionApp();
+        }
         clearCache();
     }
 
@@ -122,32 +130,33 @@
     }
 
     /**
-     * Notify the priority tracker that a session's state changed.
+     * Notify the priority tracker that a session's playback state changed.
      *
      * @param record The record that changed.
      * @param oldState Its old playback state.
      * @param newState Its new playback state.
-     * @return true if the priority order was updated, false otherwise.
      */
-    public boolean onPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
+    public void onPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
         if (shouldUpdatePriority(oldState, newState)) {
             mSessions.remove(record);
             mSessions.add(0, record);
             clearCache();
-            // This becomes the last interesting record since it entered a
-            // playing state
-            mLastInterestingRecord = record;
-            return true;
         } else if (!MediaSession.isActiveState(newState)) {
             // Just clear the volume cache when a state goes inactive
             mCachedVolumeDefault = null;
         }
-        return false;
+
+        // In most cases, playback state isn't needed for finding media buttion session,
+        // but we only use it as a hint if an app has multiple local media sessions.
+        // In that case, we pick the media session whose PlaybackState matches
+        // the audio playback configuration.
+        if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) {
+            onMediaSessionChangeInMediaButtonSessionApp();
+        }
     }
 
     /**
-     * Handle any stack changes that need to occur in response to a session
-     * state change. TODO add the old and new session state as params
+     * Handle the change in activeness for a session.
      *
      * @param record The record that changed.
      */
@@ -158,6 +167,81 @@
     }
 
     /**
+     * Update the media button session if needed.
+     * <p>The media button session is the session that will receive the media button events.
+     * <p>We send the media button events to the lastly played app. If the app has the media
+     * session, the session will receive the media button events.
+     */
+    public void updateMediaButtonSessionIfNeeded() {
+        if (DEBUG) {
+            Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
+        }
+        IntArray audioPlaybackUids = mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids();
+        for (int i = 0; i < audioPlaybackUids.size(); i++) {
+            MediaSessionRecord mediaButtonSession =
+                    findMediaButtonSession(audioPlaybackUids.get(i));
+            if (mediaButtonSession != null) {
+                // Found the media button session.
+                mAudioPlaybackMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid());
+                if (mMediaButtonSession != mediaButtonSession) {
+                    mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
+                            mMediaButtonSession, mediaButtonSession);
+                    mMediaButtonSession = mediaButtonSession;
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Handle the change in a media session in the media button session app.
+     * <p>If the app has multiple media sessions, change in a media sesion in the app may change
+     * the media button session.
+     * @see #findMediaButtonSession
+     */
+    private void onMediaSessionChangeInMediaButtonSessionApp() {
+        MediaSessionRecord newMediaButtonSession =
+                findMediaButtonSession(mMediaButtonSession.getUid());
+        if (newMediaButtonSession != mMediaButtonSession) {
+            mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(mMediaButtonSession,
+                    newMediaButtonSession);
+            mMediaButtonSession = newMediaButtonSession;
+        }
+    }
+
+    /**
+     * Find the media button session with the given {@param uid}.
+     * If the app has multiple media sessions, the media session matches the audio playback state
+     * becomes the media button session.
+     *
+     * @return The media button session. Returns {@code null} if the app doesn't have a media
+     *   session.
+     */
+    private MediaSessionRecord findMediaButtonSession(int uid) {
+        MediaSessionRecord mediaButtonSession = null;
+        for (MediaSessionRecord session : mSessions) {
+            // Since the media buttons come with the headset/speaker, users will expect changes in
+            // the headset/speaker when they press media buttons. So only consider local playback
+            // for the media button session.
+            if (uid == session.getUid()
+                    && session.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
+                if (session.isPlaybackActive() ==
+                        mAudioPlaybackMonitor.isPlaybackActive(session.getUid())) {
+                    // If there's a media session whose PlaybackState matches
+                    // the audio playback state, return it immediately.
+                    return session;
+                }
+                if (mediaButtonSession == null) {
+                    // Among the media sessions whose PlaybackState doesn't match
+                    // the audio playback state, pick the top priority.
+                    mediaButtonSession = session;
+                }
+            }
+        }
+        return mediaButtonSession;
+    }
+
+    /**
      * Get the current priority sorted list of active sessions. The most
      * important session is at index 0 and the least important at size - 1.
      *
@@ -166,57 +250,29 @@
      */
     public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
         if (mCachedActiveList == null) {
-            mCachedActiveList = getPriorityList(true, 0, userId);
+            mCachedActiveList = getPriorityList(true, userId);
         }
         return mCachedActiveList;
     }
 
     /**
-     * Get the highest priority session that can handle media buttons.
+     * Get the media button session which receives the media button events.
      *
-     * @param includeNotPlaying Return a non-playing session if nothing else is
-     *            available
-     * @return The default media button session or null.
+     * @return The media button session or null.
      */
-    public MediaSessionRecord getDefaultMediaButtonSession(boolean includeNotPlaying) {
-        if (mCachedButtonReceiver != null) {
-            return mCachedButtonReceiver;
-        }
-        ArrayList<MediaSessionRecord> records = getPriorityList(true,
-                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, UserHandle.USER_ALL);
-        if (records.size() > 0) {
-            MediaSessionRecord record = records.get(0);
-            if (record.isPlaybackActive(false)) {
-                // Since we're going to send a button event to this record make
-                // it the last interesting one.
-                mLastInterestingRecord = record;
-                mCachedButtonReceiver = record;
-            } else if (mLastInterestingRecord != null) {
-                if (records.contains(mLastInterestingRecord)) {
-                    mCachedButtonReceiver = mLastInterestingRecord;
-                } else {
-                    // That record is no longer used. Clear its reference.
-                    mLastInterestingRecord = null;
-                }
-            }
-            if (includeNotPlaying && mCachedButtonReceiver == null) {
-                // If we really want a record and we didn't find one yet use the
-                // highest priority session even if it's not playing.
-                mCachedButtonReceiver = record;
-            }
-        }
-        return mCachedButtonReceiver;
+    public MediaSessionRecord getMediaButtonSession() {
+        return mMediaButtonSession;
     }
 
     public MediaSessionRecord getDefaultVolumeSession() {
         if (mCachedVolumeDefault != null) {
             return mCachedVolumeDefault;
         }
-        ArrayList<MediaSessionRecord> records = getPriorityList(true, 0, UserHandle.USER_ALL);
+        ArrayList<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL);
         int size = records.size();
         for (int i = 0; i < size; i++) {
             MediaSessionRecord record = records.get(i);
-            if (record.isPlaybackActive(false)) {
+            if (record.isPlaybackActive()) {
                 mCachedVolumeDefault = record;
                 return record;
             }
@@ -225,7 +281,7 @@
     }
 
     public MediaSessionRecord getDefaultRemoteSession(int userId) {
-        ArrayList<MediaSessionRecord> records = getPriorityList(true, 0, userId);
+        ArrayList<MediaSessionRecord> records = getPriorityList(true, userId);
 
         int size = records.size();
         for (int i = 0; i < size; i++) {
@@ -238,9 +294,10 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        ArrayList<MediaSessionRecord> sortedSessions = getPriorityList(false, 0,
+        ArrayList<MediaSessionRecord> sortedSessions = getPriorityList(false,
                 UserHandle.USER_ALL);
         int count = sortedSessions.size();
+        pw.println(prefix + "Media button session is " + mMediaButtonSession);
         pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
         String indent = prefix + "  ";
         for (int i = 0; i < count; i++) {
@@ -252,22 +309,23 @@
 
     /**
      * Get a priority sorted list of sessions. Can filter to only return active
-     * sessions or sessions with specific flags.
+     * sessions or sessions.
+     * <p>Here's the priority order.
+     * <li>System priority session (session with FLAG_EXCLUSIVE_GLOBAL_PRIORITY)</li>
+     * <li>Active sessions whose PlaybackState is active</li>
+     * <li>Active sessions whose PlaybackState is inactive</li>
+     * <li>Inactive sessions</li>
      *
      * @param activeOnly True to only return active sessions, false to return
      *            all sessions.
-     * @param withFlags Only return sessions with all the specified flags set. 0
-     *            returns all sessions.
      * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
      *            will return sessions for all users.
      * @return The priority sorted list of sessions.
      */
-    public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int withFlags,
-            int userId) {
+    public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) {
         ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
-        int lastLocalIndex = 0;
+        int lastPlaybackActiveIndex = 0;
         int lastActiveIndex = 0;
-        int lastPublishedIndex = 0;
 
         int size = mSessions.size();
         for (int i = 0; i < size; i++) {
@@ -277,10 +335,7 @@
                 // Filter out sessions for the wrong user
                 continue;
             }
-            if ((session.getFlags() & withFlags) != withFlags) {
-                // Filter out sessions with the wrong flags
-                continue;
-            }
+
             if (!session.isActive()) {
                 if (!activeOnly) {
                     // If we're getting unpublished as well always put them at
@@ -294,28 +349,13 @@
                 // System priority sessions are special and always go at the
                 // front. We expect there to only be one of these at a time.
                 result.add(0, session);
-                lastLocalIndex++;
+                lastPlaybackActiveIndex++;
                 lastActiveIndex++;
-                lastPublishedIndex++;
-            } else if (session.isPlaybackActive(true)) {
-                // TODO this with real local route check
-                if (true) {
-                    // Active local sessions get top priority
-                    result.add(lastLocalIndex, session);
-                    lastLocalIndex++;
-                    lastActiveIndex++;
-                    lastPublishedIndex++;
-                } else {
-                    // Then active remote sessions
-                    result.add(lastActiveIndex, session);
-                    lastActiveIndex++;
-                    lastPublishedIndex++;
-                }
+            } else if (session.isPlaybackActive()) {
+                result.add(lastPlaybackActiveIndex++, session);
+                lastActiveIndex++;
             } else {
-                // inactive sessions go at the end in order of whoever last did
-                // something.
-                result.add(lastPublishedIndex, session);
-                lastPublishedIndex++;
+                result.add(lastActiveIndex++, session);
             }
         }
 
@@ -345,8 +385,6 @@
     private void clearCache() {
         mCachedDefault = null;
         mCachedVolumeDefault = null;
-        mCachedButtonReceiver = null;
         mCachedActiveList = null;
-        mCachedTransportControlList = null;
     }
 }