PageRenderTime 78ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/media/src/main/java/androidx/media/MediaController2ImplBase.java

https://gitlab.com/SkyDragon-OSP/platform_frameworks_support
Java | 1263 lines | 1137 code | 93 blank | 33 comment | 97 complexity | 5cb1f57f65ac735375cb26cc9a3f6235 MD5 | raw file
  1. /*
  2. * Copyright 2018 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package androidx.media;
  17. import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION;
  18. import static androidx.media.MediaConstants2.ARGUMENT_ALLOWED_COMMANDS;
  19. import static androidx.media.MediaConstants2.ARGUMENT_ARGUMENTS;
  20. import static androidx.media.MediaConstants2.ARGUMENT_BUFFERING_STATE;
  21. import static androidx.media.MediaConstants2.ARGUMENT_COMMAND_BUTTONS;
  22. import static androidx.media.MediaConstants2.ARGUMENT_COMMAND_CODE;
  23. import static androidx.media.MediaConstants2.ARGUMENT_CUSTOM_COMMAND;
  24. import static androidx.media.MediaConstants2.ARGUMENT_ERROR_CODE;
  25. import static androidx.media.MediaConstants2.ARGUMENT_EXTRAS;
  26. import static androidx.media.MediaConstants2.ARGUMENT_ICONTROLLER_CALLBACK;
  27. import static androidx.media.MediaConstants2.ARGUMENT_ITEM_COUNT;
  28. import static androidx.media.MediaConstants2.ARGUMENT_MEDIA_ID;
  29. import static androidx.media.MediaConstants2.ARGUMENT_MEDIA_ITEM;
  30. import static androidx.media.MediaConstants2.ARGUMENT_PACKAGE_NAME;
  31. import static androidx.media.MediaConstants2.ARGUMENT_PID;
  32. import static androidx.media.MediaConstants2.ARGUMENT_PLAYBACK_INFO;
  33. import static androidx.media.MediaConstants2.ARGUMENT_PLAYBACK_SPEED;
  34. import static androidx.media.MediaConstants2.ARGUMENT_PLAYBACK_STATE_COMPAT;
  35. import static androidx.media.MediaConstants2.ARGUMENT_PLAYER_STATE;
  36. import static androidx.media.MediaConstants2.ARGUMENT_PLAYLIST;
  37. import static androidx.media.MediaConstants2.ARGUMENT_PLAYLIST_INDEX;
  38. import static androidx.media.MediaConstants2.ARGUMENT_PLAYLIST_METADATA;
  39. import static androidx.media.MediaConstants2.ARGUMENT_QUERY;
  40. import static androidx.media.MediaConstants2.ARGUMENT_RATING;
  41. import static androidx.media.MediaConstants2.ARGUMENT_REPEAT_MODE;
  42. import static androidx.media.MediaConstants2.ARGUMENT_RESULT_RECEIVER;
  43. import static androidx.media.MediaConstants2.ARGUMENT_ROUTE_BUNDLE;
  44. import static androidx.media.MediaConstants2.ARGUMENT_SEEK_POSITION;
  45. import static androidx.media.MediaConstants2.ARGUMENT_SHUFFLE_MODE;
  46. import static androidx.media.MediaConstants2.ARGUMENT_UID;
  47. import static androidx.media.MediaConstants2.ARGUMENT_URI;
  48. import static androidx.media.MediaConstants2.ARGUMENT_VOLUME;
  49. import static androidx.media.MediaConstants2.ARGUMENT_VOLUME_DIRECTION;
  50. import static androidx.media.MediaConstants2.ARGUMENT_VOLUME_FLAGS;
  51. import static androidx.media.MediaConstants2.CONNECT_RESULT_CONNECTED;
  52. import static androidx.media.MediaConstants2.CONNECT_RESULT_DISCONNECTED;
  53. import static androidx.media.MediaConstants2.CONTROLLER_COMMAND_BY_COMMAND_CODE;
  54. import static androidx.media.MediaConstants2.CONTROLLER_COMMAND_BY_CUSTOM_COMMAND;
  55. import static androidx.media.MediaConstants2.CONTROLLER_COMMAND_CONNECT;
  56. import static androidx.media.MediaConstants2.CONTROLLER_COMMAND_DISCONNECT;
  57. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_ALLOWED_COMMANDS_CHANGED;
  58. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_BUFFERING_STATE_CHANGED;
  59. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_CHILDREN_CHANGED;
  60. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_CURRENT_MEDIA_ITEM_CHANGED;
  61. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_ERROR;
  62. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYBACK_INFO_CHANGED;
  63. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYBACK_SPEED_CHANGED;
  64. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYER_STATE_CHANGED;
  65. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYLIST_CHANGED;
  66. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_PLAYLIST_METADATA_CHANGED;
  67. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_REPEAT_MODE_CHANGED;
  68. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_ROUTES_INFO_CHANGED;
  69. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_SEARCH_RESULT_CHANGED;
  70. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_SEEK_COMPLETED;
  71. import static androidx.media.MediaConstants2.SESSION_EVENT_ON_SHUFFLE_MODE_CHANGED;
  72. import static androidx.media.MediaConstants2.SESSION_EVENT_SEND_CUSTOM_COMMAND;
  73. import static androidx.media.MediaConstants2.SESSION_EVENT_SET_CUSTOM_LAYOUT;
  74. import static androidx.media.MediaPlayerInterface.BUFFERING_STATE_UNKNOWN;
  75. import static androidx.media.MediaPlayerInterface.UNKNOWN_TIME;
  76. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE;
  77. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY;
  78. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE;
  79. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYBACK_RESET;
  80. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYBACK_SEEK_TO;
  81. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYBACK_SET_SPEED;
  82. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_ADD_ITEM;
  83. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM;
  84. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM;
  85. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST;
  86. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA;
  87. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE;
  88. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE;
  89. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_TO_NEXT_ITEM;
  90. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM;
  91. import static androidx.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_TO_PREV_ITEM;
  92. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_FAST_FORWARD;
  93. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID;
  94. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH;
  95. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_URI;
  96. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID;
  97. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH;
  98. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI;
  99. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_REWIND;
  100. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SELECT_ROUTE;
  101. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SET_RATING;
  102. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO;
  103. import static androidx.media.SessionCommand2.COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO;
  104. import static androidx.media.SessionCommand2.COMMAND_CODE_VOLUME_ADJUST_VOLUME;
  105. import static androidx.media.SessionCommand2.COMMAND_CODE_VOLUME_SET_VOLUME;
  106. import android.annotation.TargetApi;
  107. import android.app.PendingIntent;
  108. import android.content.Context;
  109. import android.net.Uri;
  110. import android.os.Build;
  111. import android.os.Bundle;
  112. import android.os.Handler;
  113. import android.os.HandlerThread;
  114. import android.os.IBinder;
  115. import android.os.Process;
  116. import android.os.RemoteException;
  117. import android.os.ResultReceiver;
  118. import android.os.SystemClock;
  119. import android.support.v4.media.MediaBrowserCompat;
  120. import android.support.v4.media.MediaMetadataCompat;
  121. import android.support.v4.media.session.MediaControllerCompat;
  122. import android.support.v4.media.session.MediaSessionCompat;
  123. import android.support.v4.media.session.PlaybackStateCompat;
  124. import android.util.Log;
  125. import androidx.annotation.GuardedBy;
  126. import androidx.annotation.NonNull;
  127. import androidx.annotation.Nullable;
  128. import androidx.core.app.BundleCompat;
  129. import androidx.media.MediaController2.ControllerCallback;
  130. import androidx.media.MediaController2.PlaybackInfo;
  131. import androidx.media.MediaController2.VolumeDirection;
  132. import androidx.media.MediaController2.VolumeFlags;
  133. import androidx.media.MediaPlaylistAgent.RepeatMode;
  134. import androidx.media.MediaPlaylistAgent.ShuffleMode;
  135. import androidx.media.MediaSession2.CommandButton;
  136. import java.util.List;
  137. import java.util.concurrent.Executor;
  138. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  139. class MediaController2ImplBase implements MediaController2.SupportLibraryImpl {
  140. private static final String TAG = "MC2ImplBase";
  141. private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
  142. // Note: Using {@code null} doesn't helpful here because MediaBrowserServiceCompat always wraps
  143. // the rootHints so it becomes non-null.
  144. static final Bundle sDefaultRootExtras = new Bundle();
  145. static {
  146. sDefaultRootExtras.putBoolean(MediaConstants2.ROOT_EXTRA_DEFAULT, true);
  147. }
  148. private final Context mContext;
  149. private final Object mLock = new Object();
  150. private final SessionToken2 mToken;
  151. private final ControllerCallback mCallback;
  152. private final Executor mCallbackExecutor;
  153. private final IBinder.DeathRecipient mDeathRecipient;
  154. private final HandlerThread mHandlerThread;
  155. private final Handler mHandler;
  156. private MediaController2 mInstance;
  157. @GuardedBy("mLock")
  158. private MediaBrowserCompat mBrowserCompat;
  159. @GuardedBy("mLock")
  160. private boolean mIsReleased;
  161. @GuardedBy("mLock")
  162. private List<MediaItem2> mPlaylist;
  163. @GuardedBy("mLock")
  164. private MediaMetadata2 mPlaylistMetadata;
  165. @GuardedBy("mLock")
  166. private @RepeatMode int mRepeatMode;
  167. @GuardedBy("mLock")
  168. private @ShuffleMode int mShuffleMode;
  169. @GuardedBy("mLock")
  170. private int mPlayerState;
  171. @GuardedBy("mLock")
  172. private MediaItem2 mCurrentMediaItem;
  173. @GuardedBy("mLock")
  174. private int mBufferingState;
  175. @GuardedBy("mLock")
  176. private PlaybackInfo mPlaybackInfo;
  177. @GuardedBy("mLock")
  178. private SessionCommandGroup2 mAllowedCommands;
  179. // Media 1.0 variables
  180. @GuardedBy("mLock")
  181. private MediaControllerCompat mControllerCompat;
  182. @GuardedBy("mLock")
  183. private ControllerCompatCallback mControllerCompatCallback;
  184. @GuardedBy("mLock")
  185. private PlaybackStateCompat mPlaybackStateCompat;
  186. @GuardedBy("mLock")
  187. private MediaMetadataCompat mMediaMetadataCompat;
  188. // Assignment should be used with the lock hold, but should be used without a lock to prevent
  189. // potential deadlock.
  190. @GuardedBy("mLock")
  191. private volatile boolean mConnected;
  192. MediaController2ImplBase(@NonNull Context context, @NonNull SessionToken2 token,
  193. @NonNull Executor executor, @NonNull ControllerCallback callback) {
  194. super();
  195. if (context == null) {
  196. throw new IllegalArgumentException("context shouldn't be null");
  197. }
  198. if (token == null) {
  199. throw new IllegalArgumentException("token shouldn't be null");
  200. }
  201. if (callback == null) {
  202. throw new IllegalArgumentException("callback shouldn't be null");
  203. }
  204. if (executor == null) {
  205. throw new IllegalArgumentException("executor shouldn't be null");
  206. }
  207. mContext = context;
  208. mHandlerThread = new HandlerThread("MediaController2_Thread");
  209. mHandlerThread.start();
  210. mHandler = new Handler(mHandlerThread.getLooper());
  211. mToken = token;
  212. mCallback = callback;
  213. mCallbackExecutor = executor;
  214. mDeathRecipient = new IBinder.DeathRecipient() {
  215. @Override
  216. public void binderDied() {
  217. MediaController2ImplBase.this.close();
  218. }
  219. };
  220. initialize();
  221. }
  222. @Override
  223. public void setInstance(MediaController2 controller) {
  224. mInstance = controller;
  225. }
  226. @Override
  227. public void close() {
  228. if (DEBUG) {
  229. //Log.d(TAG, "release from " + mToken, new IllegalStateException());
  230. }
  231. synchronized (mLock) {
  232. if (mIsReleased) {
  233. // Prevent re-enterance from the ControllerCallback.onDisconnected()
  234. return;
  235. }
  236. mHandler.removeCallbacksAndMessages(null);
  237. if (Build.VERSION.SDK_INT >= 18) {
  238. mHandlerThread.quitSafely();
  239. } else {
  240. mHandlerThread.quit();
  241. }
  242. mIsReleased = true;
  243. // Send command before the unregister callback to use mIControllerCallback in the
  244. // callback.
  245. sendCommand(CONTROLLER_COMMAND_DISCONNECT);
  246. if (mControllerCompat != null) {
  247. mControllerCompat.unregisterCallback(mControllerCompatCallback);
  248. }
  249. if (mBrowserCompat != null) {
  250. mBrowserCompat.disconnect();
  251. mBrowserCompat = null;
  252. }
  253. if (mControllerCompat != null) {
  254. mControllerCompat.unregisterCallback(mControllerCompatCallback);
  255. mControllerCompat = null;
  256. }
  257. mConnected = false;
  258. }
  259. mCallbackExecutor.execute(new Runnable() {
  260. @Override
  261. public void run() {
  262. mCallback.onDisconnected(mInstance);
  263. }
  264. });
  265. }
  266. @Override
  267. public @NonNull SessionToken2 getSessionToken() {
  268. return mToken;
  269. }
  270. @Override
  271. public boolean isConnected() {
  272. synchronized (mLock) {
  273. return mConnected;
  274. }
  275. }
  276. @Override
  277. public void play() {
  278. synchronized (mLock) {
  279. if (!mConnected) {
  280. Log.w(TAG, "Session isn't active", new IllegalStateException());
  281. return;
  282. }
  283. sendCommand(COMMAND_CODE_PLAYBACK_PLAY);
  284. }
  285. }
  286. @Override
  287. public void pause() {
  288. synchronized (mLock) {
  289. if (!mConnected) {
  290. Log.w(TAG, "Session isn't active", new IllegalStateException());
  291. return;
  292. }
  293. sendCommand(COMMAND_CODE_PLAYBACK_PAUSE);
  294. }
  295. }
  296. @Override
  297. public void reset() {
  298. synchronized (mLock) {
  299. if (!mConnected) {
  300. Log.w(TAG, "Session isn't active", new IllegalStateException());
  301. return;
  302. }
  303. sendCommand(COMMAND_CODE_PLAYBACK_RESET);
  304. }
  305. }
  306. @Override
  307. public void prepare() {
  308. synchronized (mLock) {
  309. if (!mConnected) {
  310. Log.w(TAG, "Session isn't active", new IllegalStateException());
  311. return;
  312. }
  313. sendCommand(COMMAND_CODE_PLAYBACK_PREPARE);
  314. }
  315. }
  316. @Override
  317. public void fastForward() {
  318. synchronized (mLock) {
  319. if (!mConnected) {
  320. Log.w(TAG, "Session isn't active", new IllegalStateException());
  321. return;
  322. }
  323. sendCommand(COMMAND_CODE_SESSION_FAST_FORWARD);
  324. }
  325. }
  326. @Override
  327. public void rewind() {
  328. synchronized (mLock) {
  329. if (!mConnected) {
  330. Log.w(TAG, "Session isn't active", new IllegalStateException());
  331. return;
  332. }
  333. sendCommand(COMMAND_CODE_SESSION_REWIND);
  334. }
  335. }
  336. @Override
  337. public void seekTo(long pos) {
  338. synchronized (mLock) {
  339. if (!mConnected) {
  340. Log.w(TAG, "Session isn't active", new IllegalStateException());
  341. return;
  342. }
  343. Bundle args = new Bundle();
  344. args.putLong(ARGUMENT_SEEK_POSITION, pos);
  345. sendCommand(COMMAND_CODE_PLAYBACK_SEEK_TO, args);
  346. }
  347. }
  348. @Override
  349. public void skipForward() {
  350. // To match with KEYCODE_MEDIA_SKIP_FORWARD
  351. }
  352. @Override
  353. public void skipBackward() {
  354. // To match with KEYCODE_MEDIA_SKIP_BACKWARD
  355. }
  356. @Override
  357. public void playFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
  358. synchronized (mLock) {
  359. if (!mConnected) {
  360. Log.w(TAG, "Session isn't active", new IllegalStateException());
  361. return;
  362. }
  363. Bundle args = new Bundle();
  364. args.putString(ARGUMENT_MEDIA_ID, mediaId);
  365. args.putBundle(ARGUMENT_EXTRAS, extras);
  366. sendCommand(COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID, args);
  367. }
  368. }
  369. @Override
  370. public void playFromSearch(@NonNull String query, @Nullable Bundle extras) {
  371. synchronized (mLock) {
  372. if (!mConnected) {
  373. Log.w(TAG, "Session isn't active", new IllegalStateException());
  374. return;
  375. }
  376. Bundle args = new Bundle();
  377. args.putString(ARGUMENT_QUERY, query);
  378. args.putBundle(ARGUMENT_EXTRAS, extras);
  379. sendCommand(COMMAND_CODE_SESSION_PLAY_FROM_SEARCH, args);
  380. }
  381. }
  382. @Override
  383. public void playFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
  384. synchronized (mLock) {
  385. if (!mConnected) {
  386. Log.w(TAG, "Session isn't active", new IllegalStateException());
  387. return;
  388. }
  389. Bundle args = new Bundle();
  390. args.putParcelable(ARGUMENT_URI, uri);
  391. args.putBundle(ARGUMENT_EXTRAS, extras);
  392. sendCommand(COMMAND_CODE_SESSION_PLAY_FROM_URI, args);
  393. }
  394. }
  395. @Override
  396. public void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
  397. synchronized (mLock) {
  398. if (!mConnected) {
  399. Log.w(TAG, "Session isn't active", new IllegalStateException());
  400. return;
  401. }
  402. Bundle args = new Bundle();
  403. args.putString(ARGUMENT_MEDIA_ID, mediaId);
  404. args.putBundle(ARGUMENT_EXTRAS, extras);
  405. sendCommand(COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID, args);
  406. }
  407. }
  408. @Override
  409. public void prepareFromSearch(@NonNull String query, @Nullable Bundle extras) {
  410. synchronized (mLock) {
  411. if (!mConnected) {
  412. Log.w(TAG, "Session isn't active", new IllegalStateException());
  413. return;
  414. }
  415. Bundle args = new Bundle();
  416. args.putString(ARGUMENT_QUERY, query);
  417. args.putBundle(ARGUMENT_EXTRAS, extras);
  418. sendCommand(COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH, args);
  419. }
  420. }
  421. @Override
  422. public void prepareFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
  423. synchronized (mLock) {
  424. if (!mConnected) {
  425. Log.w(TAG, "Session isn't active", new IllegalStateException());
  426. return;
  427. }
  428. Bundle args = new Bundle();
  429. args.putParcelable(ARGUMENT_URI, uri);
  430. args.putBundle(ARGUMENT_EXTRAS, extras);
  431. sendCommand(COMMAND_CODE_SESSION_PREPARE_FROM_URI, args);
  432. }
  433. }
  434. @Override
  435. public void setVolumeTo(int value, @VolumeFlags int flags) {
  436. synchronized (mLock) {
  437. if (!mConnected) {
  438. Log.w(TAG, "Session isn't active", new IllegalStateException());
  439. return;
  440. }
  441. Bundle args = new Bundle();
  442. args.putInt(ARGUMENT_VOLUME, value);
  443. args.putInt(ARGUMENT_VOLUME_FLAGS, flags);
  444. sendCommand(COMMAND_CODE_VOLUME_SET_VOLUME, args);
  445. }
  446. }
  447. @Override
  448. public void adjustVolume(@VolumeDirection int direction, @VolumeFlags int flags) {
  449. synchronized (mLock) {
  450. if (!mConnected) {
  451. Log.w(TAG, "Session isn't active", new IllegalStateException());
  452. return;
  453. }
  454. Bundle args = new Bundle();
  455. args.putInt(ARGUMENT_VOLUME_DIRECTION, direction);
  456. args.putInt(ARGUMENT_VOLUME_FLAGS, flags);
  457. sendCommand(COMMAND_CODE_VOLUME_ADJUST_VOLUME, args);
  458. }
  459. }
  460. @Override
  461. public @Nullable PendingIntent getSessionActivity() {
  462. synchronized (mLock) {
  463. if (!mConnected) {
  464. Log.w(TAG, "Session isn't active", new IllegalStateException());
  465. return null;
  466. }
  467. return mControllerCompat.getSessionActivity();
  468. }
  469. }
  470. @Override
  471. public int getPlayerState() {
  472. synchronized (mLock) {
  473. return mPlayerState;
  474. }
  475. }
  476. @Override
  477. public long getDuration() {
  478. synchronized (mLock) {
  479. if (mMediaMetadataCompat != null
  480. && mMediaMetadataCompat.containsKey(METADATA_KEY_DURATION)) {
  481. return mMediaMetadataCompat.getLong(METADATA_KEY_DURATION);
  482. }
  483. }
  484. return MediaPlayerInterface.UNKNOWN_TIME;
  485. }
  486. @Override
  487. public long getCurrentPosition() {
  488. synchronized (mLock) {
  489. if (!mConnected) {
  490. Log.w(TAG, "Session isn't active", new IllegalStateException());
  491. return UNKNOWN_TIME;
  492. }
  493. if (mPlaybackStateCompat != null) {
  494. long timeDiff = (mInstance.mTimeDiff != null) ? mInstance.mTimeDiff
  495. : SystemClock.elapsedRealtime()
  496. - mPlaybackStateCompat.getLastPositionUpdateTime();
  497. long expectedPosition = mPlaybackStateCompat.getPosition()
  498. + (long) (mPlaybackStateCompat.getPlaybackSpeed() * timeDiff);
  499. return Math.max(0, expectedPosition);
  500. }
  501. return UNKNOWN_TIME;
  502. }
  503. }
  504. @Override
  505. public float getPlaybackSpeed() {
  506. synchronized (mLock) {
  507. if (!mConnected) {
  508. Log.w(TAG, "Session isn't active", new IllegalStateException());
  509. return 0f;
  510. }
  511. return (mPlaybackStateCompat == null) ? 0f : mPlaybackStateCompat.getPlaybackSpeed();
  512. }
  513. }
  514. @Override
  515. public void setPlaybackSpeed(float speed) {
  516. synchronized (mLock) {
  517. if (!mConnected) {
  518. Log.w(TAG, "Session isn't active", new IllegalStateException());
  519. return;
  520. }
  521. Bundle args = new Bundle();
  522. args.putFloat(ARGUMENT_PLAYBACK_SPEED, speed);
  523. sendCommand(COMMAND_CODE_PLAYBACK_SET_SPEED, args);
  524. }
  525. }
  526. @Override
  527. public @MediaPlayerInterface.BuffState int getBufferingState() {
  528. synchronized (mLock) {
  529. if (!mConnected) {
  530. Log.w(TAG, "Session isn't active", new IllegalStateException());
  531. return BUFFERING_STATE_UNKNOWN;
  532. }
  533. return mBufferingState;
  534. }
  535. }
  536. @Override
  537. public long getBufferedPosition() {
  538. synchronized (mLock) {
  539. if (!mConnected) {
  540. Log.w(TAG, "Session isn't active", new IllegalStateException());
  541. return UNKNOWN_TIME;
  542. }
  543. return (mPlaybackStateCompat == null) ? UNKNOWN_TIME
  544. : mPlaybackStateCompat.getBufferedPosition();
  545. }
  546. }
  547. @Override
  548. public @Nullable PlaybackInfo getPlaybackInfo() {
  549. synchronized (mLock) {
  550. return mPlaybackInfo;
  551. }
  552. }
  553. @Override
  554. public void setRating(@NonNull String mediaId, @NonNull Rating2 rating) {
  555. synchronized (mLock) {
  556. if (!mConnected) {
  557. Log.w(TAG, "Session isn't active", new IllegalStateException());
  558. return;
  559. }
  560. Bundle args = new Bundle();
  561. args.putString(ARGUMENT_MEDIA_ID, mediaId);
  562. args.putBundle(ARGUMENT_RATING, rating.toBundle());
  563. sendCommand(COMMAND_CODE_SESSION_SET_RATING, args);
  564. }
  565. }
  566. @Override
  567. public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
  568. @Nullable ResultReceiver cb) {
  569. synchronized (mLock) {
  570. if (!mConnected) {
  571. Log.w(TAG, "Session isn't active", new IllegalStateException());
  572. return;
  573. }
  574. Bundle bundle = new Bundle();
  575. bundle.putBundle(ARGUMENT_CUSTOM_COMMAND, command.toBundle());
  576. bundle.putBundle(ARGUMENT_ARGUMENTS, args);
  577. sendCommand(CONTROLLER_COMMAND_BY_CUSTOM_COMMAND, bundle, cb);
  578. }
  579. }
  580. @Override
  581. public @Nullable List<MediaItem2> getPlaylist() {
  582. synchronized (mLock) {
  583. return mPlaylist;
  584. }
  585. }
  586. @Override
  587. public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
  588. if (list == null) {
  589. throw new IllegalArgumentException("list shouldn't be null");
  590. }
  591. Bundle args = new Bundle();
  592. args.putParcelableArray(ARGUMENT_PLAYLIST, MediaUtils2.toMediaItem2ParcelableArray(list));
  593. args.putBundle(ARGUMENT_PLAYLIST_METADATA, metadata == null ? null : metadata.toBundle());
  594. sendCommand(COMMAND_CODE_PLAYLIST_SET_LIST, args);
  595. }
  596. @Override
  597. public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
  598. Bundle args = new Bundle();
  599. args.putBundle(ARGUMENT_PLAYLIST_METADATA, metadata == null ? null : metadata.toBundle());
  600. sendCommand(COMMAND_CODE_PLAYLIST_SET_LIST_METADATA, args);
  601. }
  602. @Override
  603. public @Nullable MediaMetadata2 getPlaylistMetadata() {
  604. synchronized (mLock) {
  605. return mPlaylistMetadata;
  606. }
  607. }
  608. @Override
  609. public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
  610. Bundle args = new Bundle();
  611. args.putInt(ARGUMENT_PLAYLIST_INDEX, index);
  612. args.putBundle(ARGUMENT_MEDIA_ITEM, item.toBundle());
  613. sendCommand(COMMAND_CODE_PLAYLIST_ADD_ITEM, args);
  614. }
  615. @Override
  616. public void removePlaylistItem(@NonNull MediaItem2 item) {
  617. Bundle args = new Bundle();
  618. args.putBundle(ARGUMENT_MEDIA_ITEM, item.toBundle());
  619. sendCommand(COMMAND_CODE_PLAYLIST_REMOVE_ITEM, args);
  620. }
  621. @Override
  622. public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
  623. Bundle args = new Bundle();
  624. args.putInt(ARGUMENT_PLAYLIST_INDEX, index);
  625. args.putBundle(ARGUMENT_MEDIA_ITEM, item.toBundle());
  626. sendCommand(COMMAND_CODE_PLAYLIST_REPLACE_ITEM, args);
  627. }
  628. @Override
  629. public MediaItem2 getCurrentMediaItem() {
  630. synchronized (mLock) {
  631. return mCurrentMediaItem;
  632. }
  633. }
  634. @Override
  635. public void skipToPreviousItem() {
  636. sendCommand(COMMAND_CODE_PLAYLIST_SKIP_TO_PREV_ITEM);
  637. }
  638. @Override
  639. public void skipToNextItem() {
  640. sendCommand(COMMAND_CODE_PLAYLIST_SKIP_TO_NEXT_ITEM);
  641. }
  642. @Override
  643. public void skipToPlaylistItem(@NonNull MediaItem2 item) {
  644. Bundle args = new Bundle();
  645. args.putBundle(ARGUMENT_MEDIA_ITEM, item.toBundle());
  646. sendCommand(COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM, args);
  647. }
  648. @Override
  649. public @RepeatMode int getRepeatMode() {
  650. synchronized (mLock) {
  651. return mRepeatMode;
  652. }
  653. }
  654. @Override
  655. public void setRepeatMode(@RepeatMode int repeatMode) {
  656. Bundle args = new Bundle();
  657. args.putInt(ARGUMENT_REPEAT_MODE, repeatMode);
  658. sendCommand(COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE, args);
  659. }
  660. @Override
  661. public @ShuffleMode int getShuffleMode() {
  662. synchronized (mLock) {
  663. return mShuffleMode;
  664. }
  665. }
  666. @Override
  667. public void setShuffleMode(@ShuffleMode int shuffleMode) {
  668. Bundle args = new Bundle();
  669. args.putInt(ARGUMENT_SHUFFLE_MODE, shuffleMode);
  670. sendCommand(COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE, args);
  671. }
  672. @Override
  673. public void subscribeRoutesInfo() {
  674. sendCommand(COMMAND_CODE_SESSION_SUBSCRIBE_ROUTES_INFO);
  675. }
  676. @Override
  677. public void unsubscribeRoutesInfo() {
  678. sendCommand(COMMAND_CODE_SESSION_UNSUBSCRIBE_ROUTES_INFO);
  679. }
  680. @Override
  681. public void selectRoute(@NonNull Bundle route) {
  682. if (route == null) {
  683. throw new IllegalArgumentException("route shouldn't be null");
  684. }
  685. Bundle args = new Bundle();
  686. args.putBundle(ARGUMENT_ROUTE_BUNDLE, route);
  687. sendCommand(COMMAND_CODE_SESSION_SELECT_ROUTE, args);
  688. }
  689. @Override
  690. public @NonNull Context getContext() {
  691. return mContext;
  692. }
  693. @Override
  694. public @NonNull ControllerCallback getCallback() {
  695. return mCallback;
  696. }
  697. @Override
  698. public @NonNull Executor getCallbackExecutor() {
  699. return mCallbackExecutor;
  700. }
  701. @Override
  702. public @Nullable MediaBrowserCompat getBrowserCompat() {
  703. synchronized (mLock) {
  704. return mBrowserCompat;
  705. }
  706. }
  707. // Should be used without a lock to prevent potential deadlock.
  708. void onConnectedNotLocked(Bundle data) {
  709. data.setClassLoader(MediaSession2.class.getClassLoader());
  710. // is enough or should we pass it while connecting?
  711. final SessionCommandGroup2 allowedCommands = SessionCommandGroup2.fromBundle(
  712. data.getBundle(ARGUMENT_ALLOWED_COMMANDS));
  713. final int playerState = data.getInt(ARGUMENT_PLAYER_STATE);
  714. final int bufferingState = data.getInt(ARGUMENT_BUFFERING_STATE);
  715. final PlaybackStateCompat playbackStateCompat = data.getParcelable(
  716. ARGUMENT_PLAYBACK_STATE_COMPAT);
  717. final int repeatMode = data.getInt(ARGUMENT_REPEAT_MODE);
  718. final int shuffleMode = data.getInt(ARGUMENT_SHUFFLE_MODE);
  719. final List<MediaItem2> playlist = MediaUtils2.fromMediaItem2ParcelableArray(
  720. data.getParcelableArray(ARGUMENT_PLAYLIST));
  721. final MediaItem2 currentMediaItem = MediaItem2.fromBundle(
  722. data.getBundle(ARGUMENT_MEDIA_ITEM));
  723. final PlaybackInfo playbackInfo =
  724. PlaybackInfo.fromBundle(data.getBundle(ARGUMENT_PLAYBACK_INFO));
  725. final MediaMetadata2 metadata = MediaMetadata2.fromBundle(
  726. data.getBundle(ARGUMENT_PLAYLIST_METADATA));
  727. if (DEBUG) {
  728. Log.d(TAG, "onConnectedNotLocked sessionCompatToken=" + mToken.getSessionCompatToken()
  729. + ", allowedCommands=" + allowedCommands);
  730. }
  731. boolean close = false;
  732. try {
  733. synchronized (mLock) {
  734. if (mIsReleased) {
  735. return;
  736. }
  737. if (mConnected) {
  738. Log.e(TAG, "Cannot be notified about the connection result many times."
  739. + " Probably a bug or malicious app.");
  740. close = true;
  741. return;
  742. }
  743. mAllowedCommands = allowedCommands;
  744. mPlayerState = playerState;
  745. mBufferingState = bufferingState;
  746. mPlaybackStateCompat = playbackStateCompat;
  747. mRepeatMode = repeatMode;
  748. mShuffleMode = shuffleMode;
  749. mPlaylist = playlist;
  750. mCurrentMediaItem = currentMediaItem;
  751. mPlaylistMetadata = metadata;
  752. mConnected = true;
  753. mPlaybackInfo = playbackInfo;
  754. }
  755. mCallbackExecutor.execute(new Runnable() {
  756. @Override
  757. public void run() {
  758. // Note: We may trigger ControllerCallbacks with the initial values
  759. // But it's hard to define the order of the controller callbacks
  760. // Only notify about the
  761. mCallback.onConnected(mInstance, allowedCommands);
  762. }
  763. });
  764. } finally {
  765. if (close) {
  766. // Trick to call release() without holding the lock, to prevent potential deadlock
  767. // with the developer's custom lock within the ControllerCallback.onDisconnected().
  768. close();
  769. }
  770. }
  771. }
  772. private void initialize() {
  773. if (mToken.getType() == SessionToken2.TYPE_SESSION) {
  774. synchronized (mLock) {
  775. mBrowserCompat = null;
  776. }
  777. connectToSession(mToken.getSessionCompatToken());
  778. } else {
  779. connectToService();
  780. }
  781. }
  782. private void connectToSession(MediaSessionCompat.Token sessionCompatToken) {
  783. MediaControllerCompat controllerCompat = null;
  784. try {
  785. controllerCompat = new MediaControllerCompat(mContext, sessionCompatToken);
  786. } catch (RemoteException e) {
  787. e.printStackTrace();
  788. }
  789. synchronized (mLock) {
  790. mControllerCompat = controllerCompat;
  791. mControllerCompatCallback = new ControllerCompatCallback();
  792. mControllerCompat.registerCallback(mControllerCompatCallback, mHandler);
  793. }
  794. if (controllerCompat.isSessionReady()) {
  795. sendCommand(CONTROLLER_COMMAND_CONNECT, new ResultReceiver(mHandler) {
  796. @Override
  797. protected void onReceiveResult(int resultCode, Bundle resultData) {
  798. if (!mHandlerThread.isAlive()) {
  799. return;
  800. }
  801. switch (resultCode) {
  802. case CONNECT_RESULT_CONNECTED:
  803. onConnectedNotLocked(resultData);
  804. break;
  805. case CONNECT_RESULT_DISCONNECTED:
  806. mCallbackExecutor.execute(new Runnable() {
  807. @Override
  808. public void run() {
  809. mCallback.onDisconnected(mInstance);
  810. }
  811. });
  812. close();
  813. break;
  814. }
  815. }
  816. });
  817. }
  818. }
  819. private void connectToService() {
  820. mCallbackExecutor.execute(new Runnable() {
  821. @Override
  822. public void run() {
  823. synchronized (mLock) {
  824. mBrowserCompat = new MediaBrowserCompat(mContext, mToken.getComponentName(),
  825. new ConnectionCallback(), sDefaultRootExtras);
  826. mBrowserCompat.connect();
  827. }
  828. }
  829. });
  830. }
  831. private void sendCommand(int commandCode) {
  832. sendCommand(commandCode, null);
  833. }
  834. private void sendCommand(int commandCode, Bundle args) {
  835. if (args == null) {
  836. args = new Bundle();
  837. }
  838. args.putInt(ARGUMENT_COMMAND_CODE, commandCode);
  839. sendCommand(CONTROLLER_COMMAND_BY_COMMAND_CODE, args, null);
  840. }
  841. private void sendCommand(String command) {
  842. sendCommand(command, null, null);
  843. }
  844. private void sendCommand(String command, ResultReceiver receiver) {
  845. sendCommand(command, null, receiver);
  846. }
  847. private void sendCommand(String command, Bundle args, ResultReceiver receiver) {
  848. if (args == null) {
  849. args = new Bundle();
  850. }
  851. MediaControllerCompat controller;
  852. ControllerCompatCallback callback;
  853. synchronized (mLock) {
  854. controller = mControllerCompat;
  855. callback = mControllerCompatCallback;
  856. }
  857. BundleCompat.putBinder(args, ARGUMENT_ICONTROLLER_CALLBACK,
  858. callback.getIControllerCallback().asBinder());
  859. args.putString(ARGUMENT_PACKAGE_NAME, mContext.getPackageName());
  860. args.putInt(ARGUMENT_UID, Process.myUid());
  861. args.putInt(ARGUMENT_PID, Process.myPid());
  862. controller.sendCommand(command, args, receiver);
  863. }
  864. private class ConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
  865. @Override
  866. public void onConnected() {
  867. MediaBrowserCompat browser = getBrowserCompat();
  868. if (browser != null) {
  869. connectToSession(browser.getSessionToken());
  870. } else if (DEBUG) {
  871. Log.d(TAG, "Controller is closed prematually", new IllegalStateException());
  872. }
  873. }
  874. @Override
  875. public void onConnectionSuspended() {
  876. close();
  877. }
  878. @Override
  879. public void onConnectionFailed() {
  880. close();
  881. }
  882. }
  883. private final class ControllerCompatCallback extends MediaControllerCompat.Callback {
  884. @Override
  885. public void onSessionReady() {
  886. sendCommand(CONTROLLER_COMMAND_CONNECT, new ResultReceiver(mHandler) {
  887. @Override
  888. protected void onReceiveResult(int resultCode, Bundle resultData) {
  889. if (!mHandlerThread.isAlive()) {
  890. return;
  891. }
  892. switch (resultCode) {
  893. case CONNECT_RESULT_CONNECTED:
  894. onConnectedNotLocked(resultData);
  895. break;
  896. case CONNECT_RESULT_DISCONNECTED:
  897. mCallbackExecutor.execute(new Runnable() {
  898. @Override
  899. public void run() {
  900. mCallback.onDisconnected(mInstance);
  901. }
  902. });
  903. close();
  904. break;
  905. }
  906. }
  907. });
  908. }
  909. @Override
  910. public void onSessionDestroyed() {
  911. close();
  912. }
  913. @Override
  914. public void onPlaybackStateChanged(PlaybackStateCompat state) {
  915. synchronized (mLock) {
  916. mPlaybackStateCompat = state;
  917. }
  918. }
  919. @Override
  920. public void onMetadataChanged(MediaMetadataCompat metadata) {
  921. synchronized (mLock) {
  922. mMediaMetadataCompat = metadata;
  923. }
  924. }
  925. @Override
  926. public void onSessionEvent(String event, Bundle extras) {
  927. if (extras != null) {
  928. extras.setClassLoader(MediaSession2.class.getClassLoader());
  929. }
  930. switch (event) {
  931. case SESSION_EVENT_ON_ALLOWED_COMMANDS_CHANGED: {
  932. final SessionCommandGroup2 allowedCommands = SessionCommandGroup2.fromBundle(
  933. extras.getBundle(ARGUMENT_ALLOWED_COMMANDS));
  934. synchronized (mLock) {
  935. mAllowedCommands = allowedCommands;
  936. }
  937. mCallbackExecutor.execute(new Runnable() {
  938. @Override
  939. public void run() {
  940. mCallback.onAllowedCommandsChanged(mInstance, allowedCommands);
  941. }
  942. });
  943. break;
  944. }
  945. case SESSION_EVENT_ON_PLAYER_STATE_CHANGED: {
  946. final int playerState = extras.getInt(ARGUMENT_PLAYER_STATE);
  947. PlaybackStateCompat state =
  948. extras.getParcelable(ARGUMENT_PLAYBACK_STATE_COMPAT);
  949. if (state == null) {
  950. return;
  951. }
  952. synchronized (mLock) {
  953. mPlayerState = playerState;
  954. mPlaybackStateCompat = state;
  955. }
  956. mCallbackExecutor.execute(new Runnable() {
  957. @Override
  958. public void run() {
  959. mCallback.onPlayerStateChanged(mInstance, playerState);
  960. }
  961. });
  962. break;
  963. }
  964. case SESSION_EVENT_ON_CURRENT_MEDIA_ITEM_CHANGED: {
  965. final MediaItem2 item = MediaItem2.fromBundle(
  966. extras.getBundle(ARGUMENT_MEDIA_ITEM));
  967. synchronized (mLock) {
  968. mCurrentMediaItem = item;
  969. }
  970. mCallbackExecutor.execute(new Runnable() {
  971. @Override
  972. public void run() {
  973. mCallback.onCurrentMediaItemChanged(mInstance, item);
  974. }
  975. });
  976. break;
  977. }
  978. case SESSION_EVENT_ON_ERROR: {
  979. final int errorCode = extras.getInt(ARGUMENT_ERROR_CODE);
  980. final Bundle errorExtras = extras.getBundle(ARGUMENT_EXTRAS);
  981. mCallbackExecutor.execute(new Runnable() {
  982. @Override
  983. public void run() {
  984. mCallback.onError(mInstance, errorCode, errorExtras);
  985. }
  986. });
  987. break;
  988. }
  989. case SESSION_EVENT_ON_ROUTES_INFO_CHANGED: {
  990. final List<Bundle> routes = MediaUtils2.toBundleList(
  991. extras.getParcelableArray(ARGUMENT_ROUTE_BUNDLE));
  992. mCallbackExecutor.execute(new Runnable() {
  993. @Override
  994. public void run() {
  995. mCallback.onRoutesInfoChanged(mInstance, routes);
  996. }
  997. });
  998. break;
  999. }
  1000. case SESSION_EVENT_ON_PLAYLIST_CHANGED: {
  1001. final MediaMetadata2 playlistMetadata = MediaMetadata2.fromBundle(
  1002. extras.getBundle(ARGUMENT_PLAYLIST_METADATA));
  1003. final List<MediaItem2> playlist = MediaUtils2.fromMediaItem2ParcelableArray(
  1004. extras.getParcelableArray(ARGUMENT_PLAYLIST));
  1005. synchronized (mLock) {
  1006. mPlaylist = playlist;
  1007. mPlaylistMetadata = playlistMetadata;
  1008. }
  1009. mCallbackExecutor.execute(new Runnable() {
  1010. @Override
  1011. public void run() {
  1012. mCallback.onPlaylistChanged(mInstance, playlist, playlistMetadata);
  1013. }
  1014. });
  1015. break;
  1016. }
  1017. case SESSION_EVENT_ON_PLAYLIST_METADATA_CHANGED: {
  1018. final MediaMetadata2 playlistMetadata = MediaMetadata2.fromBundle(
  1019. extras.getBundle(ARGUMENT_PLAYLIST_METADATA));
  1020. synchronized (mLock) {
  1021. mPlaylistMetadata = playlistMetadata;
  1022. }
  1023. mCallbackExecutor.execute(new Runnable() {
  1024. @Override
  1025. public void run() {
  1026. mCallback.onPlaylistMetadataChanged(mInstance, playlistMetadata);
  1027. }
  1028. });
  1029. break;
  1030. }
  1031. case SESSION_EVENT_ON_REPEAT_MODE_CHANGED: {
  1032. final int repeatMode = extras.getInt(ARGUMENT_REPEAT_MODE);
  1033. synchronized (mLock) {
  1034. mRepeatMode = repeatMode;
  1035. }
  1036. mCallbackExecutor.execute(new Runnable() {
  1037. @Override
  1038. public void run() {
  1039. mCallback.onRepeatModeChanged(mInstance, repeatMode);
  1040. }
  1041. });
  1042. break;
  1043. }
  1044. case SESSION_EVENT_ON_SHUFFLE_MODE_CHANGED: {
  1045. final int shuffleMode = extras.getInt(ARGUMENT_SHUFFLE_MODE);
  1046. synchronized (mLock) {
  1047. mShuffleMode = shuffleMode;
  1048. }
  1049. mCallbackExecutor.execute(new Runnable() {
  1050. @Override
  1051. public void run() {
  1052. mCallback.onShuffleModeChanged(mInstance, shuffleMode);
  1053. }
  1054. });
  1055. break;
  1056. }
  1057. case SESSION_EVENT_SEND_CUSTOM_COMMAND: {
  1058. Bundle commandBundle = extras.getBundle(ARGUMENT_CUSTOM_COMMAND);
  1059. if (commandBundle == null) {
  1060. return;
  1061. }
  1062. final SessionCommand2 command = SessionCommand2.fromBundle(commandBundle);
  1063. final Bundle args = extras.getBundle(ARGUMENT_ARGUMENTS);
  1064. final ResultReceiver receiver = extras.getParcelable(ARGUMENT_RESULT_RECEIVER);
  1065. mCallbackExecutor.execute(new Runnable() {
  1066. @Override
  1067. public void run() {
  1068. mCallback.onCustomCommand(mInstance, command, args, receiver);
  1069. }
  1070. });
  1071. break;
  1072. }
  1073. case SESSION_EVENT_SET_CUSTOM_LAYOUT: {
  1074. final List<CommandButton> layout = MediaUtils2.fromCommandButtonParcelableArray(
  1075. extras.getParcelableArray(ARGUMENT_COMMAND_BUTTONS));
  1076. if (layout == null) {
  1077. return;
  1078. }
  1079. mCallbackExecutor.execute(new Runnable() {
  1080. @Override
  1081. public void run() {
  1082. mCallback.onCustomLayoutChanged(mInstance, layout);
  1083. }
  1084. });
  1085. break;
  1086. }
  1087. case SESSION_EVENT_ON_PLAYBACK_INFO_CHANGED: {
  1088. final PlaybackInfo info = PlaybackInfo.fromBundle(
  1089. extras.getBundle(ARGUMENT_PLAYBACK_INFO));
  1090. if (info == null) {
  1091. return;
  1092. }
  1093. synchronized (mLock) {
  1094. mPlaybackInfo = info;
  1095. }
  1096. mCallbackExecutor.execute(new Runnable() {
  1097. @Override
  1098. public void run() {
  1099. mCallback.onPlaybackInfoChanged(mInstance, info);
  1100. }
  1101. });
  1102. break;
  1103. }
  1104. case SESSION_EVENT_ON_PLAYBACK_SPEED_CHANGED: {
  1105. final PlaybackStateCompat state =
  1106. extras.getParcelable(ARGUMENT_PLAYBACK_STATE_COMPAT);
  1107. if (state == null) {
  1108. return;
  1109. }
  1110. synchronized (mLock) {
  1111. mPlaybackStateCompat = state;
  1112. }
  1113. mCallbackExecutor.execute(new Runnable() {
  1114. @Override
  1115. public void run() {
  1116. mCallback.onPlaybackSpeedChanged(mInstance, state.getPlaybackSpeed());
  1117. }
  1118. });
  1119. break;
  1120. }
  1121. case SESSION_EVENT_ON_BUFFERING_STATE_CHANGED: {
  1122. final MediaItem2 item = MediaItem2.fromBundle(
  1123. extras.getBundle(ARGUMENT_MEDIA_ITEM));
  1124. final int bufferingState = extras.getInt(ARGUMENT_BUFFERING_STATE);
  1125. PlaybackStateCompat state =
  1126. extras.getParcelable(ARGUMENT_PLAYBACK_STATE_COMPAT);
  1127. if (item == null || state == null) {
  1128. return;
  1129. }
  1130. synchronized (mLock) {
  1131. mBufferingState = bufferingState;
  1132. mPlaybackStateCompat = state;
  1133. }
  1134. mCallbackExecutor.execute(new Runnable() {
  1135. @Override
  1136. public void run() {
  1137. mCallback.onBufferingStateChanged(mInstance, item, bufferingState);
  1138. }
  1139. });
  1140. break;
  1141. }
  1142. case SESSION_EVENT_ON_SEEK_COMPLETED: {
  1143. final long position = extras.getLong(ARGUMENT_SEEK_POSITION);
  1144. PlaybackStateCompat state =
  1145. extras.getParcelable(ARGUMENT_PLAYBACK_STATE_COMPAT);
  1146. if (state == null) {
  1147. return;
  1148. }
  1149. synchronized (mLock) {
  1150. mPlaybackStateCompat = state;
  1151. }
  1152. mCallbackExecutor.execute(new Runnable() {
  1153. @Override
  1154. public void run() {
  1155. mCallback.onSeekCompleted(mInstance, position);
  1156. }
  1157. });
  1158. break;
  1159. }
  1160. case SESSION_EVENT_ON_CHILDREN_CHANGED: {
  1161. String parentId = extras.getString(ARGUMENT_MEDIA_ID);
  1162. if (parentId == null || !(mInstance instanceof MediaBrowser2)) {
  1163. return;
  1164. }
  1165. int itemCount = extras.getInt(ARGUMENT_ITEM_COUNT, -1);
  1166. Bundle childrenExtras = extras.getBundle(ARGUMENT_EXTRAS);
  1167. ((MediaBrowser2.BrowserCallback) mCallback).onChildrenChanged(
  1168. (MediaBrowser2) mInstance, parentId, itemCount, childrenExtras);
  1169. break;
  1170. }