135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim/*
235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * Copyright 2018 The Android Open Source Project
335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim *
435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * Licensed under the Apache License, Version 2.0 (the "License");
535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * you may not use this file except in compliance with the License.
635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * You may obtain a copy of the License at
735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim *
835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim *      http://www.apache.org/licenses/LICENSE-2.0
935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim *
1035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * Unless required by applicable law or agreed to in writing, software
1135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * distributed under the License is distributed on an "AS IS" BASIS,
1235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * See the License for the specific language governing permissions and
1435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim * limitations under the License.
1535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim */
1635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
1735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimpackage com.android.media;
1835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
19d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_SET_VOLUME;
20d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_ADD_ITEM;
21d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM;
22d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM;
23d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST;
24d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA;
25d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE;
26d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE;
27d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID;
28d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH;
29d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_URI;
30d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID;
31d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH;
32d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI;
33483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim
34da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kimimport android.app.PendingIntent;
3535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.content.ComponentName;
3635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.content.Context;
3735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.content.Intent;
3835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.content.ServiceConnection;
39991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kimimport android.media.AudioAttributes;
40785d2f41ea05d9372fe7e06a76c58661f9727db3Hyundo Moonimport android.media.MediaController2;
41785d2f41ea05d9372fe7e06a76c58661f9727db3Hyundo Moonimport android.media.MediaController2.ControllerCallback;
42da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kimimport android.media.MediaController2.PlaybackInfo;
43da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kimimport android.media.MediaItem2;
44e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kimimport android.media.MediaMetadata2;
4544648206f6a9c66f9f9478a8091685cb702349eeJaewan Kimimport android.media.MediaPlaylistAgent.RepeatMode;
4644648206f6a9c66f9f9478a8091685cb702349eeJaewan Kimimport android.media.MediaPlaylistAgent.ShuffleMode;
47d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport android.media.SessionCommand2;
489b67177ebfe5f46c0c6216f87f5ee98c03e5cbadJaewan Kimimport android.media.MediaSession2.CommandButton;
49d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kimimport android.media.SessionCommandGroup2;
5035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.media.MediaSessionService2;
51da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kimimport android.media.Rating2;
527027e8019ad974d2e474a7fcaf43adedbdf08f23Jaewan Kimimport android.media.SessionToken2;
5335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.media.update.MediaController2Provider;
54da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kimimport android.net.Uri;
553bad8ce270642d8780f3cd450a0c8af94788fde4Jaewan Kimimport android.os.Bundle;
5635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.os.IBinder;
577cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kimimport android.os.Process;
5835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.os.RemoteException;
59da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kimimport android.os.ResultReceiver;
607cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kimimport android.os.UserHandle;
6135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.support.annotation.GuardedBy;
6298fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moonimport android.text.TextUtils;
6335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport android.util.Log;
6435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
6535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport java.util.ArrayList;
6635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport java.util.List;
6735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimimport java.util.concurrent.Executor;
6835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
6935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kimpublic class MediaController2Impl implements MediaController2Provider {
7035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private static final String TAG = "MediaController2";
7135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private static final boolean DEBUG = true; // TODO(jaewan): Change
7235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
7335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private final MediaController2 mInstance;
7478e257713ebf4805647772f32fac433d2621405eSungsoo Lim    private final Context mContext;
7535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private final Object mLock = new Object();
7635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
7798f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim    private final MediaController2Stub mControllerStub;
787027e8019ad974d2e474a7fcaf43adedbdf08f23Jaewan Kim    private final SessionToken2 mToken;
7935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private final ControllerCallback mCallback;
8035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private final Executor mCallbackExecutor;
8135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private final IBinder.DeathRecipient mDeathRecipient;
8235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
8335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @GuardedBy("mLock")
8435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private SessionServiceConnection mServiceConnection;
8535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @GuardedBy("mLock")
8635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private boolean mIsReleased;
8798421b59b047196e2605ad1696e47ecca890dcf3Jaewan Kim    @GuardedBy("mLock")
883923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon    private List<MediaItem2> mPlaylist;
893923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon    @GuardedBy("mLock")
90e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim    private MediaMetadata2 mPlaylistMetadata;
91e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim    @GuardedBy("mLock")
9244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    private @RepeatMode int mRepeatMode;
9344648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    @GuardedBy("mLock")
9444648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    private @ShuffleMode int mShuffleMode;
95c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon    @GuardedBy("mLock")
9679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    private int mPlayerState;
9779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    @GuardedBy("mLock")
9879f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    private long mPositionEventTimeMs;
9979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    @GuardedBy("mLock")
10079f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    private long mPositionMs;
10179f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    @GuardedBy("mLock")
10279f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    private float mPlaybackSpeed;
10379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    @GuardedBy("mLock")
10479f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    private long mBufferedPositionMs;
10579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    @GuardedBy("mLock")
106c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon    private PlaybackInfo mPlaybackInfo;
107c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim    @GuardedBy("mLock")
1080261708bf17cb0469de8eb3c81455e2f48ff9433Jaewan Kim    private PendingIntent mSessionActivity;
1090261708bf17cb0469de8eb3c81455e2f48ff9433Jaewan Kim    @GuardedBy("mLock")
110d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim    private SessionCommandGroup2 mAllowedCommands;
11135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
11235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // Assignment should be used with the lock hold, but should be used without a lock to prevent
11335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // potential deadlock.
11435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // Postfix -Binder is added to explicitly show that it's potentially remote process call.
11535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // Technically -Interface is more correct, but it may misread that it's interface (vs class)
11635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // so let's keep this postfix until we find better postfix.
11735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @GuardedBy("mLock")
11835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private volatile IMediaSession2 mSessionBinder;
11935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
12035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // TODO(jaewan): Require session activeness changed listener, because controller can be
12135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    //               available when the session's player is null.
122c21a36910245a441d60f5f3c053e3e1bdaa24d5eJaewan Kim    public MediaController2Impl(Context context, MediaController2 instance, SessionToken2 token,
123c21a36910245a441d60f5f3c053e3e1bdaa24d5eJaewan Kim            Executor executor, ControllerCallback callback) {
12435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mInstance = instance;
12535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (context == null) {
12635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            throw new IllegalArgumentException("context shouldn't be null");
12735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
12835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (token == null) {
12935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            throw new IllegalArgumentException("token shouldn't be null");
13035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
13135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (callback == null) {
13235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            throw new IllegalArgumentException("callback shouldn't be null");
13335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
13435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (executor == null) {
13535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            throw new IllegalArgumentException("executor shouldn't be null");
13635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
13735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mContext = context;
13898f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim        mControllerStub = new MediaController2Stub(this);
13935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mToken = token;
14035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mCallback = callback;
14135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mCallbackExecutor = executor;
14235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mDeathRecipient = () -> {
143113a561d324e89b1db5edcc39c903c879e08dd94Jaewan Kim            mInstance.close();
14435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        };
14535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
14635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mSessionBinder = null;
147dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim    }
14835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
149dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim    @Override
150dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim    public void initialize() {
151dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim        // TODO(jaewan): More sanity checks.
152e38637402cf0426808f77b47483b83096112be5fSungsoo Lim        if (mToken.getType() == SessionToken2.TYPE_SESSION) {
153e38637402cf0426808f77b47483b83096112be5fSungsoo Lim            // Session
154e38637402cf0426808f77b47483b83096112be5fSungsoo Lim            mServiceConnection = null;
155e38637402cf0426808f77b47483b83096112be5fSungsoo Lim            connectToSession(SessionToken2Impl.from(mToken).getSessionBinder());
156e38637402cf0426808f77b47483b83096112be5fSungsoo Lim        } else {
157dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim            // Session service
1587cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim            if (Process.myUid() == Process.SYSTEM_UID) {
1597cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                // It's system server (MediaSessionService) that wants to monitor session.
1607cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                // Don't bind if able..
1617cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                IMediaSession2 binder = SessionToken2Impl.from(mToken).getSessionBinder();
1627cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                if (binder != null) {
1637cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    // Use binder in the session token instead of bind by its own.
1647cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    // Otherwise server will holds the binding to the service *forever* and service
1657cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    // will never stop.
1667cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    mServiceConnection = null;
1677cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    connectToSession(SessionToken2Impl.from(mToken).getSessionBinder());
1687cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    return;
1697cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                } else if (DEBUG) {
1707cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    // Should happen only when system server wants to dispatch media key events to
1717cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    // a dead service.
1727cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    Log.d(TAG, "System server binds to a session service. Should unbind"
1737cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                            + " immediately after the use.");
1747cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                }
1757cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim            }
17635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            mServiceConnection = new SessionServiceConnection();
17735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            connectToService();
17835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
17935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
18035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
18135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private void connectToService() {
18235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        // Service. Needs to get fresh binder whenever connection is needed.
183dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim        SessionToken2Impl impl = SessionToken2Impl.from(mToken);
18435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        final Intent intent = new Intent(MediaSessionService2.SERVICE_INTERFACE);
185dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim        intent.setClassName(mToken.getPackageName(), impl.getServiceName());
18635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
18735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        // Use bindService() instead of startForegroundService() to start session service for three
18835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        // reasons.
18935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        // 1. Prevent session service owner's stopSelf() from destroying service.
19035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    With the startForegroundService(), service's call of stopSelf() will trigger immediate
19135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    onDestroy() calls on the main thread even when onConnect() is running in another
19235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    thread.
19335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        // 2. Minimize APIs for developers to take care about.
19435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    With bindService(), developers only need to take care about Service.onBind()
19535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    but Service.onStartCommand() should be also taken care about with the
19635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    startForegroundService().
19735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        // 3. Future support for UI-less playback
19835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    If a service wants to keep running, it should be either foreground service or
19935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    bounded service. But there had been request for the feature for system apps
20035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        //    and using bindService() will be better fit with it.
2017cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim        boolean result;
2027cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim        if (Process.myUid() == Process.SYSTEM_UID) {
2037cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim            // Use bindServiceAsUser() for binding from system service to avoid following warning.
2047cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim            // ContextImpl: Calling a method in the system process without a qualified user
2057cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim            result = mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE,
2067cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim                    UserHandle.getUserHandleForUid(mToken.getUid()));
2077cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim        } else {
2087cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim            result = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
2097cda0f098a62cc0297d63f9c9dfa6b5345c7fbf9Jaewan Kim        }
21035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (!result) {
21135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            Log.w(TAG, "bind to " + mToken + " failed");
21235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        } else if (DEBUG) {
21335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            Log.d(TAG, "bind to " + mToken + " success");
21435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
21535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
21635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
21735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private void connectToSession(IMediaSession2 sessionBinder) {
21835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        try {
21998f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim            sessionBinder.connect(mControllerStub, mContext.getPackageName());
22035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        } catch (RemoteException e) {
22135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            Log.w(TAG, "Failed to call connection request. Framework will retry"
22235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    + " automatically");
22335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
22435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
22535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
22635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
227113a561d324e89b1db5edcc39c903c879e08dd94Jaewan Kim    public void close_impl() {
2285aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim        if (DEBUG) {
229dd4397861fd98aefae95e6d6036a96f499eaf8b1Jaewan Kim            Log.d(TAG, "release from " + mToken);
2305aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim        }
23135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        final IMediaSession2 binder;
23235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        synchronized (mLock) {
23335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            if (mIsReleased) {
23435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                // Prevent re-enterance from the ControllerCallback.onDisconnected()
23535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                return;
23635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
23735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            mIsReleased = true;
23835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            if (mServiceConnection != null) {
23935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                mContext.unbindService(mServiceConnection);
24035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                mServiceConnection = null;
24135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
24235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            binder = mSessionBinder;
24335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            mSessionBinder = null;
24498f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim            mControllerStub.destroy();
24535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
24635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (binder != null) {
24735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            try {
24835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                binder.asBinder().unlinkToDeath(mDeathRecipient, 0);
24998f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.release(mControllerStub);
25035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            } catch (RemoteException e) {
25135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                // No-op.
25235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
25335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
25435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        mCallbackExecutor.execute(() -> {
255d3d75358db7e3fe87805f1d91aa53abe1b0c2e92Jaewan Kim            mCallback.onDisconnected(mInstance);
25635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        });
25735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
25835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
259735f3430f9cd1224a68e87b015baca0789debe22Jaewan Kim    IMediaSession2 getSessionBinder() {
260735f3430f9cd1224a68e87b015baca0789debe22Jaewan Kim        return mSessionBinder;
261735f3430f9cd1224a68e87b015baca0789debe22Jaewan Kim    }
262735f3430f9cd1224a68e87b015baca0789debe22Jaewan Kim
26398f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim    MediaController2Stub getControllerStub() {
26498f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim        return mControllerStub;
265735f3430f9cd1224a68e87b015baca0789debe22Jaewan Kim    }
266735f3430f9cd1224a68e87b015baca0789debe22Jaewan Kim
2675aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim    Executor getCallbackExecutor() {
2685aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim        return mCallbackExecutor;
2695aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim    }
2705aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim
27178e257713ebf4805647772f32fac433d2621405eSungsoo Lim    Context getContext() {
272b41bfb59dd52decb1e5c5952d6ed786f159d772dJaewan Kim        return mContext;
273b41bfb59dd52decb1e5c5952d6ed786f159d772dJaewan Kim    }
274b41bfb59dd52decb1e5c5952d6ed786f159d772dJaewan Kim
275b41bfb59dd52decb1e5c5952d6ed786f159d772dJaewan Kim    MediaController2 getInstance() {
276b41bfb59dd52decb1e5c5952d6ed786f159d772dJaewan Kim        return mInstance;
27778e257713ebf4805647772f32fac433d2621405eSungsoo Lim    }
27878e257713ebf4805647772f32fac433d2621405eSungsoo Lim
279483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim    // Returns session binder if the controller can send the command.
280483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim    IMediaSession2 getSessionBinderIfAble(int commandCode) {
281483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        synchronized (mLock) {
282483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            if (!mAllowedCommands.hasCommand(commandCode)) {
283483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                // Cannot send because isn't allowed to.
284483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                Log.w(TAG, "Controller isn't allowed to call command, commandCode="
285483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                        + commandCode);
286483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                return null;
287483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            }
288483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        }
289483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        // TODO(jaewan): Should we do this with the lock hold?
290483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        final IMediaSession2 binder = mSessionBinder;
291483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        if (binder == null) {
292483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            // Cannot send because disconnected.
293483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            Log.w(TAG, "Session is disconnected");
294483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        }
295483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        return binder;
296483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim    }
297483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim
298483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim    // Returns session binder if the controller can send the command.
299d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim    IMediaSession2 getSessionBinderIfAble(SessionCommand2 command) {
300483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        synchronized (mLock) {
301483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            if (!mAllowedCommands.hasCommand(command)) {
302483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                Log.w(TAG, "Controller isn't allowed to call command, command=" + command);
303483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                return null;
304483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            }
305483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        }
306483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        // TODO(jaewan): Should we do this with the lock hold?
307483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        final IMediaSession2 binder = mSessionBinder;
308483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        if (binder == null) {
309483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            // Cannot send because disconnected.
310483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            Log.w(TAG, "Session is disconnected");
311483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        }
312483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        return binder;
313483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim    }
314483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim
31535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
3167027e8019ad974d2e474a7fcaf43adedbdf08f23Jaewan Kim    public SessionToken2 getSessionToken_impl() {
31735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        return mToken;
31835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
31935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
32035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
32135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    public boolean isConnected_impl() {
32235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        final IMediaSession2 binder = mSessionBinder;
32335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        return binder != null;
32435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
32535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
32635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
32735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    public void play_impl() {
328d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim        sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY);
32935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
33035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
33135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
33235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    public void pause_impl() {
333d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim        sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE);
33435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
33535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
33635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
33735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    public void stop_impl() {
338d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim        sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_STOP);
33935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
34035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
34135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
34254d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim    public void skipToPlaylistItem_impl(MediaItem2 item) {
34354d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        if (item == null) {
34454d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            throw new IllegalArgumentException("item shouldn't be null");
34554d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        }
34654d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        final IMediaSession2 binder = mSessionBinder;
34754d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        if (binder != null) {
34854d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            try {
34954d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim                binder.skipToPlaylistItem(mControllerStub, item.toBundle());
35054d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            } catch (RemoteException e) {
35154d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
35254d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            }
35354d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        } else {
35454d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
35554d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        }
35654d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim    }
35754d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim
35854d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim    @Override
359c8508bf04576790230c5a32b329a4b47468ac7fbJaewan Kim    public void skipToPreviousItem_impl() {
36054d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        final IMediaSession2 binder = mSessionBinder;
36154d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        if (binder != null) {
36254d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            try {
36354d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim                binder.skipToPreviousItem(mControllerStub);
36454d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            } catch (RemoteException e) {
36554d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
36654d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            }
36754d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        } else {
36854d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
36954d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        }
37035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
37135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
37235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
373c8508bf04576790230c5a32b329a4b47468ac7fbJaewan Kim    public void skipToNextItem_impl() {
37454d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        final IMediaSession2 binder = mSessionBinder;
37554d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        if (binder != null) {
37654d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            try {
37754d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim                binder.skipToNextItem(mControllerStub);
37854d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            } catch (RemoteException e) {
37954d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
38054d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            }
38154d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        } else {
38254d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
38354d638619dc3473e35a2aa61738fb6034d77b8deJaewan Kim        }
3843bad8ce270642d8780f3cd450a0c8af94788fde4Jaewan Kim    }
3853bad8ce270642d8780f3cd450a0c8af94788fde4Jaewan Kim
386f6f272f8c4bdd41274537761ff0efb7b7a801f3dJaewan Kim    private void sendTransportControlCommand(int commandCode) {
387e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        sendTransportControlCommand(commandCode, null);
388f6f272f8c4bdd41274537761ff0efb7b7a801f3dJaewan Kim    }
3893bad8ce270642d8780f3cd450a0c8af94788fde4Jaewan Kim
390e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon    private void sendTransportControlCommand(int commandCode, Bundle args) {
39135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        final IMediaSession2 binder = mSessionBinder;
39235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (binder != null) {
39335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            try {
39498f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.sendTransportControlCommand(mControllerStub, commandCode, args);
39535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            } catch (RemoteException e) {
39635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
39735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
39835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        } else {
39935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
40035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
40135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
40235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
40335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
404da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public PendingIntent getSessionActivity_impl() {
4050261708bf17cb0469de8eb3c81455e2f48ff9433Jaewan Kim        return mSessionActivity;
40635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
40735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
40835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    @Override
409da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void setVolumeTo_impl(int value, int flags) {
410c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        // TODO(hdmoon): sanity check
411d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
412c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        if (binder != null) {
413c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            try {
41498f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.setVolumeTo(mControllerStub, value, flags);
415c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            } catch (RemoteException e) {
416c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
417c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            }
418c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        } else {
419c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            Log.w(TAG, "Session isn't active", new IllegalStateException());
420c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        }
421da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
422da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
423da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
424da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void adjustVolume_impl(int direction, int flags) {
425c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        // TODO(hdmoon): sanity check
426d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
427c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        if (binder != null) {
428c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            try {
42998f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.adjustVolume(mControllerStub, direction, flags);
430c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            } catch (RemoteException e) {
431c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
432c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            }
433c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        } else {
434c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon            Log.w(TAG, "Session isn't active", new IllegalStateException());
435c7840a9c538949820d8b3481072d4bc13081d85dHyundo Moon        }
436da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
437da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
438da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
439da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void prepareFromUri_impl(Uri uri, Bundle extras) {
44054936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PREPARE_FROM_URI);
44198fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (uri == null) {
44298fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("uri shouldn't be null");
44398fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
444edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        if (binder != null) {
445edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            try {
44698f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.prepareFromUri(mControllerStub, uri, extras);
447edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            } catch (RemoteException e) {
448edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
449edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            }
450edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        } else {
451edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            // TODO(jaewan): Handle.
452edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        }
453da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
454da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
455da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
456da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void prepareFromSearch_impl(String query, Bundle extras) {
45754936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(
45854936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim                COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH);
45998fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (TextUtils.isEmpty(query)) {
46098fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("query shouldn't be empty");
46198fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
462edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        if (binder != null) {
463edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            try {
46498f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.prepareFromSearch(mControllerStub, query, extras);
465edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            } catch (RemoteException e) {
466edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
467edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            }
468edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        } else {
469edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            // TODO(jaewan): Handle.
470edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        }
471da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
472da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
473da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
47498fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon    public void prepareFromMediaId_impl(String mediaId, Bundle extras) {
47554936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(
47654936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim                COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID);
47798fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (mediaId == null) {
47898fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("mediaId shouldn't be null");
47998fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
480edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        if (binder != null) {
481edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            try {
48298f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.prepareFromMediaId(mControllerStub, mediaId, extras);
483edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            } catch (RemoteException e) {
484edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
485edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            }
486edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        } else {
487edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            // TODO(jaewan): Handle.
488edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        }
489da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
490da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
491da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
492edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim    public void playFromUri_impl(Uri uri, Bundle extras) {
49354936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PLAY_FROM_URI);
49498fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (uri == null) {
49598fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("uri shouldn't be null");
49698fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
497edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        if (binder != null) {
498edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            try {
49998f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.playFromUri(mControllerStub, uri, extras);
500edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            } catch (RemoteException e) {
501edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
502edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            }
503edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        } else {
504edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            // TODO(jaewan): Handle.
505edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        }
506da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
507da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
508da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
509edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim    public void playFromSearch_impl(String query, Bundle extras) {
51054936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PLAY_FROM_SEARCH);
51198fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (TextUtils.isEmpty(query)) {
51298fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("query shouldn't be empty");
51398fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
514edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        if (binder != null) {
515edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            try {
51698f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.playFromSearch(mControllerStub, query, extras);
517edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            } catch (RemoteException e) {
518edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
519edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            }
520edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        } else {
521edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            // TODO(jaewan): Handle.
522edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        }
523da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
524da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
525da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
526da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void playFromMediaId_impl(String mediaId, Bundle extras) {
52754936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(
52854936be442ba7153667cb582fb9d2035f9f1f63cJaewan Kim                COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID);
52998fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (mediaId == null) {
53098fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("mediaId shouldn't be null");
53198fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
532edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        if (binder != null) {
533edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            try {
53498f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.playFromMediaId(mControllerStub, mediaId, extras);
535edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            } catch (RemoteException e) {
536edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
537edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            }
538edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        } else {
539edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim            // TODO(jaewan): Handle.
540edd9171c285692efc04a8f38b3aed0140ae1e56cJaewan Kim        }
541da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
542af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon
543da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
544af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon    public void setRating_impl(String mediaId, Rating2 rating) {
545af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        if (mediaId == null) {
546af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon            throw new IllegalArgumentException("mediaId shouldn't be null");
547af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        }
548af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        if (rating == null) {
549af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon            throw new IllegalArgumentException("rating shouldn't be null");
550af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        }
551af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon
552af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        final IMediaSession2 binder = mSessionBinder;
553af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        if (binder != null) {
554af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon            try {
55598f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.setRating(mControllerStub, mediaId, rating.toBundle());
556af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon            } catch (RemoteException e) {
557af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
558af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon            }
559af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        } else {
560af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon            // TODO(jaewan): Handle.
561af063f1179a5046c13b55710a42aaba993e19fcbHyundo Moon        }
562da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
563da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
564da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
565d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim    public void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb) {
566bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        if (command == null) {
567bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            throw new IllegalArgumentException("command shouldn't be null");
568bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        }
569483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(command);
570bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        if (binder != null) {
571bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            try {
57298f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.sendCustomCommand(mControllerStub, command.toBundle(), args, cb);
573bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            } catch (RemoteException e) {
574bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
575bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            }
576bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        } else {
577bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
578bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        }
579da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
580da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
581da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
582da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public List<MediaItem2> getPlaylist_impl() {
5833923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon        synchronized (mLock) {
5843923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon            return mPlaylist;
5853923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon        }
586da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
587da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
588da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
589e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim    public void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata) {
590e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        if (list == null) {
591e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            throw new IllegalArgumentException("list shouldn't be null");
592e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        }
593e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_SET_LIST);
594e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        if (binder != null) {
595e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            List<Bundle> bundleList = new ArrayList<>();
596e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            for (int i = 0; i < list.size(); i++) {
597e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim                bundleList.add(list.get(i).toBundle());
598e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            }
599e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            Bundle metadataBundle = (metadata == null) ? null : metadata.toBundle();
600e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            try {
60198f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.setPlaylist(mControllerStub, bundleList, metadataBundle);
602e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            } catch (RemoteException e) {
603e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
604e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            }
605e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        } else {
606e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
607e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        }
608e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim    }
609e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim
610e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim    @Override
611df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim    public MediaMetadata2 getPlaylistMetadata_impl() {
612df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        synchronized (mLock) {
613df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            return mPlaylistMetadata;
614df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        }
615df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim    }
616df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim
617df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim    @Override
618df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim    public void updatePlaylistMetadata_impl(MediaMetadata2 metadata) {
619df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(
620df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim                COMMAND_CODE_PLAYLIST_SET_LIST_METADATA);
621df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        if (binder != null) {
622df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            Bundle metadataBundle = (metadata == null) ? null : metadata.toBundle();
623df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            try {
62498f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.updatePlaylistMetadata(mControllerStub, metadataBundle);
625df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            } catch (RemoteException e) {
626df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
627df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            }
628df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        } else {
629df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
630df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        }
631df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim    }
632df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim
633df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim    @Override
634da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void prepare_impl() {
635d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim        sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE);
636da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
637da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
638da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
639da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void fastForward_impl() {
640076d7da0936e85fc5243761ffd393d5c2beeee20Jaewan Kim        // TODO(jaewan): Implement this. Note that fast forward isn't a transport command anymore
641076d7da0936e85fc5243761ffd393d5c2beeee20Jaewan Kim        //sendTransportControlCommand(MediaSession2.COMMAND_CODE_SESSION_FAST_FORWARD);
642da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
643da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
644da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
645da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void rewind_impl() {
646076d7da0936e85fc5243761ffd393d5c2beeee20Jaewan Kim        // TODO(jaewan): Implement this. Note that rewind isn't a transport command anymore
647076d7da0936e85fc5243761ffd393d5c2beeee20Jaewan Kim        //sendTransportControlCommand(MediaSession2.COMMAND_CODE_SESSION_REWIND);
648da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
649da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
650da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
651da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    public void seekTo_impl(long pos) {
65298fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (pos < 0) {
65398fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("position shouldn't be negative");
65498fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
655e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        Bundle args = new Bundle();
656e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        args.putLong(MediaSession2Stub.ARGUMENT_KEY_POSITION, pos);
657d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim        sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_SEEK_TO, args);
658da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
659da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
660da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
661f82f720c00cf35a8f0f9a6135551ee47855dd6a0Sungsoo Lim    public void addPlaylistItem_impl(int index, MediaItem2 item) {
66298fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (index < 0) {
66398fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("index shouldn't be negative");
66498fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
66598fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (item == null) {
66698fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("item shouldn't be null");
66798fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
66824ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_ADD_ITEM);
66924ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        if (binder != null) {
67024ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            try {
67198f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.addPlaylistItem(mControllerStub, index, item.toBundle());
67224ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            } catch (RemoteException e) {
67324ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
67424ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            }
67524ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        } else {
67624ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
67724ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        }
678da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
679da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
680da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
681f82f720c00cf35a8f0f9a6135551ee47855dd6a0Sungsoo Lim    public void removePlaylistItem_impl(MediaItem2 item) {
68298fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (item == null) {
68398fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("item shouldn't be null");
68498fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
68524ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_REMOVE_ITEM);
68624ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        if (binder != null) {
68724ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            try {
68898f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.removePlaylistItem(mControllerStub, item.toBundle());
68924ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            } catch (RemoteException e) {
69024ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
69124ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            }
69224ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        } else {
69324ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
69424ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        }
695da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    }
696da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim
697da59499289a68a2558370b6acfd4096d3feb8107Jaewan Kim    @Override
698414ef561baf181397ecab5aac0e4a3ce66a9fd96Jean-Michel Trivi    public void replacePlaylistItem_impl(int index, MediaItem2 item) {
69998fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (index < 0) {
70098fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("index shouldn't be negative");
70198fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
70298fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        if (item == null) {
70398fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon            throw new IllegalArgumentException("item shouldn't be null");
70498fdf3583d3bfd1178ef1e0102e3de6d2b1dd2bcHyundo Moon        }
70524ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_REPLACE_ITEM);
70624ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        if (binder != null) {
70724ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            try {
70898f280314350160874af3bc3fd660aa3e9a0fabeSungsoo Lim                binder.replacePlaylistItem(mControllerStub, index, item.toBundle());
70924ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            } catch (RemoteException e) {
71024ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
71124ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            }
71224ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        } else {
71324ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
71424ab94cf1e42ea5bcfedebff29fd7a237641f6dfJaewan Kim        }
715414ef561baf181397ecab5aac0e4a3ce66a9fd96Jean-Michel Trivi    }
716414ef561baf181397ecab5aac0e4a3ce66a9fd96Jean-Michel Trivi
717414ef561baf181397ecab5aac0e4a3ce66a9fd96Jean-Michel Trivi    @Override
71844648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    public int getShuffleMode_impl() {
71944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        return mShuffleMode;
72044648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    }
72144648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim
72244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    @Override
72344648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    public void setShuffleMode_impl(int shuffleMode) {
72444648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(
72544648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE);
72644648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        if (binder != null) {
72744648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            try {
72844648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                binder.setShuffleMode(mControllerStub, shuffleMode);
72944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            } catch (RemoteException e) {
73044648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
73144648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            }
73244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        } else {
73344648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
734e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        }
73535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
73635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
737621289a788c968d46df85ff238c3e41cf0f34476Jaewan Kim    @Override
73844648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    public int getRepeatMode_impl() {
73944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        return mRepeatMode;
74044648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    }
74144648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim
74244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    @Override
74344648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    public void setRepeatMode_impl(int repeatMode) {
74444648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE);
74544648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        if (binder != null) {
74644648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            try {
74744648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                binder.setRepeatMode(mControllerStub, repeatMode);
74844648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            } catch (RemoteException e) {
74944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
75044648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            }
75144648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        } else {
75244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            Log.w(TAG, "Session isn't active", new IllegalStateException());
753c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon        }
754c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon    }
755c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon
756c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon    @Override
75744648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    public PlaybackInfo getPlaybackInfo_impl() {
75844648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        synchronized (mLock) {
75944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            return mPlaybackInfo;
760e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        }
761621289a788c968d46df85ff238c3e41cf0f34476Jaewan Kim    }
762621289a788c968d46df85ff238c3e41cf0f34476Jaewan Kim
76381c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    @Override
76481c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    public int getPlayerState_impl() {
76579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        synchronized (mLock) {
76679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            return mPlayerState;
76779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        }
76881c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    }
76981c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim
77081c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    @Override
7711fe063606ecf3ef1fd71496bc2bcdd0d8ce8342dJaewan Kim    public long getCurrentPosition_impl() {
77279f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        synchronized (mLock) {
77379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            long timeDiff = System.currentTimeMillis() - mPositionEventTimeMs;
77479f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            long expectedPosition = mPositionMs + (long) (mPlaybackSpeed * timeDiff);
77579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            return Math.max(0, expectedPosition);
77679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        }
77781c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    }
77881c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim
77981c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    @Override
78081c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    public float getPlaybackSpeed_impl() {
78179f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        synchronized (mLock) {
78279f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            return mPlaybackSpeed;
78379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        }
78481c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    }
78581c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim
78681c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    @Override
78781c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    public long getBufferedPosition_impl() {
78879f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        synchronized (mLock) {
78979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            return mBufferedPositionMs;
79079f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        }
79181c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    }
79281c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim
79381c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    @Override
794c8508bf04576790230c5a32b329a4b47468ac7fbJaewan Kim    public MediaItem2 getCurrentMediaItem_impl() {
79581c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim        // TODO(jaewan): Implement
79681c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim        return null;
79781c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim    }
79881c1b87234bdab435259acd355ba2df4730da3ceJaewan Kim
79979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    void pushPlayerStateChanges(final int state) {
80079f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        synchronized (mLock) {
80179f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            mPlayerState = state;
80279f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        }
80379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        mCallbackExecutor.execute(() -> {
80479f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            if (!mInstance.isConnected()) {
80579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                return;
80679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            }
80779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            mCallback.onPlayerStateChanged(mInstance, state);
80879f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        });
80979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    }
81079f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon
8114a7b86426b7139dae80d1362eddb842e675bc069Jaewan Kim    // TODO(jaewan): Rename to seek completed
81279f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    void pushPositionChanges(final long eventTimeMs, final long positionMs) {
81379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        synchronized (mLock) {
81479f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            mPositionEventTimeMs = eventTimeMs;
81579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            mPositionMs = positionMs;
81679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        }
81779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        mCallbackExecutor.execute(() -> {
81879f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            if (!mInstance.isConnected()) {
81979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                return;
82079f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            }
8214a7b86426b7139dae80d1362eddb842e675bc069Jaewan Kim            mCallback.onSeekCompleted(mInstance, positionMs);
82279f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        });
82379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    }
82479f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon
82579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    void pushPlaybackSpeedChanges(final float speed) {
82679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        synchronized (mLock) {
82779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            mPlaybackSpeed = speed;
82879f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        }
82979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        mCallbackExecutor.execute(() -> {
83079f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            if (!mInstance.isConnected()) {
83179f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                return;
83279f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            }
83379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            mCallback.onPlaybackSpeedChanged(mInstance, speed);
83479f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon        });
83579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    }
83679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon
83779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon    void pushBufferedPositionChanges(final long bufferedPositionMs) {
83835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        synchronized (mLock) {
83979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            mBufferedPositionMs = bufferedPositionMs;
84035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
841e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        mCallbackExecutor.execute(() -> {
842e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon            if (!mInstance.isConnected()) {
843e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon                return;
844e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon            }
8451fe063606ecf3ef1fd71496bc2bcdd0d8ce8342dJaewan Kim            // TODO(jaewan): Fix this -- it's now buffered state
8461fe063606ecf3ef1fd71496bc2bcdd0d8ce8342dJaewan Kim            //mCallback.onBufferedPositionChanged(mInstance, bufferedPositionMs);
847e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        });
848e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon    }
849e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon
85044648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    void pushPlaybackInfoChanges(final PlaybackInfo info) {
851e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        synchronized (mLock) {
85244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            mPlaybackInfo = info;
853e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        }
854e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        mCallbackExecutor.execute(() -> {
855e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon            if (!mInstance.isConnected()) {
856e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon                return;
857e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon            }
85844648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            mCallback.onPlaybackInfoChanged(mInstance, info);
859e71339cbe74fc7b87c822ae95d1c842d7944942bHyundo Moon        });
86035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
86135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
86244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    void pushPlaylistChanges(final List<MediaItem2> playlist, final MediaMetadata2 metadata) {
863c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon        synchronized (mLock) {
86444648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            mPlaylist = playlist;
86544648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            mPlaylistMetadata = metadata;
866c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon        }
867c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon        mCallbackExecutor.execute(() -> {
868c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon            if (!mInstance.isConnected()) {
869c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon                return;
870c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon            }
871cf1e1332e0b6880b4776d0bab31506092163423fJaewan Kim            mCallback.onPlaylistChanged(mInstance, playlist, metadata);
872c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon        });
873c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon    }
874c81e4db8363fb9736bc2362f0ff213b5d9e33ac3Hyundo Moon
87544648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    void pushPlaylistMetadataChanges(MediaMetadata2 metadata) {
8763923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon        synchronized (mLock) {
877e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            mPlaylistMetadata = metadata;
8783923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon        }
879e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        mCallbackExecutor.execute(() -> {
880e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            if (!mInstance.isConnected()) {
881e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim                return;
882e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim            }
883cf1e1332e0b6880b4776d0bab31506092163423fJaewan Kim            mCallback.onPlaylistMetadataChanged(mInstance, metadata);
884e0a38c69b26945394936922dd837cc7e9fa37d3cJaewan Kim        });
8853923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon    }
8863923b1b71628e668f9b11ed4891ed2840dd331cdHyundo Moon
88744648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    void pushShuffleModeChanges(int shuffleMode) {
888df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        synchronized (mLock) {
88944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            mShuffleMode = shuffleMode;
890df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        }
891df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        mCallbackExecutor.execute(() -> {
892df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            if (!mInstance.isConnected()) {
893df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim                return;
894df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim            }
895cf1e1332e0b6880b4776d0bab31506092163423fJaewan Kim            mCallback.onShuffleModeChanged(mInstance, shuffleMode);
89644648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        });
89744648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    }
89844648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim
89944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim    void pushRepeatModeChanges(int repeatMode) {
90044648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        synchronized (mLock) {
90144648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            mRepeatMode = repeatMode;
90244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        }
90344648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim        mCallbackExecutor.execute(() -> {
90444648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            if (!mInstance.isConnected()) {
90544648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                return;
90644648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            }
907cf1e1332e0b6880b4776d0bab31506092163423fJaewan Kim            mCallback.onRepeatModeChanged(mInstance, repeatMode);
908df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim        });
909df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim    }
910df4f8498f40e995abeedbbdf6053c252c0fa3350Jaewan Kim
9116ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon    void pushError(int errorCode, Bundle extras) {
9126ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon        mCallbackExecutor.execute(() -> {
9136ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon            if (!mInstance.isConnected()) {
9146ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon                return;
9156ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon            }
9166ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon            mCallback.onError(mInstance, errorCode, extras);
9176ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon        });
9186ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon    }
9196ae7465a068f4a63d679423a83c786f77e034b3cHyundo Moon
92035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // Should be used without a lock to prevent potential deadlock.
921b41bfb59dd52decb1e5c5952d6ed786f159d772dJaewan Kim    void onConnectedNotLocked(IMediaSession2 sessionBinder,
922d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim            final SessionCommandGroup2 allowedCommands,
92379f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            final int playerState,
92479f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            final long positionEventTimeMs,
92579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            final long positionMs,
92679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            final float playbackSpeed,
92779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            final long bufferedPositionMs,
92879f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon            final PlaybackInfo info,
92944648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            final int repeatMode,
93044648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            final int shuffleMode,
93144648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim            final List<MediaItem2> playlist,
9320261708bf17cb0469de8eb3c81455e2f48ff9433Jaewan Kim            final PendingIntent sessionActivity) {
93335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        if (DEBUG) {
934c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim            Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
935483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                    + ", allowedCommands=" + allowedCommands);
93635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
937c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim        boolean close = false;
93835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        try {
939483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim            if (sessionBinder == null || allowedCommands == null) {
94035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                // Connection rejected.
941c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim                close = true;
94235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                return;
94335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
94435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            synchronized (mLock) {
94535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                if (mIsReleased) {
94635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    return;
94735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                }
94835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                if (mSessionBinder != null) {
94935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    Log.e(TAG, "Cannot be notified about the connection result many times."
95035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                            + " Probably a bug or malicious app.");
951c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim                    close = true;
95235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    return;
95335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                }
954483261f98e9eeb6191d42873970a2ae9709ac1fcJaewan Kim                mAllowedCommands = allowedCommands;
95579f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                mPlayerState = playerState;
95679f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                mPositionEventTimeMs = positionEventTimeMs;
95779f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                mPositionMs = positionMs;
95879f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                mPlaybackSpeed = playbackSpeed;
95979f5e126cb9a8c6f72904d8cb4c611ee69405993Hyundo Moon                mBufferedPositionMs = bufferedPositionMs;
9600261708bf17cb0469de8eb3c81455e2f48ff9433Jaewan Kim                mPlaybackInfo = info;
96144648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                mRepeatMode = repeatMode;
96244648206f6a9c66f9f9478a8091685cb702349eeJaewan Kim                mShuffleMode = shuffleMode;
9630261708bf17cb0469de8eb3c81455e2f48ff9433Jaewan Kim                mPlaylist = playlist;
9640261708bf17cb0469de8eb3c81455e2f48ff9433Jaewan Kim                mSessionActivity = sessionActivity;
96535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                mSessionBinder = sessionBinder;
96635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                try {
96735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    // Implementation for the local binder is no-op,
96835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    // so can be used without worrying about deadlock.
96935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    mSessionBinder.asBinder().linkToDeath(mDeathRecipient, 0);
97035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                } catch (RemoteException e) {
9715aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim                    if (DEBUG) {
9725aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim                        Log.d(TAG, "Session died too early.", e);
9735aec249a238ff2dcb9bb5bc0434b7a17fa96a035Jaewan Kim                    }
974c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim                    close = true;
97535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                    return;
97635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                }
97735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
97835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // TODO(jaewan): Keep commands to prevents illegal API calls.
97935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            mCallbackExecutor.execute(() -> {
980c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim                // Note: We may trigger ControllerCallbacks with the initial values
981c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim                // But it's hard to define the order of the controller callbacks
982c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim                // Only notify about the
983d3d75358db7e3fe87805f1d91aa53abe1b0c2e92Jaewan Kim                mCallback.onConnected(mInstance, allowedCommands);
98435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            });
98535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        } finally {
986c4a9db19b2c4a4bf1283dd060147e17e48142beeJaewan Kim            if (close) {
98735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                // Trick to call release() without holding the lock, to prevent potential deadlock
98835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                // with the developer's custom lock within the ControllerCallback.onDisconnected().
989113a561d324e89b1db5edcc39c903c879e08dd94Jaewan Kim                mInstance.close();
99035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
99135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
99235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
99335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
994d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim    void onCustomCommand(final SessionCommand2 command, final Bundle args,
995bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            final ResultReceiver receiver) {
996bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        if (DEBUG) {
997bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            Log.d(TAG, "onCustomCommand cmd=" + command);
998bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        }
999bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        mCallbackExecutor.execute(() -> {
1000bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim            // TODO(jaewan): Double check if the controller exists.
1001d3d75358db7e3fe87805f1d91aa53abe1b0c2e92Jaewan Kim            mCallback.onCustomCommand(mInstance, command, args, receiver);
1002bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim        });
1003bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim    }
1004bf9606868a825bf9f606d4d3ec8d15e7f29b76a7Jaewan Kim
1005d5b878d0cf0c987caf18be697a0e1637fb632d66Jaewan Kim    void onAllowedCommandsChanged(final SessionCommandGroup2 commands) {
1006e0e207174080d93a97b5c0ab84dcf84dd47c730cHyundo Moon        mCallbackExecutor.execute(() -> {
1007e0e207174080d93a97b5c0ab84dcf84dd47c730cHyundo Moon            mCallback.onAllowedCommandsChanged(mInstance, commands);
1008e0e207174080d93a97b5c0ab84dcf84dd47c730cHyundo Moon        });
1009e0e207174080d93a97b5c0ab84dcf84dd47c730cHyundo Moon    }
1010e0e207174080d93a97b5c0ab84dcf84dd47c730cHyundo Moon
10119b67177ebfe5f46c0c6216f87f5ee98c03e5cbadJaewan Kim    void onCustomLayoutChanged(final List<CommandButton> layout) {
10129b67177ebfe5f46c0c6216f87f5ee98c03e5cbadJaewan Kim        mCallbackExecutor.execute(() -> {
1013d3d75358db7e3fe87805f1d91aa53abe1b0c2e92Jaewan Kim            mCallback.onCustomLayoutChanged(mInstance, layout);
10149b67177ebfe5f46c0c6216f87f5ee98c03e5cbadJaewan Kim        });
10159b67177ebfe5f46c0c6216f87f5ee98c03e5cbadJaewan Kim    }
10169b67177ebfe5f46c0c6216f87f5ee98c03e5cbadJaewan Kim
101735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    // This will be called on the main thread.
101835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    private class SessionServiceConnection implements ServiceConnection {
101935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        @Override
102035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        public void onServiceConnected(ComponentName name, IBinder service) {
102135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // Note that it's always main-thread.
102235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            if (DEBUG) {
102335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                Log.d(TAG, "onServiceConnected " + name + " " + this);
102435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
102535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // Sanity check
102635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            if (!mToken.getPackageName().equals(name.getPackageName())) {
102735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                Log.wtf(TAG, name + " was connected, but expected pkg="
102835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                        + mToken.getPackageName() + " with id=" + mToken.getId());
102935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                return;
103035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
103135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            final IMediaSession2 sessionBinder = IMediaSession2.Stub.asInterface(service);
103235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            connectToSession(sessionBinder);
103335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
103435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
103535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        @Override
103635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        public void onServiceDisconnected(ComponentName name) {
103735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // Temporal lose of the binding because of the service crash. System will automatically
103835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // rebind, so just no-op.
103935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // TODO(jaewan): Really? Either disconnect cleanly or
104035a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            if (DEBUG) {
104135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim                Log.w(TAG, "Session service " + name + " is disconnected.");
104235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            }
104335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
104435a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim
104535a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        @Override
104635a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        public void onBindingDied(ComponentName name) {
104735a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // Permanent lose of the binding because of the service package update or removed.
104835a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // This SessionServiceRecord will be removed accordingly, but forget session binder here
104935a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim            // for sure.
1050113a561d324e89b1db5edcc39c903c879e08dd94Jaewan Kim            mInstance.close();
105135a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim        }
105235a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim    }
1053991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1054991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim    public static final class PlaybackInfoImpl implements PlaybackInfoProvider {
1055991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1056991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private static final String KEY_PLAYBACK_TYPE =
1057991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim                "android.media.playbackinfo_impl.playback_type";
1058991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private static final String KEY_CONTROL_TYPE =
1059991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim                "android.media.playbackinfo_impl.control_type";
1060991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private static final String KEY_MAX_VOLUME =
1061991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim                "android.media.playbackinfo_impl.max_volume";
1062991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private static final String KEY_CURRENT_VOLUME =
1063991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim                "android.media.playbackinfo_impl.current_volume";
1064991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private static final String KEY_AUDIO_ATTRIBUTES =
1065991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim                "android.media.playbackinfo_impl.audio_attrs";
1066991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1067991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private final PlaybackInfo mInstance;
1068991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1069991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private final int mPlaybackType;
1070991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private final int mControlType;
1071991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private final int mMaxVolume;
1072991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private final int mCurrentVolume;
1073991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        private final AudioAttributes mAudioAttrs;
1074991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
10750de7babb75683eba98af2db52101d262372235daChristofer Åkersten        private PlaybackInfoImpl(int playbackType, AudioAttributes attrs, int controlType,
10760de7babb75683eba98af2db52101d262372235daChristofer Åkersten                int max, int current) {
1077991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            mPlaybackType = playbackType;
1078991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            mAudioAttrs = attrs;
1079991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            mControlType = controlType;
1080991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            mMaxVolume = max;
1081991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            mCurrentVolume = current;
1082991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            mInstance = new PlaybackInfo(this);
1083991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1084991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1085991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        @Override
1086991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        public int getPlaybackType_impl() {
1087991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            return mPlaybackType;
1088991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1089991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1090991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        @Override
1091991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        public AudioAttributes getAudioAttributes_impl() {
1092991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            return mAudioAttrs;
1093991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1094991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1095991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        @Override
1096991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        public int getControlType_impl() {
1097991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            return mControlType;
1098991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1099991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1100991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        @Override
1101991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        public int getMaxVolume_impl() {
1102991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            return mMaxVolume;
1103991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1104991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
1105991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        @Override
1106991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        public int getCurrentVolume_impl() {
1107991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            return mCurrentVolume;
1108991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1109991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
11100de7babb75683eba98af2db52101d262372235daChristofer Åkersten        PlaybackInfo getInstance() {
1111991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            return mInstance;
1112991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1113991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
11140de7babb75683eba98af2db52101d262372235daChristofer Åkersten        Bundle toBundle() {
1115991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            Bundle bundle = new Bundle();
1116991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            bundle.putInt(KEY_PLAYBACK_TYPE, mPlaybackType);
1117991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            bundle.putInt(KEY_CONTROL_TYPE, mControlType);
1118991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            bundle.putInt(KEY_MAX_VOLUME, mMaxVolume);
1119991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            bundle.putInt(KEY_CURRENT_VOLUME, mCurrentVolume);
1120991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            bundle.putParcelable(KEY_AUDIO_ATTRIBUTES, mAudioAttrs);
1121991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            return bundle;
1122991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1123991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
11240de7babb75683eba98af2db52101d262372235daChristofer Åkersten        static PlaybackInfo createPlaybackInfo(int playbackType, AudioAttributes attrs,
11250de7babb75683eba98af2db52101d262372235daChristofer Åkersten                int controlType, int max, int current) {
11260de7babb75683eba98af2db52101d262372235daChristofer Åkersten            return new PlaybackInfoImpl(playbackType, attrs, controlType, max, current)
1127991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim                    .getInstance();
1128991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1129991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
11300de7babb75683eba98af2db52101d262372235daChristofer Åkersten        static PlaybackInfo fromBundle(Bundle bundle) {
1131991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            if (bundle == null) {
1132991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim                return null;
1133991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            }
1134991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            final int volumeType = bundle.getInt(KEY_PLAYBACK_TYPE);
1135991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            final int volumeControl = bundle.getInt(KEY_CONTROL_TYPE);
1136991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            final int maxVolume = bundle.getInt(KEY_MAX_VOLUME);
1137991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            final int currentVolume = bundle.getInt(KEY_CURRENT_VOLUME);
1138991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim            final AudioAttributes attrs = bundle.getParcelable(KEY_AUDIO_ATTRIBUTES);
1139991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim
11400de7babb75683eba98af2db52101d262372235daChristofer Åkersten            return createPlaybackInfo(volumeType, attrs, volumeControl, maxVolume, currentVolume);
1141991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim        }
1142991a25e6fd2aa7456fa7686de0baf619ebd50c2cJaewan Kim    }
114335a6aa31951fa0178efe7245fe09e628275c2200Jaewan Kim}
1144