MediaSession2: Fix timing issue

Session/Controller needs mProvider. However, if the createProvider()
interacts with other components, than other components may use session
/controller object before mProvider is set.

This CL prevents such issues by calling initialize() to communicate
with other components after the provider is set.

Test: Run all MediaComponents test once
Change-Id: Ic6eb1a7f96a2084b3a011da30a5053aff5620977
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index a90ce8d..dba5e85 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -92,7 +92,6 @@
     public MediaController2Impl(Context context, MediaController2 instance, SessionToken2 token,
             Executor executor, ControllerCallback callback) {
         mInstance = instance;
-
         if (context == null) {
             throw new IllegalArgumentException("context shouldn't be null");
         }
@@ -115,21 +114,28 @@
         };
 
         mSessionBinder = null;
+    }
 
-        if (token.getSessionBinder() == null) {
+    @Override
+    public void initialize() {
+        SessionToken2Impl impl = SessionToken2Impl.from(mToken);
+        // TODO(jaewan): More sanity checks.
+        if (impl.getSessionBinder() == null) {
+            // Session service
             mServiceConnection = new SessionServiceConnection();
             connectToService();
         } else {
+            // Session
             mServiceConnection = null;
-            connectToSession(token.getSessionBinder());
+            connectToSession(impl.getSessionBinder());
         }
     }
 
-    // Should be only called by constructor.
     private void connectToService() {
         // Service. Needs to get fresh binder whenever connection is needed.
+        SessionToken2Impl impl = SessionToken2Impl.from(mToken);
         final Intent intent = new Intent(MediaSessionService2.SERVICE_INTERFACE);
-        intent.setClassName(mToken.getPackageName(), mToken.getServiceName());
+        intent.setClassName(mToken.getPackageName(), impl.getServiceName());
 
         // Use bindService() instead of startForegroundService() to start session service for three
         // reasons.
@@ -166,7 +172,7 @@
     @Override
     public void close_impl() {
         if (DEBUG) {
-            Log.d(TAG, "relese from " + mToken);
+            Log.d(TAG, "release from " + mToken);
         }
         final IMediaSession2 binder;
         synchronized (mLock) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index d713f78..a5c9129 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -26,6 +26,7 @@
 import android.media.MediaSession2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSessionService2;
+import android.media.SessionToken2;
 import android.media.VolumeProvider;
 import android.media.update.MediaLibraryService2Provider;
 import android.os.Bundle;
@@ -55,10 +56,8 @@
     }
 
     @Override
-    Intent createServiceIntent() {
-        Intent serviceIntent = new Intent(mInstance, mInstance.getClass());
-        serviceIntent.setAction(MediaLibraryService2.SERVICE_INTERFACE);
-        return serviceIntent;
+    int getSessionType() {
+        return SessionToken2.TYPE_LIBRARY_SERVICE;
     }
 
     public static class MediaLibrarySessionImpl extends MediaSession2Impl
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 600fc95..e173712 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -16,14 +16,21 @@
 
 package com.android.media;
 
+import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
+import static android.media.SessionToken2.TYPE_SESSION;
+import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
+
 import android.Manifest.permission;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.media.AudioAttributes;
 import android.media.IMediaSession2Callback;
 import android.media.MediaItem2;
+import android.media.MediaLibraryService2;
 import android.media.MediaPlayerInterface;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Builder;
@@ -33,15 +40,18 @@
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
+import android.media.MediaSessionService2;
 import android.media.PlaybackState2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider;
 import android.media.session.MediaSessionManager;
 import android.media.update.MediaSession2Provider;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.support.annotation.GuardedBy;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.lang.ref.WeakReference;
@@ -95,25 +105,69 @@
         mCallback = callback;
         mCallbackExecutor = callbackExecutor;
         mSessionStub = new MediaSession2Stub(this);
-        // Ask server to create session token for following reasons.
-        //   1. Make session ID unique per package.
-        //      Server can only know if the package has another process and has another session
-        //      with the same id. Let server check this.
-        //      Note that 'ID is unique per package' is important for controller to distinguish
-        //      a session in another package.
-        //   2. Easier to know the type of session.
-        //      Session created here can be the session service token. In order distinguish,
-        //      we need to iterate AndroidManifest.xml but it's already done by the server.
-        //      Let server to create token with the type.
+
+        // Infer type from the id and package name.
+        String sessionService = getServiceName(context, MediaSessionService2.SERVICE_INTERFACE, id);
+        String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id);
+        if (sessionService != null && libraryService != null) {
+            throw new IllegalArgumentException("Ambiguous session type. Multiple"
+                    + " session services define the same id=" + id);
+        } else if (sessionService != null) {
+            mSessionToken = new SessionToken2(context, Process.myUid(), TYPE_SESSION_SERVICE,
+                    mContext.getPackageName(), sessionService, id, mSessionStub);
+        } else if (libraryService != null) {
+            mSessionToken = new SessionToken2(context, Process.myUid(), TYPE_LIBRARY_SERVICE,
+                    mContext.getPackageName(), libraryService, id, mSessionStub);
+        } else {
+            mSessionToken = new SessionToken2(context, Process.myUid(), TYPE_SESSION,
+                    mContext.getPackageName(), null, id, mSessionStub);
+        }
+
+        // Only remember player. Actual settings will be done in the initialize().
+        mPlayer = player;
+    }
+
+    private static String getServiceName(Context context, String serviceAction, String id) {
+        PackageManager manager = context.getPackageManager();
+        Intent serviceIntent = new Intent(serviceAction);
+        serviceIntent.setPackage(context.getPackageName());
+        List<ResolveInfo> services = manager.queryIntentServices(serviceIntent,
+                PackageManager.GET_META_DATA);
+        String serviceName = null;
+        if (services != null) {
+            for (int i = 0; i < services.size(); i++) {
+                String serviceId = SessionToken2Impl.getSessionId(services.get(i));
+                if (serviceId != null && TextUtils.equals(id, serviceId)) {
+                    if (services.get(i).serviceInfo == null) {
+                        continue;
+                    }
+                    if (serviceName != null) {
+                        throw new IllegalArgumentException("Ambiguous session type. Multiple"
+                                + " session services define the same id=" + id);
+                    }
+                    serviceName = services.get(i).serviceInfo.name;
+                }
+            }
+        }
+        return serviceName;
+    }
+
+    @Override
+    public void initialize() {
+        synchronized (mLock) {
+            setPlayerLocked(mPlayer);
+        }
+        // Ask server for the sanity check, and starts
+        // Sanity check for making session ID unique 'per package' cannot be done in here.
+        // Server can only know if the package has another process and has another session with the
+        // same id. Note that 'ID is unique per package' is important for controller to distinguish
+        // a session in another package.
         MediaSessionManager manager =
                 (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-        mSessionToken = manager.createSessionToken(mContext.getPackageName(), mId, mSessionStub);
-        if (mSessionToken == null) {
+        if (!manager.onSessionCreated(mSessionToken)) {
             throw new IllegalStateException("Session with the same id is already used by"
                     + " another process. Use MediaController2 instead.");
         }
-
-        setPlayerInternal(player);
     }
 
     // TODO(jaewan): Add explicit release() and do not remove session object with the
@@ -126,28 +180,31 @@
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
-        setPlayerInternal(player);
+        if (player == mPlayer) {
+            return;
+        }
+        synchronized (mLock) {
+            setPlayerLocked(player);
+        }
     }
 
-    private void setPlayerInternal(MediaPlayerInterface player) {
-        synchronized (mLock) {
-            if (mPlayer == player) {
-                // Player didn't changed. No-op.
-                return;
-            }
-            if (mPlayer != null && mListener != null) {
-                // This might not work for a poorly implemented player.
-                mPlayer.removePlaybackListener(mListener);
-            }
-            mListener = new MyPlaybackListener(this, player);
-            player.addPlaybackListener(mCallbackExecutor, mListener);
-            mPlayer = player;
+    private void setPlayerLocked(MediaPlayerInterface player) {
+        if (mPlayer != null && mListener != null) {
+            // This might not work for a poorly implemented player.
+            mPlayer.removePlaybackListener(mListener);
         }
-        notifyPlaybackStateChangedNotLocked(player.getPlaybackState());
+        mPlayer = player;
+        mListener = new MyPlaybackListener(this, player);
+        player.addPlaybackListener(mCallbackExecutor, mListener);
     }
 
     @Override
     public void close_impl() {
+        // Stop system service from listening this session first.
+        MediaSessionManager manager =
+                (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        manager.onSessionDestroyed(mSessionToken);
+
         if (mSessionStub != null) {
             if (DEBUG) {
                 Log.d(TAG, "session is now unavailable, id=" + mId);
@@ -160,7 +217,6 @@
                 // close can be called multiple times
                 mPlayer.removePlaybackListener(mListener);
                 mPlayer = null;
-                return;
             }
         }
     }
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 33cc78f..77e7f22 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -18,7 +18,6 @@
 
 import static com.android.media.MediaController2Impl.CALLBACK_FLAG_PLAYBACK;
 
-import android.content.Context;
 import android.media.IMediaSession2;
 import android.media.IMediaSession2Callback;
 import android.media.MediaLibraryService2.BrowserRoot;
@@ -28,15 +27,10 @@
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
 import android.media.PlaybackState2;
-import android.media.session.PlaybackState;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.support.annotation.GuardedBy;
 import android.util.ArrayMap;
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index b9db305..70e8750 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -27,6 +27,8 @@
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
 import android.media.PlaybackState2;
+import android.media.SessionToken2;
+import android.media.SessionToken2.TokenType;
 import android.media.session.PlaybackState;
 import android.media.update.MediaSessionService2Provider;
 import android.os.IBinder;
@@ -81,39 +83,24 @@
                 NOTIFICATION_SERVICE);
         mStartSelfIntent = new Intent(mInstance, mInstance.getClass());
 
-        Intent serviceIntent = createServiceIntent();
-        ResolveInfo resolveInfo = mInstance.getPackageManager()
-                .resolveService(serviceIntent, PackageManager.GET_META_DATA);
-        String id;
-        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
-            throw new IllegalArgumentException("service " + mInstance + " doesn't implement"
-                    + serviceIntent.getAction());
-        } else if (resolveInfo.serviceInfo.metaData == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Failed to resolve ID for " + mInstance + ". Using empty id");
-            }
-            id = "";
-        } else {
-            id = resolveInfo.serviceInfo.metaData.getString(
-                    MediaSessionService2.SERVICE_META_DATA, "");
-        }
-        mSession = mInstance.onCreateSession(id);
-        if (mSession == null || !id.equals(mSession.getToken().getId())) {
-            throw new RuntimeException("Expected session with id " + id + ", but got " + mSession);
+        SessionToken2 token = new SessionToken2(mInstance, getSessionType(),
+                mInstance.getPackageName(), mInstance.getClass().getName());
+        mSession = mInstance.onCreateSession(token.getId());
+        if (mSession == null || !token.getId().equals(mSession.getToken().getId())) {
+            throw new RuntimeException("Expected session with id " + token.getId()
+                    + ", but got " + mSession);
         }
         // TODO(jaewan): Uncomment here.
         // mSession.addPlaybackListener(mListener, mSession.getExecutor());
     }
 
-    Intent createServiceIntent() {
-        Intent serviceIntent = new Intent(mInstance, mInstance.getClass());
-        serviceIntent.setAction(MediaSessionService2.SERVICE_INTERFACE);
-        return serviceIntent;
+    @TokenType int getSessionType() {
+        return SessionToken2.TYPE_SESSION_SERVICE;
     }
 
     public IBinder onBind_impl(Intent intent) {
         if (MediaSessionService2.SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mSession.getToken().getSessionBinder().asBinder();
+            return SessionToken2Impl.from(mSession.getToken()).getSessionBinder().asBinder();
         }
         return null;
     }
diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
new file mode 100644
index 0000000..c9ac434
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2018 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.media;
+
+import static android.media.SessionToken2.TYPE_SESSION;
+import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
+import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.media.IMediaSession2;
+import android.media.MediaLibraryService2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken2;
+import android.media.SessionToken2.TokenType;
+import android.media.update.SessionToken2Provider;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+public class SessionToken2Impl implements SessionToken2Provider {
+    private static final String KEY_UID = "android.media.token.uid";
+    private static final String KEY_TYPE = "android.media.token.type";
+    private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
+    private static final String KEY_SERVICE_NAME = "android.media.token.service_name";
+    private static final String KEY_ID = "android.media.token.id";
+    private static final String KEY_SESSION_BINDER = "android.media.token.session_binder";
+
+    private final SessionToken2 mInstance;
+    private final int mUid;
+    private final @TokenType int mType;
+    private final String mPackageName;
+    private final String mServiceName;
+    private final String mId;
+    private final IMediaSession2 mSessionBinder;
+
+    public SessionToken2Impl(Context context, SessionToken2 instance, int uid, int type,
+            String packageName, String serviceName, String id, IMediaSession2 sessionBinder) {
+        // TODO(jaewan): Add sanity check
+        mInstance = instance;
+        if (uid < 0) {
+            PackageManager manager = context.getPackageManager();
+            try {
+                uid = manager.getPackageUid(packageName, 0);
+            } catch (NameNotFoundException e) {
+                throw new IllegalArgumentException("Invalid uid=" + uid);
+            }
+        }
+        mUid = uid;
+        mType = type;
+        mPackageName = packageName;
+        mServiceName = serviceName;
+        if (id == null && !TextUtils.isEmpty(mServiceName)) {
+            // Will be called for an app with no
+            PackageManager manager = context.getPackageManager();
+            String action;
+            switch (type) {
+                case TYPE_SESSION_SERVICE:
+                    action = MediaSessionService2.SERVICE_INTERFACE;
+                    break;
+                case TYPE_LIBRARY_SERVICE:
+                    action = MediaLibraryService2.SERVICE_INTERFACE;
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid type");
+            }
+            Intent serviceIntent = new Intent(action);
+            serviceIntent.setClassName(packageName, serviceName);
+            id = getSessionId(manager.resolveService(serviceIntent,
+                    PackageManager.GET_META_DATA));
+            if (id == null) {
+                throw new IllegalArgumentException("service " + serviceName + " doesn't implement"
+                        + serviceIntent.getAction());
+            }
+        } else if (id == null) {
+            throw new IllegalArgumentException("ID shouldn't be null");
+        }
+        mId = id;
+        mSessionBinder = sessionBinder;
+    }
+
+    public static String getSessionId(ResolveInfo resolveInfo) {
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            return null;
+        } else if (resolveInfo.serviceInfo.metaData == null) {
+            return "";
+        } else {
+            return resolveInfo.serviceInfo.metaData.getString(
+                    MediaSessionService2.SERVICE_META_DATA, "");
+        }
+    }
+
+    @Override
+    public String getPackageName_impl() {
+        return mPackageName;
+    }
+
+    @Override
+    public int getUid_impl() {
+        return mUid;
+    }
+
+    @Override
+    public String getId_imp() {
+        return mId;
+    }
+
+    @Override
+    public int getType_impl() {
+        return mType;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public IMediaSession2 getSessionBinder() {
+        return mSessionBinder;
+    }
+
+    public static SessionToken2 fromBundle(Context context, Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final int uid = bundle.getInt(KEY_UID);
+        final @TokenType int type = bundle.getInt(KEY_TYPE, -1);
+        final String packageName = bundle.getString(KEY_PACKAGE_NAME);
+        final String serviceName = bundle.getString(KEY_SERVICE_NAME);
+        final String id = bundle.getString(KEY_ID);
+        final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER);
+
+        // Sanity check.
+        switch (type) {
+            case TYPE_SESSION:
+                if (sessionBinder == null) {
+                    throw new IllegalArgumentException("Unexpected sessionBinder for session,"
+                            + " binder=" + sessionBinder);
+                }
+                break;
+            case TYPE_SESSION_SERVICE:
+            case TYPE_LIBRARY_SERVICE:
+                if (TextUtils.isEmpty(serviceName)) {
+                    throw new IllegalArgumentException("Session service needs service name");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid type");
+        }
+        if (TextUtils.isEmpty(packageName) || id == null) {
+            throw new IllegalArgumentException("Package name nor ID cannot be null.");
+        }
+        // TODO(jaewan): Revisit here when we add connection callback to the session for individual
+        //               controller's permission check. With it, sessionBinder should be available
+        //               if and only if for session, not session service.
+        return new SessionToken2(context, uid, type, packageName, serviceName, id,
+                sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null);
+    }
+
+    @Override
+    public Bundle toBundle_impl() {
+        Bundle bundle = new Bundle();
+        bundle.putInt(KEY_UID, mUid);
+        bundle.putString(KEY_PACKAGE_NAME, mPackageName);
+        bundle.putString(KEY_SERVICE_NAME, mServiceName);
+        bundle.putString(KEY_ID, mId);
+        bundle.putInt(KEY_TYPE, mType);
+        bundle.putBinder(KEY_SESSION_BINDER,
+                mSessionBinder != null ? mSessionBinder.asBinder() : null);
+        return bundle;
+    }
+
+    @Override
+    public int hashCode_impl() {
+        final int prime = 31;
+        return mType
+                + prime * (mUid
+                + prime * (mPackageName.hashCode()
+                + prime * (mId.hashCode()
+                + prime * ((mServiceName != null ? mServiceName.hashCode() : 0)
+                + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0)))));
+    }
+
+    @Override
+    public boolean equals_impl(Object obj) {
+        if (!(obj instanceof SessionToken2)) {
+            return false;
+        }
+        SessionToken2Impl other = from((SessionToken2) obj);
+        if (mUid != other.mUid
+                || !TextUtils.equals(mPackageName, other.mPackageName)
+                || !TextUtils.equals(mServiceName, other.mServiceName)
+                || !TextUtils.equals(mId, other.mId)
+                || mType != other.mType) {
+            return false;
+        }
+        if (mSessionBinder == other.mSessionBinder) {
+            return true;
+        } else if (mSessionBinder == null || other.mSessionBinder == null) {
+            return false;
+        }
+        return mSessionBinder.asBinder().equals(other.mSessionBinder.asBinder());
+    }
+
+    @Override
+    public String toString_impl() {
+        return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType
+                + " service=" + mServiceName + " binder=" + mSessionBinder + "}";
+    }
+
+    public static SessionToken2Impl from(SessionToken2 token) {
+        return ((SessionToken2Impl) token.getProvider());
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index bb67bcd..4a2d6c4 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
+import android.media.IMediaSession2;
 import android.media.MediaBrowser2;
 import android.media.MediaBrowser2.BrowserCallback;
 import android.media.MediaController2;
@@ -41,9 +42,11 @@
 import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSessionService2Provider;
+import android.media.update.SessionToken2Provider;
 import android.media.update.VideoView2Provider;
 import android.media.update.StaticProvider;
 import android.media.update.ViewProvider;
+import android.os.Bundle;
 import android.os.IInterface;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
@@ -56,6 +59,7 @@
 import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
 import com.android.media.MediaSession2Impl;
 import com.android.media.MediaSessionService2Impl;
+import com.android.media.SessionToken2Impl;
 import com.android.widget.MediaControlView2Impl;
 import com.android.widget.VideoView2Impl;
 
@@ -131,4 +135,17 @@
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         return new VideoView2Impl(instance, superProvider, attrs, defStyleAttr, defStyleRes);
     }
+
+    @Override
+    public SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
+            int uid, int type, String packageName, String serviceName, String id,
+            IInterface sessionBinderInterface) {
+        return new SessionToken2Impl(context, instance, uid, type, packageName,
+                serviceName, id, (IMediaSession2) sessionBinderInterface);
+    }
+
+    @Override
+    public SessionToken2 SessionToken2_fromBundle(Context context, Bundle bundle) {
+        return SessionToken2Impl.fromBundle(context, bundle);
+    }
 }
diff --git a/packages/MediaComponents/test/runtest.sh b/packages/MediaComponents/test/runtest.sh
index 5c0ef51..d0290e7 100644
--- a/packages/MediaComponents/test/runtest.sh
+++ b/packages/MediaComponents/test/runtest.sh
@@ -129,6 +129,7 @@
       ${adb} root
       ${adb} remount
       ${adb} shell stop
+      ${adb} shell setprop log.tag.MediaSessionService DEBUG
       ${adb} sync
       ${adb} shell start
       ${adb} wait-for-device || break
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 3e39f40..d7e0ae0 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -381,7 +381,6 @@
         assertNotNull(token);
         assertEquals(mContext.getPackageName(), token.getPackageName());
         assertEquals(MockMediaSessionService2.ID, token.getId());
-        assertNull(token.getSessionBinder());
         assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
     }
 
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 6037619..d0106fa 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -85,7 +85,6 @@
             SessionToken2 token = tokens.get(i);
             if (mContext.getPackageName().equals(token.getPackageName())
                     && TAG.equals(token.getId())) {
-                assertNotNull(token.getSessionBinder());
                 assertNull(controller);
                 controller = createController(token);
             }
@@ -163,13 +162,11 @@
                     && MockMediaSessionService2.ID.equals(token.getId())) {
                 assertFalse(foundTestSessionService);
                 assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
-                assertNull(token.getSessionBinder());
                 foundTestSessionService = true;
             } else if (mContext.getPackageName().equals(token.getPackageName())
                     && MockMediaLibraryService2.ID.equals(token.getId())) {
                 assertFalse(foundTestLibraryService);
                 assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
-                assertNull(token.getSessionBinder());
                 foundTestLibraryService = true;
             }
         }
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 14cf257..f57a52c 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -70,9 +70,8 @@
     public static SessionToken2 getToken(Context context) {
         synchronized (MockMediaLibraryService2.class) {
             if (sToken == null) {
-                sToken = new SessionToken2(SessionToken2.TYPE_LIBRARY_SERVICE,
-                        context.getPackageName(), ID,
-                        MockMediaLibraryService2.class.getName(), null);
+                sToken = new SessionToken2(context, SessionToken2.TYPE_LIBRARY_SERVICE,
+                        context.getPackageName(), MockMediaLibraryService2.class.getName());
             }
             return sToken;
         }