MediaSessionService: Cache active sessions per an user

This fixes an issue that MediaSessionManager.getActiveSessions() may
return media sessions of the wrong user. This had been an issue since
the Android M.

Bug: 36227938
Test: Run the CtsMediaTest (MediaSessionTest and MediaSessionManagerTest)
  and CtsMediaHostTestCases
Change-Id: I8a8297e4d182f52c84f654bdb0ba78bc16a1c058
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index b0d8adc..2f82915 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -23,6 +23,7 @@
 import android.os.UserHandle;
 import android.util.IntArray;
 import android.util.Log;
+import android.util.SparseArray;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -38,7 +39,7 @@
     private static final String TAG = "MediaSessionStack";
 
     /**
-     * Listens the change in the media button session.
+     * Listen the change in the media button session.
      */
     interface OnMediaButtonSessionChangedListener {
         /**
@@ -85,7 +86,12 @@
 
     private MediaSessionRecord mCachedDefault;
     private MediaSessionRecord mCachedVolumeDefault;
-    private ArrayList<MediaSessionRecord> mCachedActiveList;
+
+    /**
+     * Cache the result of the {@link #getActiveSessions} per user.
+     */
+    private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists =
+            new SparseArray<>();
 
     MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) {
         mAudioPlaybackMonitor = monitor;
@@ -99,7 +105,7 @@
      */
     public void addSession(MediaSessionRecord record) {
         mSessions.add(record);
-        clearCache();
+        clearCache(record.getUserId());
 
         // Update the media button session.
         // The added session could be the session from the package with the audio playback.
@@ -119,7 +125,7 @@
             // in the media button session app.
             onMediaSessionChangeInMediaButtonSessionApp();
         }
-        clearCache();
+        clearCache(record.getUserId());
     }
 
     /**
@@ -140,7 +146,7 @@
         if (shouldUpdatePriority(oldState, newState)) {
             mSessions.remove(record);
             mSessions.add(0, record);
-            clearCache();
+            clearCache(record.getUserId());
         } else if (!MediaSession.isActiveState(newState)) {
             // Just clear the volume cache when a state goes inactive
             mCachedVolumeDefault = null;
@@ -163,7 +169,7 @@
     public void onSessionStateChange(MediaSessionRecord record) {
         // For now just clear the cache. Eventually we'll selectively clear
         // depending on what changed.
-        clearCache();
+        clearCache(record.getUserId());
     }
 
     /**
@@ -245,14 +251,17 @@
      * Get the current priority sorted list of active sessions. The most
      * important session is at index 0 and the least important at size - 1.
      *
-     * @param userId The user to check.
+     * @param userId The user to check. It can be {@link UserHandle#USER_ALL} to get all sessions
+     *    for all users in this {@link MediaSessionStack}.
      * @return All the active sessions in priority order.
      */
     public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
-        if (mCachedActiveList == null) {
-            mCachedActiveList = getPriorityList(true, userId);
+        ArrayList<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId);
+        if (cachedActiveList == null) {
+            cachedActiveList = getPriorityList(true, userId);
+            mCachedActiveLists.put(userId, cachedActiveList);
         }
-        return mCachedActiveList;
+        return cachedActiveList;
     }
 
     /**
@@ -382,9 +391,12 @@
         return false;
     }
 
-    private void clearCache() {
+    private void clearCache(int userId) {
         mCachedDefault = null;
         mCachedVolumeDefault = null;
-        mCachedActiveList = null;
+        mCachedActiveLists.remove(userId);
+        // mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL,
+        // so they also need to be cleared.
+        mCachedActiveLists.remove(UserHandle.USER_ALL);
     }
 }