OSDN Git Service

Remove MediaController2 usages in MediaSessionServiceImpl
authorHyundo Moon <hdmoon@google.com>
Thu, 24 Jan 2019 09:43:28 +0000 (18:43 +0900)
committerHyundo Moon <hdmoon@google.com>
Fri, 25 Jan 2019 09:52:44 +0000 (18:52 +0900)
Bug: 123336670
Test: Passed below tests
      MediaSessionManagerTest#testNotifySession2Created/Destroyed*
Change-Id: I25a20bf9e8a78920f2219d22b81b899af665ed32

api/current.txt
api/system-current.txt
media/Android.bp
media/apex/java/android/media/MediaConstants.java
media/apex/java/android/media/MediaController2.java
media/apex/java/android/media/MediaSession2.java
media/apex/java/android/media/MediaSession2Service.java
media/java/android/media/Session2Token.java [moved from media/apex/java/android/media/Session2Token.java with 70% similarity]
media/java/android/media/session/ISessionManager.aidl
media/java/android/media/session/MediaSessionManager.java
services/core/java/com/android/server/media/MediaSessionServiceImpl.java

index ea4966b..b897877 100644 (file)
@@ -25936,7 +25936,6 @@ package android.media {
     method @NonNull public abstract android.media.MediaSession2 onGetPrimarySession();
     method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2);
     method public final void removeSession(@NonNull android.media.MediaSession2);
-    field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
   }
 
   public static class MediaSession2Service.MediaNotification {
@@ -27459,6 +27458,7 @@ package android.media.session {
     method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens();
     method public boolean isTrustedForMediaControl(@NonNull android.media.session.MediaSessionManager.RemoteUserInfo);
     method public void notifySession2Created(@NonNull android.media.Session2Token);
+    method public void notifySession2Destroyed(@NonNull android.media.Session2Token);
     method public void removeOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
     method public void removeOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
   }
index 3466e2e..7700ecb 100644 (file)
@@ -3381,6 +3381,15 @@ package android.media {
     method public void stop();
   }
 
+  public final class Session2Token implements android.os.Parcelable {
+    ctor public Session2Token(@NonNull android.content.Context, @NonNull String, @Nullable android.os.Bundle);
+    method public void destroy();
+    method @NonNull public android.os.Bundle getExtras();
+    method public int getPid();
+    method public boolean isDestroyed();
+    field public static final String SESSION_SERVICE_INTERFACE = "android.media.MediaSession2Service";
+  }
+
   public static class SubtitleData.Builder {
     ctor public SubtitleData.Builder();
     ctor public SubtitleData.Builder(@NonNull android.media.SubtitleData);
index 88ed9c6..0675a36 100644 (file)
@@ -132,7 +132,6 @@ filegroup {
         "apex/java/android/media/Session2Command.java",
         "apex/java/android/media/Session2CommandGroup.java",
         "apex/java/android/media/Session2Link.java",
-        "apex/java/android/media/Session2Token.java",
     ],
 }
 
index 65b6f55..45ea826 100644 (file)
@@ -24,7 +24,8 @@ class MediaConstants {
     static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
 
     // Bundle key for Parcelable
-    static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
+    static final String KEY_SESSION2_TOKEN = "android.media.key.SESSION2_TOKEN";
+    static final String KEY_SESSION2_LINK = "android.media.key.SESSION2_LINK";
     static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
     static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE";
 
index 887b447..41721f3 100644 (file)
@@ -20,9 +20,11 @@ import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
 import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
-import static android.media.MediaConstants.KEY_SESSION2LINK;
+import static android.media.MediaConstants.KEY_SESSION2_LINK;
+import static android.media.MediaConstants.KEY_SESSION2_TOKEN;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
+import static android.media.Session2Token.SESSION_SERVICE_INTERFACE;
 import static android.media.Session2Token.TYPE_SESSION;
 
 import android.annotation.NonNull;
@@ -260,7 +262,8 @@ public class MediaController2 implements AutoCloseable {
 
     // Called by Controller2Link.onConnected
     void onConnected(int seq, Bundle connectionResult) {
-        Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
+        Session2Token token = connectionResult.getParcelable(KEY_SESSION2_TOKEN);
+        Session2Link sessionBinder = token.getExtras().getParcelable(KEY_SESSION2_LINK);
         Session2CommandGroup allowedCommands =
                 connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
         boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE);
@@ -281,8 +284,7 @@ public class MediaController2 implements AutoCloseable {
             // Implementation for the local binder is no-op,
             // so can be used without worrying about deadlock.
             sessionBinder.linkToDeath(mDeathRecipient, 0);
-            mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
-                    mSessionToken.getPackageName(), sessionBinder);
+            mConnectedToken = token;
         }
         mCallbackExecutor.execute(() -> {
             mCallback.onConnected(MediaController2.this, allowedCommands);
@@ -353,7 +355,7 @@ public class MediaController2 implements AutoCloseable {
     }
 
     private boolean requestConnectToSession() {
-        Session2Link sessionBinder = mSessionToken.getSessionLink();
+        Session2Link sessionBinder = mSessionToken.getExtras().getParcelable(KEY_SESSION2_LINK);
         Bundle connectionRequest = createConnectionRequest();
         try {
             sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
@@ -366,7 +368,7 @@ public class MediaController2 implements AutoCloseable {
 
     private boolean requestConnectToService() {
         // Service. Needs to get fresh binder whenever connection is needed.
-        final Intent intent = new Intent(MediaSession2Service.SERVICE_INTERFACE);
+        final Intent intent = new Intent(SESSION_SERVICE_INTERFACE);
         intent.setClassName(mSessionToken.getPackageName(), mSessionToken.getServiceName());
 
         // Use bindService() instead of startForegroundService() to start session service for three
index fdd07fd..80c91cc 100644 (file)
@@ -20,10 +20,10 @@ import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
 import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
-import static android.media.MediaConstants.KEY_SESSION2LINK;
+import static android.media.MediaConstants.KEY_SESSION2_LINK;
+import static android.media.MediaConstants.KEY_SESSION2_TOKEN;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
-import static android.media.Session2Token.TYPE_SESSION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,7 +34,6 @@ import android.media.session.MediaSessionManager;
 import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Process;
 import android.os.ResultReceiver;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -108,8 +107,10 @@ public class MediaSession2 implements AutoCloseable {
         mCallbackExecutor = callbackExecutor;
         mCallback = callback;
         mSessionStub = new Session2Link(this);
-        mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
-                mSessionStub);
+
+        Bundle extras = new Bundle();
+        extras.putParcelable(KEY_SESSION2_LINK, mSessionStub);
+        mSessionToken = new Session2Token(context, id, extras);
         mSessionManager = (MediaSessionManager) mContext.getSystemService(
                 Context.MEDIA_SESSION_SERVICE);
         // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
@@ -141,6 +142,8 @@ public class MediaSession2 implements AutoCloseable {
             for (ControllerInfo info : controllerInfos) {
                 info.notifyDisconnected();
             }
+            mSessionToken.destroy();
+            mSessionManager.notifySession2Destroyed(mSessionToken);
         } catch (Exception e) {
             // Should not be here.
         }
@@ -328,7 +331,7 @@ public class MediaSession2 implements AutoCloseable {
                 // It's needed because we cannot call synchronous calls between
                 // session/controller.
                 Bundle connectionResult = new Bundle();
-                connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
+                connectionResult.putParcelable(KEY_SESSION2_TOKEN, mSessionToken);
                 connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
                         controllerInfo.mAllowedCommands);
                 connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive());
index 5bb746a..f18cd31 100644 (file)
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.Session2Token.SESSION_SERVICE_INTERFACE;
+
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -45,10 +47,6 @@ import java.util.Map;
  * for consistent behavior across all devices.
  */
 public abstract class MediaSession2Service extends Service {
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     */
-    public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
 
     private static final String TAG = "MediaSession2Service";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -100,7 +98,7 @@ public abstract class MediaSession2Service extends Service {
     @Override
     @Nullable
     public IBinder onBind(@NonNull Intent intent) {
-        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+        if (SESSION_SERVICE_INTERFACE.equals(intent.getAction())) {
             synchronized (mLock) {
                 return mStub;
             }
@@ -19,13 +19,17 @@ package android.media;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Process;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -35,7 +39,7 @@ import java.util.List;
 import java.util.Objects;
 
 /**
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
+ * Represents an ongoing MediaSession2 or a MediaSession2Service.
  * If it's representing a session service, it may not be ongoing.
  * <p>
  * This API is not generally intended for third party application developers.
@@ -44,7 +48,7 @@ import java.util.Objects;
  * for consistent behavior across all devices.
  * <p>
  * This may be passed to apps by the session owner to allow them to create a
- * {@link MediaController2} to communicate with the session.
+ * MediaController2 to communicate with the session.
  * <p>
  * It can be also obtained by {@link android.media.session.MediaSessionManager}.
  */
@@ -64,6 +68,13 @@ public final class Session2Token implements Parcelable {
     };
 
     /**
+     * The {@link Intent} that must be declared for the session service.
+     * @hide
+     */
+    @SystemApi
+    public static final String SESSION_SERVICE_INTERFACE = "android.media.MediaSession2Service";
+
+    /**
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -72,22 +83,26 @@ public final class Session2Token implements Parcelable {
     }
 
     /**
-     * Type for {@link MediaSession2}.
+     * Type for MediaSession2.
      */
     public static final int TYPE_SESSION = 0;
 
     /**
-     * Type for {@link MediaSession2Service}.
+     * Type for MediaSession2Service.
      */
     public static final int TYPE_SESSION_SERVICE = 1;
 
+    private final String mSessionId;
+    private final int mPid;
     private final int mUid;
     @TokenType
     private final int mType;
     private final String mPackageName;
     private final String mServiceName;
-    private final Session2Link mSessionLink;
     private final ComponentName mComponentName;
+    private final Bundle mExtras;
+
+    private boolean mDestroyed = false;
 
     /**
      * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
@@ -106,44 +121,67 @@ public final class Session2Token implements Parcelable {
         final PackageManager manager = context.getPackageManager();
         final int uid = getUid(manager, serviceComponent.getPackageName());
 
-        if (!isInterfaceDeclared(manager, MediaSession2Service.SERVICE_INTERFACE,
-                serviceComponent)) {
+        if (!isInterfaceDeclared(manager, SESSION_SERVICE_INTERFACE, serviceComponent)) {
             Log.w(TAG, serviceComponent + " doesn't implement MediaSession2Service.");
         }
+        mSessionId = null;
         mComponentName = serviceComponent;
         mPackageName = serviceComponent.getPackageName();
         mServiceName = serviceComponent.getClassName();
+        mPid = -1;
         mUid = uid;
         mType = TYPE_SESSION_SERVICE;
-        mSessionLink = null;
+        mExtras = null;
     }
 
-    Session2Token(int uid, int type, String packageName, Session2Link sessionLink) {
-        mUid = uid;
-        mType = type;
-        mPackageName = packageName;
+    /**
+     * Constructor for the token with type {@link #TYPE_SESSION}.
+     *
+     * @param context The context.
+     * @param sessionId The ID of the session. Should be unique.
+     * @param extras The extras.
+     * @hide
+     */
+    @SystemApi
+    public Session2Token(@NonNull Context context, @NonNull String sessionId,
+            @Nullable Bundle extras) {
+        if (sessionId == null) {
+            throw new IllegalArgumentException("sessionId shouldn't be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("context shouldn't be null");
+        }
+        mSessionId = sessionId;
+        mPid = Process.myPid();
+        mUid = Process.myUid();
+        mType = TYPE_SESSION;
+        mPackageName = context.getPackageName();
+        mExtras = extras;
         mServiceName = null;
         mComponentName = null;
-        mSessionLink = sessionLink;
     }
 
     Session2Token(Parcel in) {
+        mSessionId = in.readString();
+        mPid = in.readInt();
         mUid = in.readInt();
         mType = in.readInt();
         mPackageName = in.readString();
         mServiceName = in.readString();
-        mSessionLink = in.readParcelable(null);
         mComponentName = ComponentName.unflattenFromString(in.readString());
+        mExtras = in.readParcelable(null);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mSessionId);
+        dest.writeInt(mPid);
         dest.writeInt(mUid);
         dest.writeInt(mType);
         dest.writeString(mPackageName);
         dest.writeString(mServiceName);
-        dest.writeParcelable(mSessionLink, flags);
         dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
+        dest.writeParcelable(mExtras, flags);
     }
 
     @Override
@@ -153,7 +191,7 @@ public final class Session2Token implements Parcelable {
 
     @Override
     public int hashCode() {
-        return Objects.hash(mType, mUid, mPackageName, mServiceName, mSessionLink);
+        return Objects.hash(mSessionId, mPid, mUid, mType, mPackageName, mServiceName);
     }
 
     @Override
@@ -162,17 +200,27 @@ public final class Session2Token implements Parcelable {
             return false;
         }
         Session2Token other = (Session2Token) obj;
-        return mUid == other.mUid
-                && TextUtils.equals(mPackageName, other.mPackageName)
-                && TextUtils.equals(mServiceName, other.mServiceName)
+        return TextUtils.equals(mSessionId, other.mSessionId)
+                && mPid == other.mPid
+                && mUid == other.mUid
                 && mType == other.mType
-                && Objects.equals(mSessionLink, other.mSessionLink);
+                && TextUtils.equals(mPackageName, other.mPackageName)
+                && TextUtils.equals(mServiceName, other.mServiceName);
     }
 
     @Override
     public String toString() {
         return "Session2Token {pkg=" + mPackageName + " type=" + mType
-                + " service=" + mServiceName + " Session2Link=" + mSessionLink + "}";
+                + " service=" + mServiceName + "}";
+    }
+
+    /**
+     * @return pid of the session
+     * @hide
+     */
+    @SystemApi
+    public int getPid() {
+        return mPid;
     }
 
     /**
@@ -207,8 +255,36 @@ public final class Session2Token implements Parcelable {
         return mType;
     }
 
-    Session2Link getSessionLink() {
-        return mSessionLink;
+    /**
+     * @return extras
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras == null ? new Bundle() : new Bundle(mExtras);
+    }
+
+    /**
+     * Destroys this session token. After this method is called,
+     * {@link MediaSessionManager#notifySession2Created(Session2Token)} should not be called
+     * with this token.
+     *
+     * @see MediaSessionManager#notifySession2Created(Session2Token)
+     * @hide
+     */
+    @SystemApi
+    public void destroy() {
+        mDestroyed = true;
+    }
+
+    /**
+     * @return whether this token is destroyed
+     * @hide
+     */
+    @SystemApi
+    public boolean isDestroyed() {
+        return mDestroyed;
     }
 
     private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface,
index ed16250..fa6e034 100644 (file)
@@ -37,6 +37,7 @@ interface ISessionManager {
     SessionLink createSession(String packageName, in SessionCallbackLink sessionCb, String tag,
             int userId);
     void notifySession2Created(in Session2Token sessionToken);
+    void notifySession2Destroyed(in Session2Token sessionToken);
     List<ControllerLink> getSessions(in ComponentName compName, int userId);
     List<Session2Token> getSession2Tokens(int userId);
     void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
index c64c452..cae4d17 100644 (file)
@@ -26,7 +26,6 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
-import android.media.MediaSession2;
 import android.media.Session2Token;
 import android.os.Handler;
 import android.os.IBinder;
@@ -115,11 +114,11 @@ public final class MediaSessionManager {
     }
 
     /**
-     * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
+     * Notifies that a new MediaSession2 with type {@link Session2Token#TYPE_SESSION} is
      * created.
      * <p>
      * Do not use this API directly, but create a new instance through the
-     * {@link MediaSession2.Builder} instead.
+     * MediaSession2.Builder instead.
      *
      * @param token newly created session2 token
      */
@@ -130,6 +129,9 @@ public final class MediaSessionManager {
         if (token.getType() != Session2Token.TYPE_SESSION) {
             throw new IllegalArgumentException("token's type should be TYPE_SESSION");
         }
+        if (token.isDestroyed()) {
+            throw new IllegalArgumentException("token is already destroyed");
+        }
         try {
             mService.notifySession2Created(token);
         } catch (RemoteException e) {
@@ -138,6 +140,31 @@ public final class MediaSessionManager {
     }
 
     /**
+     * Notifies that a new MediaSession2 with type {@link Session2Token#TYPE_SESSION} is
+     * destroyed.
+     * <p>
+     * Do not use this API directly, but close a session with MediaSession2#close() instead.
+     *
+     * @param token destroyed session2 token
+     */
+    public void notifySession2Destroyed(@NonNull Session2Token token) {
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        if (token.getType() != Session2Token.TYPE_SESSION) {
+            throw new IllegalArgumentException("token's type should be TYPE_SESSION");
+        }
+        if (!token.isDestroyed()) {
+            throw new IllegalArgumentException("token should have been destroyed");
+        }
+        try {
+            mService.notifySession2Destroyed(token);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get a list of controllers for all ongoing sessions. The controllers will
      * be provided in priority order with the most important controller at index
      * 0.
@@ -192,7 +219,7 @@ public final class MediaSessionManager {
      * current user.
      * <p>
      * Although this API can be used without any restriction, each session owners can accept or
-     * reject your uses of {@link MediaSession2}.
+     * reject your uses of MediaSession2.
      *
      * @return A list of {@link Session2Token}.
      */
index 1541b1d..dd26a29 100644 (file)
@@ -42,8 +42,6 @@ import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
-import android.media.MediaController2;
-import android.media.Session2CommandGroup;
 import android.media.Session2Token;
 import android.media.session.ControllerLink;
 import android.media.session.IActiveSessionsListener;
@@ -60,7 +58,6 @@ import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
@@ -1007,17 +1004,50 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
                 if (DEBUG) {
                     Log.d(TAG, "Session2 is created " + sessionToken);
                 }
+                if (pid != sessionToken.getPid()) {
+                    throw new SecurityException("Unexpected Session2Token's PID, expected=" + pid
+                            + " but actually=" + sessionToken.getPid());
+                }
+                if (uid != sessionToken.getUid()) {
+                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+                            + " but actually=" + sessionToken.getUid());
+                }
+                int userId = UserHandle.getUserId(uid);
+                List<Session2Token> session2Tokens = mSession2TokensPerUser.get(userId);
+                if (session2Tokens.contains(sessionToken)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "notifySession2Created(): Ignoring already existing token "
+                                + sessionToken);
+                    }
+                    return;
+                }
+                session2Tokens.add(sessionToken);
+                pushSession2TokensChangedLocked(userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void notifySession2Destroyed(Session2Token sessionToken) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "Session2 is destroyed " + sessionToken);
+                }
+                if (pid != sessionToken.getPid()) {
+                    throw new SecurityException("Unexpected Session2Token's PID, expected=" + pid
+                            + " but actually=" + sessionToken.getPid());
+                }
                 if (uid != sessionToken.getUid()) {
                     throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
                             + " but actually=" + sessionToken.getUid());
                 }
-                Controller2Callback callback = new Controller2Callback(sessionToken);
-                // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
-                //       it's closed.
-                // TODO: Keep controller as well for better readability
-                //       because the GC behavior isn't straightforward.
-                MediaController2 controller = new MediaController2(mContext, sessionToken,
-                        new HandlerExecutor(mHandler), callback);
+                int userId = UserHandle.getUserId(uid);
+                mSession2TokensPerUser.get(userId).remove(sessionToken);
+                pushSession2TokensChangedLocked(userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2114,30 +2144,4 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
             obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
         }
     }
-
-    private class Controller2Callback extends MediaController2.ControllerCallback {
-        private final Session2Token mToken;
-
-        Controller2Callback(Session2Token token) {
-            mToken = token;
-        }
-
-        @Override
-        public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
-            synchronized (mLock) {
-                int userId = UserHandle.getUserId(mToken.getUid());
-                mSession2TokensPerUser.get(userId).add(mToken);
-                pushSession2TokensChangedLocked(userId);
-            }
-        }
-
-        @Override
-        public void onDisconnected(MediaController2 controller) {
-            synchronized (mLock) {
-                int userId = UserHandle.getUserId(mToken.getUid());
-                mSession2TokensPerUser.get(userId).remove(mToken);
-                pushSession2TokensChangedLocked(userId);
-            }
-        }
-    }
 }