MediaSessionCompat.java revision 65cde2c4554985493a2e7560b5e71c5f24969287
1/* 2 * Copyright (C) 2014 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 17package android.support.v4.media.session; 18 19import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21import android.app.Activity; 22import android.app.PendingIntent; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.graphics.Bitmap; 28import android.media.AudioManager; 29import android.media.MediaMetadataEditor; 30import android.media.MediaMetadataRetriever; 31import android.media.Rating; 32import android.media.RemoteControlClient; 33import android.net.Uri; 34import android.os.Build; 35import android.os.Bundle; 36import android.os.Handler; 37import android.os.IBinder; 38import android.os.Looper; 39import android.os.Message; 40import android.os.Parcel; 41import android.os.Parcelable; 42import android.os.RemoteCallbackList; 43import android.os.RemoteException; 44import android.os.ResultReceiver; 45import android.os.SystemClock; 46import android.support.annotation.IntDef; 47import android.support.annotation.RequiresApi; 48import android.support.annotation.RestrictTo; 49import android.support.v4.app.BundleCompat; 50import android.support.v4.media.MediaDescriptionCompat; 51import android.support.v4.media.MediaMetadataCompat; 52import android.support.v4.media.RatingCompat; 53import android.support.v4.media.VolumeProviderCompat; 54import android.text.TextUtils; 55import android.util.Log; 56import android.util.TypedValue; 57import android.view.KeyEvent; 58 59import java.lang.annotation.Retention; 60import java.lang.annotation.RetentionPolicy; 61import java.lang.ref.WeakReference; 62import java.util.ArrayList; 63import java.util.List; 64 65/** 66 * Allows interaction with media controllers, volume keys, media buttons, and 67 * transport controls. 68 * <p> 69 * A MediaSession should be created when an app wants to publish media playback 70 * information or handle media keys. In general an app only needs one session 71 * for all playback, though multiple sessions can be created to provide finer 72 * grain controls of media. 73 * <p> 74 * Once a session is created the owner of the session may pass its 75 * {@link #getSessionToken() session token} to other processes to allow them to 76 * create a {@link MediaControllerCompat} to interact with the session. 77 * <p> 78 * To receive commands, media keys, and other events a {@link Callback} must be 79 * set with {@link #setCallback(Callback)}. 80 * <p> 81 * When an app is finished performing playback it must call {@link #release()} 82 * to clean up the session and notify any controllers. 83 * <p> 84 * MediaSessionCompat objects are not thread safe and all calls should be made 85 * from the same thread. 86 * <p> 87 * This is a helper for accessing features in 88 * {@link android.media.session.MediaSession} introduced after API level 4 in a 89 * backwards compatible fashion. 90 */ 91public class MediaSessionCompat { 92 static final String TAG = "MediaSessionCompat"; 93 94 private final MediaSessionImpl mImpl; 95 private final MediaControllerCompat mController; 96 private final ArrayList<OnActiveChangeListener> mActiveListeners = new ArrayList<>(); 97 98 /** 99 * @hide 100 */ 101 @RestrictTo(LIBRARY_GROUP) 102 @IntDef(flag=true, value={ 103 FLAG_HANDLES_MEDIA_BUTTONS, 104 FLAG_HANDLES_TRANSPORT_CONTROLS, 105 FLAG_HANDLES_QUEUE_COMMANDS }) 106 @Retention(RetentionPolicy.SOURCE) 107 public @interface SessionFlags {} 108 109 /** 110 * Set this flag on the session to indicate that it can handle media button 111 * events. 112 */ 113 public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0; 114 115 /** 116 * Set this flag on the session to indicate that it handles transport 117 * control commands through its {@link Callback}. 118 */ 119 public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; 120 121 /** 122 * Set this flag on the session to indicate that it handles queue 123 * management commands through its {@link Callback}. 124 */ 125 public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2; 126 127 /** 128 * Predefined custom action to flag the media that is currently playing as inappropriate. 129 * 130 * @see Callback#onCustomAction 131 */ 132 public static final String ACTION_FLAG_AS_INAPPROPRIATE = 133 "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE"; 134 135 /** 136 * Predefined custom action to skip the advertisement that is currently playing. 137 * 138 * @see Callback#onCustomAction 139 */ 140 public static final String ACTION_SKIP_AD = 141 "android.support.v4.media.session.action.SKIP_AD"; 142 143 /** 144 * Custom action to invoke playFromUri() for the forward compatibility. 145 */ 146 static final String ACTION_PLAY_FROM_URI = 147 "android.support.v4.media.session.action.PLAY_FROM_URI"; 148 149 /** 150 * Custom action to invoke prepare() for the forward compatibility. 151 */ 152 static final String ACTION_PREPARE = "android.support.v4.media.session.action.PREPARE"; 153 154 /** 155 * Custom action to invoke prepareFromMediaId() for the forward compatibility. 156 */ 157 static final String ACTION_PREPARE_FROM_MEDIA_ID = 158 "android.support.v4.media.session.action.PREPARE_FROM_MEDIA_ID"; 159 160 /** 161 * Custom action to invoke prepareFromSearch() for the forward compatibility. 162 */ 163 static final String ACTION_PREPARE_FROM_SEARCH = 164 "android.support.v4.media.session.action.PREPARE_FROM_SEARCH"; 165 166 /** 167 * Custom action to invoke prepareFromUri() for the forward compatibility. 168 */ 169 static final String ACTION_PREPARE_FROM_URI = 170 "android.support.v4.media.session.action.PREPARE_FROM_URI"; 171 172 /** 173 * Custom action to invoke setCaptioningEnabled() for the forward compatibility. 174 */ 175 static final String ACTION_SET_CAPTIONING_ENABLED = 176 "android.support.v4.media.session.action.SET_CAPTIONING_ENABLED"; 177 178 /** 179 * Custom action to invoke setRepeatMode() for the forward compatibility. 180 */ 181 static final String ACTION_SET_REPEAT_MODE = 182 "android.support.v4.media.session.action.SET_REPEAT_MODE"; 183 184 /** 185 * Custom action to invoke setShuffleModeEnabled() for the forward compatibility. 186 */ 187 static final String ACTION_SET_SHUFFLE_MODE_ENABLED = 188 "android.support.v4.media.session.action.SET_SHUFFLE_MODE_ENABLED"; 189 190 /** 191 * Argument for use with {@link #ACTION_PREPARE_FROM_MEDIA_ID} indicating media id to play. 192 */ 193 static final String ACTION_ARGUMENT_MEDIA_ID = 194 "android.support.v4.media.session.action.ARGUMENT_MEDIA_ID"; 195 196 /** 197 * Argument for use with {@link #ACTION_PREPARE_FROM_SEARCH} indicating search query. 198 */ 199 static final String ACTION_ARGUMENT_QUERY = 200 "android.support.v4.media.session.action.ARGUMENT_QUERY"; 201 202 /** 203 * Argument for use with {@link #ACTION_PREPARE_FROM_URI} and {@link #ACTION_PLAY_FROM_URI} 204 * indicating URI to play. 205 */ 206 static final String ACTION_ARGUMENT_URI = 207 "android.support.v4.media.session.action.ARGUMENT_URI"; 208 209 /** 210 * Argument for use with various actions indicating extra bundle. 211 */ 212 static final String ACTION_ARGUMENT_EXTRAS = 213 "android.support.v4.media.session.action.ARGUMENT_EXTRAS"; 214 215 /** 216 * Argument for use with {@link #ACTION_SET_CAPTIONING_ENABLED} indicating whether captioning is 217 * enabled. 218 */ 219 static final String ACTION_ARGUMENT_CAPTIONING_ENABLED = 220 "android.support.v4.media.session.action.ARGUMENT_CAPTIONING_ENABLED"; 221 222 /** 223 * Argument for use with {@link #ACTION_SET_REPEAT_MODE} indicating repeat mode. 224 */ 225 static final String ACTION_ARGUMENT_REPEAT_MODE = 226 "android.support.v4.media.session.action.ARGUMENT_REPEAT_MODE"; 227 228 /** 229 * Argument for use with {@link #ACTION_SET_SHUFFLE_MODE_ENABLED} indicating that shuffle mode 230 * is enabled. 231 */ 232 static final String ACTION_ARGUMENT_SHUFFLE_MODE_ENABLED = 233 "android.support.v4.media.session.action.ARGUMENT_SHUFFLE_MODE_ENABLED"; 234 235 static final String EXTRA_BINDER = "android.support.v4.media.session.EXTRA_BINDER"; 236 237 // Maximum size of the bitmap in dp. 238 private static final int MAX_BITMAP_SIZE_IN_DP = 320; 239 240 // Maximum size of the bitmap in px. It shouldn't be changed. 241 static int sMaxBitmapSize; 242 243 /** 244 * Creates a new session. You must call {@link #release()} when finished with the session. 245 * <p> 246 * The session will automatically be registered with the system but will not be published 247 * until {@link #setActive(boolean) setActive(true)} is called. 248 * </p><p> 249 * For API 20 or earlier, note that a media button receiver is required for handling 250 * {@link Intent#ACTION_MEDIA_BUTTON}. This constructor will attempt to find an appropriate 251 * {@link BroadcastReceiver} from your manifest. See {@link MediaButtonReceiver} for more 252 * details. 253 * </p> 254 * @param context The context to use to create the session. 255 * @param tag A short name for debugging purposes. 256 */ 257 public MediaSessionCompat(Context context, String tag) { 258 this(context, tag, null, null); 259 } 260 261 /** 262 * Creates a new session with a specified media button receiver (a component name and/or 263 * a pending intent). You must call {@link #release()} when finished with the session. 264 * <p> 265 * The session will automatically be registered with the system but will not be published 266 * until {@link #setActive(boolean) setActive(true)} is called. 267 * </p><p> 268 * For API 20 or earlier, note that a media button receiver is required for handling 269 * {@link Intent#ACTION_MEDIA_BUTTON}. This constructor will attempt to find an appropriate 270 * {@link BroadcastReceiver} from your manifest if it's not specified. See 271 * {@link MediaButtonReceiver} for more details. 272 * </p> 273 * @param context The context to use to create the session. 274 * @param tag A short name for debugging purposes. 275 * @param mbrComponent The component name for your media button receiver. 276 * @param mbrIntent The PendingIntent for your receiver component that handles 277 * media button events. This is optional and will be used on between 278 * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} and 279 * {@link android.os.Build.VERSION_CODES#KITKAT_WATCH} instead of the 280 * component name. 281 */ 282 public MediaSessionCompat(Context context, String tag, ComponentName mbrComponent, 283 PendingIntent mbrIntent) { 284 if (context == null) { 285 throw new IllegalArgumentException("context must not be null"); 286 } 287 if (TextUtils.isEmpty(tag)) { 288 throw new IllegalArgumentException("tag must not be null or empty"); 289 } 290 291 if (mbrComponent == null) { 292 mbrComponent = MediaButtonReceiver.getMediaButtonReceiverComponent(context); 293 if (mbrComponent == null) { 294 Log.w(TAG, "Couldn't find a unique registered media button receiver in the " 295 + "given context."); 296 } 297 } 298 if (mbrComponent != null && mbrIntent == null) { 299 // construct a PendingIntent for the media button 300 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 301 // the associated intent will be handled by the component being registered 302 mediaButtonIntent.setComponent(mbrComponent); 303 mbrIntent = PendingIntent.getBroadcast(context, 304 0/* requestCode, ignored */, mediaButtonIntent, 0/* flags */); 305 } 306 if (android.os.Build.VERSION.SDK_INT >= 21) { 307 mImpl = new MediaSessionImplApi21(context, tag); 308 // Set default callback to respond to controllers' extra binder requests. 309 setCallback(new Callback() {}); 310 mImpl.setMediaButtonReceiver(mbrIntent); 311 } else if (android.os.Build.VERSION.SDK_INT >= 19) { 312 mImpl = new MediaSessionImplApi19(context, tag, mbrComponent, mbrIntent); 313 } else if (android.os.Build.VERSION.SDK_INT >= 18) { 314 mImpl = new MediaSessionImplApi18(context, tag, mbrComponent, mbrIntent); 315 } else { 316 mImpl = new MediaSessionImplBase(context, tag, mbrComponent, mbrIntent); 317 } 318 mController = new MediaControllerCompat(context, this); 319 320 if (sMaxBitmapSize == 0) { 321 sMaxBitmapSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 322 MAX_BITMAP_SIZE_IN_DP, context.getResources().getDisplayMetrics()); 323 } 324 } 325 326 private MediaSessionCompat(Context context, MediaSessionImpl impl) { 327 mImpl = impl; 328 if (android.os.Build.VERSION.SDK_INT >= 21) { 329 // Set default callback to respond to controllers' extra binder requests. 330 setCallback(new Callback() {}); 331 } 332 mController = new MediaControllerCompat(context, this); 333 } 334 335 /** 336 * Add a callback to receive updates on for the MediaSession. This includes 337 * media button and volume events. The caller's thread will be used to post 338 * events. 339 * 340 * @param callback The callback object 341 */ 342 public void setCallback(Callback callback) { 343 setCallback(callback, null); 344 } 345 346 /** 347 * Set the callback to receive updates for the MediaSession. This includes 348 * media button and volume events. Set the callback to null to stop 349 * receiving events. 350 * 351 * @param callback The callback to receive updates on. 352 * @param handler The handler that events should be posted on. 353 */ 354 public void setCallback(Callback callback, Handler handler) { 355 mImpl.setCallback(callback, handler != null ? handler : new Handler()); 356 } 357 358 /** 359 * Set an intent for launching UI for this Session. This can be used as a 360 * quick link to an ongoing media screen. The intent should be for an 361 * activity that may be started using 362 * {@link Activity#startActivity(Intent)}. 363 * 364 * @param pi The intent to launch to show UI for this Session. 365 */ 366 public void setSessionActivity(PendingIntent pi) { 367 mImpl.setSessionActivity(pi); 368 } 369 370 /** 371 * Set a pending intent for your media button receiver to allow restarting 372 * playback after the session has been stopped. If your app is started in 373 * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via 374 * the pending intent. 375 * <p> 376 * This method will only work on 377 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. Earlier 378 * platform versions must include the media button receiver in the 379 * constructor. 380 * 381 * @param mbr The {@link PendingIntent} to send the media button event to. 382 */ 383 public void setMediaButtonReceiver(PendingIntent mbr) { 384 mImpl.setMediaButtonReceiver(mbr); 385 } 386 387 /** 388 * Set any flags for the session. 389 * 390 * @param flags The flags to set for this session. 391 */ 392 public void setFlags(@SessionFlags int flags) { 393 mImpl.setFlags(flags); 394 } 395 396 /** 397 * Set the stream this session is playing on. This will affect the system's 398 * volume handling for this session. If {@link #setPlaybackToRemote} was 399 * previously called it will stop receiving volume commands and the system 400 * will begin sending volume changes to the appropriate stream. 401 * <p> 402 * By default sessions are on {@link AudioManager#STREAM_MUSIC}. 403 * 404 * @param stream The {@link AudioManager} stream this session is playing on. 405 */ 406 public void setPlaybackToLocal(int stream) { 407 mImpl.setPlaybackToLocal(stream); 408 } 409 410 /** 411 * Configure this session to use remote volume handling. This must be called 412 * to receive volume button events, otherwise the system will adjust the 413 * current stream volume for this session. If {@link #setPlaybackToLocal} 414 * was previously called that stream will stop receiving volume changes for 415 * this session. 416 * <p> 417 * On platforms earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP} 418 * this will only allow an app to handle volume commands sent directly to 419 * the session by a {@link MediaControllerCompat}. System routing of volume 420 * keys will not use the volume provider. 421 * 422 * @param volumeProvider The provider that will handle volume changes. May 423 * not be null. 424 */ 425 public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) { 426 if (volumeProvider == null) { 427 throw new IllegalArgumentException("volumeProvider may not be null!"); 428 } 429 mImpl.setPlaybackToRemote(volumeProvider); 430 } 431 432 /** 433 * Set if this session is currently active and ready to receive commands. If 434 * set to false your session's controller may not be discoverable. You must 435 * set the session to active before it can start receiving media button 436 * events or transport commands. 437 * <p> 438 * On platforms earlier than 439 * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, 440 * a media button event receiver should be set via the constructor to 441 * receive media button events. 442 * 443 * @param active Whether this session is active or not. 444 */ 445 public void setActive(boolean active) { 446 mImpl.setActive(active); 447 for (OnActiveChangeListener listener : mActiveListeners) { 448 listener.onActiveChanged(); 449 } 450 } 451 452 /** 453 * Get the current active state of this session. 454 * 455 * @return True if the session is active, false otherwise. 456 */ 457 public boolean isActive() { 458 return mImpl.isActive(); 459 } 460 461 /** 462 * Send a proprietary event to all MediaControllers listening to this 463 * Session. It's up to the Controller/Session owner to determine the meaning 464 * of any events. 465 * 466 * @param event The name of the event to send 467 * @param extras Any extras included with the event 468 */ 469 public void sendSessionEvent(String event, Bundle extras) { 470 if (TextUtils.isEmpty(event)) { 471 throw new IllegalArgumentException("event cannot be null or empty"); 472 } 473 mImpl.sendSessionEvent(event, extras); 474 } 475 476 /** 477 * This must be called when an app has finished performing playback. If 478 * playback is expected to start again shortly the session can be left open, 479 * but it must be released if your activity or service is being destroyed. 480 */ 481 public void release() { 482 mImpl.release(); 483 } 484 485 /** 486 * Retrieve a token object that can be used by apps to create a 487 * {@link MediaControllerCompat} for interacting with this session. The 488 * owner of the session is responsible for deciding how to distribute these 489 * tokens. 490 * <p> 491 * On platform versions before 492 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} this token may only be 493 * used within your app as there is no way to guarantee other apps are using 494 * the same version of the support library. 495 * 496 * @return A token that can be used to create a media controller for this 497 * session. 498 */ 499 public Token getSessionToken() { 500 return mImpl.getSessionToken(); 501 } 502 503 /** 504 * Get a controller for this session. This is a convenience method to avoid 505 * having to cache your own controller in process. 506 * 507 * @return A controller for this session. 508 */ 509 public MediaControllerCompat getController() { 510 return mController; 511 } 512 513 /** 514 * Update the current playback state. 515 * 516 * @param state The current state of playback 517 */ 518 public void setPlaybackState(PlaybackStateCompat state) { 519 mImpl.setPlaybackState(state); 520 } 521 522 /** 523 * Update the current metadata. New metadata can be created using 524 * {@link android.support.v4.media.MediaMetadataCompat.Builder}. This operation may take time 525 * proportional to the size of the bitmap to replace large bitmaps with a scaled down copy. 526 * 527 * @param metadata The new metadata 528 * @see android.support.v4.media.MediaMetadataCompat.Builder#putBitmap 529 */ 530 public void setMetadata(MediaMetadataCompat metadata) { 531 mImpl.setMetadata(metadata); 532 } 533 534 /** 535 * Update the list of items in the play queue. It is an ordered list and 536 * should contain the current item, and previous or upcoming items if they 537 * exist. Specify null if there is no current play queue. 538 * <p> 539 * The queue should be of reasonable size. If the play queue is unbounded 540 * within your app, it is better to send a reasonable amount in a sliding 541 * window instead. 542 * 543 * @param queue A list of items in the play queue. 544 */ 545 public void setQueue(List<QueueItem> queue) { 546 mImpl.setQueue(queue); 547 } 548 549 /** 550 * Set the title of the play queue. The UI should display this title along 551 * with the play queue itself. e.g. "Play Queue", "Now Playing", or an album 552 * name. 553 * 554 * @param title The title of the play queue. 555 */ 556 public void setQueueTitle(CharSequence title) { 557 mImpl.setQueueTitle(title); 558 } 559 560 /** 561 * Set the style of rating used by this session. Apps trying to set the 562 * rating should use this style. Must be one of the following: 563 * <ul> 564 * <li>{@link RatingCompat#RATING_NONE}</li> 565 * <li>{@link RatingCompat#RATING_3_STARS}</li> 566 * <li>{@link RatingCompat#RATING_4_STARS}</li> 567 * <li>{@link RatingCompat#RATING_5_STARS}</li> 568 * <li>{@link RatingCompat#RATING_HEART}</li> 569 * <li>{@link RatingCompat#RATING_PERCENTAGE}</li> 570 * <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li> 571 * </ul> 572 */ 573 public void setRatingType(@RatingCompat.Style int type) { 574 mImpl.setRatingType(type); 575 } 576 577 /** 578 * Enable/disable captioning for this session. 579 * 580 * @param enabled {@code true} to enable captioning, {@code false} to disable. 581 */ 582 public void setCaptioningEnabled(boolean enabled) { 583 mImpl.setCaptioningEnabled(enabled); 584 } 585 586 /** 587 * Set the repeat mode for this session. 588 * <p> 589 * Note that if this method is not called before, {@link MediaControllerCompat#getRepeatMode} 590 * will return {@link PlaybackStateCompat#REPEAT_MODE_NONE}. 591 * 592 * @param repeatMode The repeat mode. Must be one of the followings: 593 * {@link PlaybackStateCompat#REPEAT_MODE_NONE}, 594 * {@link PlaybackStateCompat#REPEAT_MODE_ONE}, 595 * {@link PlaybackStateCompat#REPEAT_MODE_ALL} 596 */ 597 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 598 mImpl.setRepeatMode(repeatMode); 599 } 600 601 /** 602 * Set the shuffle mode for this session. 603 * <p> 604 * Note that if this method is not called before, 605 * {@link MediaControllerCompat#isShuffleModeEnabled} will return {@code false}. 606 * 607 * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable. 608 */ 609 public void setShuffleModeEnabled(boolean enabled) { 610 mImpl.setShuffleModeEnabled(enabled); 611 } 612 613 /** 614 * Set some extras that can be associated with the 615 * {@link MediaSessionCompat}. No assumptions should be made as to how a 616 * {@link MediaControllerCompat} will handle these extras. Keys should be 617 * fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts. 618 * 619 * @param extras The extras associated with the session. 620 */ 621 public void setExtras(Bundle extras) { 622 mImpl.setExtras(extras); 623 } 624 625 /** 626 * Gets the underlying framework {@link android.media.session.MediaSession} 627 * object. 628 * <p> 629 * This method is only supported on API 21+. 630 * </p> 631 * 632 * @return The underlying {@link android.media.session.MediaSession} object, 633 * or null if none. 634 */ 635 public Object getMediaSession() { 636 return mImpl.getMediaSession(); 637 } 638 639 /** 640 * Gets the underlying framework {@link android.media.RemoteControlClient} 641 * object. 642 * <p> 643 * This method is only supported on APIs 14-20. On API 21+ 644 * {@link #getMediaSession()} should be used instead. 645 * 646 * @return The underlying {@link android.media.RemoteControlClient} object, 647 * or null if none. 648 */ 649 public Object getRemoteControlClient() { 650 return mImpl.getRemoteControlClient(); 651 } 652 653 /** 654 * Returns the name of the package that sent the last media button, transport control, or 655 * command from controllers and the system. This is only valid while in a request callback, such 656 * as {@link Callback#onPlay}. This method is not available and returns null on pre-N devices. 657 * 658 * @hide 659 */ 660 @RestrictTo(LIBRARY_GROUP) 661 public String getCallingPackage() { 662 return mImpl.getCallingPackage(); 663 } 664 665 /** 666 * Adds a listener to be notified when the active status of this session 667 * changes. This is primarily used by the support library and should not be 668 * needed by apps. 669 * 670 * @param listener The listener to add. 671 */ 672 public void addOnActiveChangeListener(OnActiveChangeListener listener) { 673 if (listener == null) { 674 throw new IllegalArgumentException("Listener may not be null"); 675 } 676 mActiveListeners.add(listener); 677 } 678 679 /** 680 * Stops the listener from being notified when the active status of this 681 * session changes. 682 * 683 * @param listener The listener to remove. 684 */ 685 public void removeOnActiveChangeListener(OnActiveChangeListener listener) { 686 if (listener == null) { 687 throw new IllegalArgumentException("Listener may not be null"); 688 } 689 mActiveListeners.remove(listener); 690 } 691 692 /** 693 * Creates an instance from a framework {@link android.media.session.MediaSession} object. 694 * <p> 695 * This method is only supported on API 21+. On API 20 and below, it returns null. 696 * </p> 697 * 698 * @param context The context to use to create the session. 699 * @param mediaSession A {@link android.media.session.MediaSession} object. 700 * @return An equivalent {@link MediaSessionCompat} object, or null if none. 701 */ 702 public static MediaSessionCompat fromMediaSession(Context context, Object mediaSession) { 703 if (context != null && mediaSession != null && Build.VERSION.SDK_INT >= 21) { 704 return new MediaSessionCompat(context, new MediaSessionImplApi21(mediaSession)); 705 } 706 return null; 707 } 708 709 /** 710 * Receives transport controls, media buttons, and commands from controllers 711 * and the system. The callback may be set using {@link #setCallback}. 712 */ 713 public abstract static class Callback { 714 final Object mCallbackObj; 715 WeakReference<MediaSessionImpl> mSessionImpl; 716 717 public Callback() { 718 if (android.os.Build.VERSION.SDK_INT >= 24) { 719 mCallbackObj = MediaSessionCompatApi24.createCallback(new StubApi24()); 720 } else if (android.os.Build.VERSION.SDK_INT >= 23) { 721 mCallbackObj = MediaSessionCompatApi23.createCallback(new StubApi23()); 722 } else if (android.os.Build.VERSION.SDK_INT >= 21) { 723 mCallbackObj = MediaSessionCompatApi21.createCallback(new StubApi21()); 724 } else { 725 mCallbackObj = null; 726 } 727 } 728 729 /** 730 * Called when a controller has sent a custom command to this session. 731 * The owner of the session may handle custom commands but is not 732 * required to. 733 * 734 * @param command The command name. 735 * @param extras Optional parameters for the command, may be null. 736 * @param cb A result receiver to which a result may be sent by the command, may be null. 737 */ 738 public void onCommand(String command, Bundle extras, ResultReceiver cb) { 739 } 740 741 /** 742 * Override to handle media button events. 743 * 744 * @param mediaButtonEvent The media button event intent. 745 * @return True if the event was handled, false otherwise. 746 */ 747 public boolean onMediaButtonEvent(Intent mediaButtonEvent) { 748 return false; 749 } 750 751 /** 752 * Override to handle requests to prepare playback. During the preparation, a session 753 * should not hold audio focus in order to allow other session play seamlessly. 754 * The state of playback should be updated to {@link PlaybackStateCompat#STATE_PAUSED} 755 * after the preparation is done. 756 */ 757 public void onPrepare() { 758 } 759 760 /** 761 * Override to handle requests to prepare for playing a specific mediaId that was provided 762 * by your app. During the preparation, a session should not hold audio focus in order to 763 * allow other session play seamlessly. The state of playback should be updated to 764 * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. The playback 765 * of the prepared content should start in the implementation of {@link #onPlay}. Override 766 * {@link #onPlayFromMediaId} to handle requests for starting playback without preparation. 767 */ 768 public void onPrepareFromMediaId(String mediaId, Bundle extras) { 769 } 770 771 /** 772 * Override to handle requests to prepare playback from a search query. An 773 * empty query indicates that the app may prepare any music. The 774 * implementation should attempt to make a smart choice about what to 775 * play. During the preparation, a session should not hold audio focus in order to allow 776 * other session play seamlessly. The state of playback should be updated to 777 * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. 778 * The playback of the prepared content should start in the implementation of 779 * {@link #onPlay}. Override {@link #onPlayFromSearch} to handle requests for 780 * starting playback without preparation. 781 */ 782 public void onPrepareFromSearch(String query, Bundle extras) { 783 } 784 785 /** 786 * Override to handle requests to prepare a specific media item represented by a URI. 787 * During the preparation, a session should not hold audio focus in order to allow other 788 * session play seamlessly. The state of playback should be updated to 789 * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. The playback of 790 * the prepared content should start in the implementation of {@link #onPlay}. Override 791 * {@link #onPlayFromUri} to handle requests for starting playback without preparation. 792 */ 793 public void onPrepareFromUri(Uri uri, Bundle extras) { 794 } 795 796 /** 797 * Override to handle requests to begin playback. 798 */ 799 public void onPlay() { 800 } 801 802 /** 803 * Override to handle requests to play a specific mediaId that was 804 * provided by your app. 805 */ 806 public void onPlayFromMediaId(String mediaId, Bundle extras) { 807 } 808 809 /** 810 * Override to handle requests to begin playback from a search query. An 811 * empty query indicates that the app may play any music. The 812 * implementation should attempt to make a smart choice about what to 813 * play. 814 */ 815 public void onPlayFromSearch(String query, Bundle extras) { 816 } 817 818 /** 819 * Override to handle requests to play a specific media item represented by a URI. 820 */ 821 public void onPlayFromUri(Uri uri, Bundle extras) { 822 } 823 824 /** 825 * Override to handle requests to play an item with a given id from the 826 * play queue. 827 */ 828 public void onSkipToQueueItem(long id) { 829 } 830 831 /** 832 * Override to handle requests to pause playback. 833 */ 834 public void onPause() { 835 } 836 837 /** 838 * Override to handle requests to skip to the next media item. 839 */ 840 public void onSkipToNext() { 841 } 842 843 /** 844 * Override to handle requests to skip to the previous media item. 845 */ 846 public void onSkipToPrevious() { 847 } 848 849 /** 850 * Override to handle requests to fast forward. 851 */ 852 public void onFastForward() { 853 } 854 855 /** 856 * Override to handle requests to rewind. 857 */ 858 public void onRewind() { 859 } 860 861 /** 862 * Override to handle requests to stop playback. 863 */ 864 public void onStop() { 865 } 866 867 /** 868 * Override to handle requests to seek to a specific position in ms. 869 * 870 * @param pos New position to move to, in milliseconds. 871 */ 872 public void onSeekTo(long pos) { 873 } 874 875 /** 876 * Override to handle the item being rated. 877 * 878 * @param rating 879 */ 880 public void onSetRating(RatingCompat rating) { 881 } 882 883 /** 884 * Override to handle requests to enable/disable captioning. 885 * 886 * @param enabled {@code true} to enable captioning, {@code false} to disable. 887 */ 888 public void onSetCaptioningEnabled(boolean enabled) { 889 } 890 891 /** 892 * Override to handle the setting of the repeat mode. 893 * <p> 894 * You should call {@link #setRepeatMode} before end of this method in order to notify 895 * the change to the {@link MediaControllerCompat}, or 896 * {@link MediaControllerCompat#getRepeatMode} could return an invalid value. 897 * 898 * @param repeatMode The repeat mode which is one of followings: 899 * {@link PlaybackStateCompat#REPEAT_MODE_NONE}, 900 * {@link PlaybackStateCompat#REPEAT_MODE_ONE}, 901 * {@link PlaybackStateCompat#REPEAT_MODE_ALL} 902 */ 903 public void onSetRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 904 } 905 906 /** 907 * Override to handle the setting of the shuffle mode. 908 * <p> 909 * You should call {@link #setShuffleModeEnabled} before the end of this method in order to 910 * notify the change to the {@link MediaControllerCompat}, or 911 * {@link MediaControllerCompat#isShuffleModeEnabled} could return an invalid value. 912 * 913 * @param enabled true when the shuffle mode is enabled, false otherwise. 914 */ 915 public void onSetShuffleModeEnabled(boolean enabled) { 916 } 917 918 /** 919 * Called when a {@link MediaControllerCompat} wants a 920 * {@link PlaybackStateCompat.CustomAction} to be performed. 921 * 922 * @param action The action that was originally sent in the 923 * {@link PlaybackStateCompat.CustomAction}. 924 * @param extras Optional extras specified by the 925 * {@link MediaControllerCompat}. 926 * @see #ACTION_FLAG_AS_INAPPROPRIATE 927 * @see #ACTION_SKIP_AD 928 */ 929 public void onCustomAction(String action, Bundle extras) { 930 } 931 932 /** 933 * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem} 934 * with the given {@link MediaDescriptionCompat description} at the end of the play queue. 935 * 936 * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem} 937 * to be inserted. 938 */ 939 public void onAddQueueItem(MediaDescriptionCompat description) { 940 } 941 942 /** 943 * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem} 944 * with the given {@link MediaDescriptionCompat description} at the specified position 945 * in the play queue. 946 * 947 * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem} 948 * to be inserted. 949 * @param index The index at which the created {@link QueueItem} is to be inserted. 950 */ 951 public void onAddQueueItem(MediaDescriptionCompat description, int index) { 952 } 953 954 /** 955 * Called when a {@link MediaControllerCompat} wants to remove the first occurrence of the 956 * specified {@link QueueItem} with the given {@link MediaDescriptionCompat description} 957 * in the play queue. 958 * 959 * @param description The {@link MediaDescriptionCompat} for denoting the {@link QueueItem} 960 * to be removed. 961 */ 962 public void onRemoveQueueItem(MediaDescriptionCompat description) { 963 } 964 965 /** 966 * Called when a {@link MediaControllerCompat} wants to remove a {@link QueueItem} at the 967 * specified position in the play queue. 968 * 969 * @param index The index of the element to be removed. 970 * @deprecated {@link #onRemoveQueueItem} will be called instead. 971 */ 972 @Deprecated 973 public void onRemoveQueueItemAt(int index) { 974 } 975 976 @RequiresApi(21) 977 private class StubApi21 implements MediaSessionCompatApi21.Callback { 978 979 StubApi21() { 980 } 981 982 @Override 983 public void onCommand(String command, Bundle extras, ResultReceiver cb) { 984 if (command.equals(MediaControllerCompat.COMMAND_GET_EXTRA_BINDER)) { 985 MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get(); 986 if (impl != null) { 987 Bundle result = new Bundle(); 988 IMediaSession extraBinder = impl.getSessionToken().getExtraBinder(); 989 BundleCompat.putBinder(result, EXTRA_BINDER, 990 extraBinder == null ? null : extraBinder.asBinder()); 991 cb.send(0, result); 992 } 993 } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM)) { 994 extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader()); 995 Callback.this.onAddQueueItem( 996 (MediaDescriptionCompat) extras.getParcelable( 997 MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION)); 998 } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM_AT)) { 999 extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader()); 1000 Callback.this.onAddQueueItem( 1001 (MediaDescriptionCompat) extras.getParcelable( 1002 MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION), 1003 extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX)); 1004 } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM)) { 1005 extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader()); 1006 Callback.this.onRemoveQueueItem( 1007 (MediaDescriptionCompat) extras.getParcelable( 1008 MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION)); 1009 } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM_AT)) { 1010 MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get(); 1011 if (impl != null && impl.mQueue != null) { 1012 int index = extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX, -1); 1013 QueueItem item = (index >= 0 && index < impl.mQueue.size()) 1014 ? impl.mQueue.get(index) : null; 1015 if (item != null) { 1016 Callback.this.onRemoveQueueItem(item.getDescription()); 1017 } 1018 } 1019 } else { 1020 Callback.this.onCommand(command, extras, cb); 1021 } 1022 } 1023 1024 @Override 1025 public boolean onMediaButtonEvent(Intent mediaButtonIntent) { 1026 return Callback.this.onMediaButtonEvent(mediaButtonIntent); 1027 } 1028 1029 @Override 1030 public void onPlay() { 1031 Callback.this.onPlay(); 1032 } 1033 1034 @Override 1035 public void onPlayFromMediaId(String mediaId, Bundle extras) { 1036 Callback.this.onPlayFromMediaId(mediaId, extras); 1037 } 1038 1039 @Override 1040 public void onPlayFromSearch(String search, Bundle extras) { 1041 Callback.this.onPlayFromSearch(search, extras); 1042 } 1043 1044 @Override 1045 public void onSkipToQueueItem(long id) { 1046 Callback.this.onSkipToQueueItem(id); 1047 } 1048 1049 @Override 1050 public void onPause() { 1051 Callback.this.onPause(); 1052 } 1053 1054 @Override 1055 public void onSkipToNext() { 1056 Callback.this.onSkipToNext(); 1057 } 1058 1059 @Override 1060 public void onSkipToPrevious() { 1061 Callback.this.onSkipToPrevious(); 1062 } 1063 1064 @Override 1065 public void onFastForward() { 1066 Callback.this.onFastForward(); 1067 } 1068 1069 @Override 1070 public void onRewind() { 1071 Callback.this.onRewind(); 1072 } 1073 1074 @Override 1075 public void onStop() { 1076 Callback.this.onStop(); 1077 } 1078 1079 @Override 1080 public void onSeekTo(long pos) { 1081 Callback.this.onSeekTo(pos); 1082 } 1083 1084 @Override 1085 public void onSetRating(Object ratingObj) { 1086 Callback.this.onSetRating(RatingCompat.fromRating(ratingObj)); 1087 } 1088 1089 @Override 1090 public void onCustomAction(String action, Bundle extras) { 1091 if (action.equals(ACTION_PLAY_FROM_URI)) { 1092 Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI); 1093 Bundle bundle = extras.getParcelable(ACTION_ARGUMENT_EXTRAS); 1094 Callback.this.onPlayFromUri(uri, bundle); 1095 } else if (action.equals(ACTION_PREPARE)) { 1096 Callback.this.onPrepare(); 1097 } else if (action.equals(ACTION_PREPARE_FROM_MEDIA_ID)) { 1098 String mediaId = extras.getString(ACTION_ARGUMENT_MEDIA_ID); 1099 Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS); 1100 Callback.this.onPrepareFromMediaId(mediaId, bundle); 1101 } else if (action.equals(ACTION_PREPARE_FROM_SEARCH)) { 1102 String query = extras.getString(ACTION_ARGUMENT_QUERY); 1103 Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS); 1104 Callback.this.onPrepareFromSearch(query, bundle); 1105 } else if (action.equals(ACTION_PREPARE_FROM_URI)) { 1106 Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI); 1107 Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS); 1108 Callback.this.onPrepareFromUri(uri, bundle); 1109 } else if (action.equals(ACTION_SET_CAPTIONING_ENABLED)) { 1110 boolean enabled = extras.getBoolean(ACTION_ARGUMENT_CAPTIONING_ENABLED); 1111 Callback.this.onSetCaptioningEnabled(enabled); 1112 } else if (action.equals(ACTION_SET_REPEAT_MODE)) { 1113 int repeatMode = extras.getInt(ACTION_ARGUMENT_REPEAT_MODE); 1114 Callback.this.onSetRepeatMode(repeatMode); 1115 } else if (action.equals(ACTION_SET_SHUFFLE_MODE_ENABLED)) { 1116 boolean enabled = extras.getBoolean(ACTION_ARGUMENT_SHUFFLE_MODE_ENABLED); 1117 Callback.this.onSetShuffleModeEnabled(enabled); 1118 } else { 1119 Callback.this.onCustomAction(action, extras); 1120 } 1121 } 1122 } 1123 1124 @RequiresApi(23) 1125 private class StubApi23 extends StubApi21 implements MediaSessionCompatApi23.Callback { 1126 1127 StubApi23() { 1128 } 1129 1130 @Override 1131 public void onPlayFromUri(Uri uri, Bundle extras) { 1132 Callback.this.onPlayFromUri(uri, extras); 1133 } 1134 } 1135 1136 @RequiresApi(24) 1137 private class StubApi24 extends StubApi23 implements MediaSessionCompatApi24.Callback { 1138 1139 StubApi24() { 1140 } 1141 1142 @Override 1143 public void onPrepare() { 1144 Callback.this.onPrepare(); 1145 } 1146 1147 @Override 1148 public void onPrepareFromMediaId(String mediaId, Bundle extras) { 1149 Callback.this.onPrepareFromMediaId(mediaId, extras); 1150 } 1151 1152 @Override 1153 public void onPrepareFromSearch(String query, Bundle extras) { 1154 Callback.this.onPrepareFromSearch(query, extras); 1155 } 1156 1157 @Override 1158 public void onPrepareFromUri(Uri uri, Bundle extras) { 1159 Callback.this.onPrepareFromUri(uri, extras); 1160 } 1161 } 1162 } 1163 1164 /** 1165 * Represents an ongoing session. This may be passed to apps by the session 1166 * owner to allow them to create a {@link MediaControllerCompat} to communicate with 1167 * the session. 1168 */ 1169 public static final class Token implements Parcelable { 1170 private final Object mInner; 1171 private final IMediaSession mExtraBinder; 1172 1173 Token(Object inner) { 1174 this(inner, null); 1175 } 1176 1177 Token(Object inner, IMediaSession extraBinder) { 1178 mInner = inner; 1179 mExtraBinder = extraBinder; 1180 } 1181 1182 /** 1183 * Creates a compat Token from a framework 1184 * {@link android.media.session.MediaSession.Token} object. 1185 * <p> 1186 * This method is only supported on 1187 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. 1188 * </p> 1189 * 1190 * @param token The framework token object. 1191 * @return A compat Token for use with {@link MediaControllerCompat}. 1192 */ 1193 public static Token fromToken(Object token) { 1194 return fromToken(token, null); 1195 } 1196 1197 /** 1198 * Creates a compat Token from a framework 1199 * {@link android.media.session.MediaSession.Token} object, and the extra binder. 1200 * <p> 1201 * This method is only supported on 1202 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. 1203 * </p> 1204 * 1205 * @param token The framework token object. 1206 * @param extraBinder The extra binder. 1207 * @return A compat Token for use with {@link MediaControllerCompat}. 1208 * @hide 1209 */ 1210 @RestrictTo(LIBRARY_GROUP) 1211 public static Token fromToken(Object token, IMediaSession extraBinder) { 1212 if (token != null && android.os.Build.VERSION.SDK_INT >= 21) { 1213 return new Token(MediaSessionCompatApi21.verifyToken(token), extraBinder); 1214 } 1215 return null; 1216 } 1217 1218 @Override 1219 public int describeContents() { 1220 return 0; 1221 } 1222 1223 @Override 1224 public void writeToParcel(Parcel dest, int flags) { 1225 if (android.os.Build.VERSION.SDK_INT >= 21) { 1226 dest.writeParcelable((Parcelable) mInner, flags); 1227 } else { 1228 dest.writeStrongBinder((IBinder) mInner); 1229 } 1230 } 1231 1232 @Override 1233 public int hashCode() { 1234 if (mInner == null) { 1235 return 0; 1236 } 1237 return mInner.hashCode(); 1238 } 1239 1240 @Override 1241 public boolean equals(Object obj) { 1242 if (this == obj) { 1243 return true; 1244 } 1245 if (!(obj instanceof Token)) { 1246 return false; 1247 } 1248 1249 Token other = (Token) obj; 1250 if (mInner == null) { 1251 return other.mInner == null; 1252 } 1253 if (other.mInner == null) { 1254 return false; 1255 } 1256 return mInner.equals(other.mInner); 1257 } 1258 1259 /** 1260 * Gets the underlying framework {@link android.media.session.MediaSession.Token} object. 1261 * <p> 1262 * This method is only supported on API 21+. 1263 * </p> 1264 * 1265 * @return The underlying {@link android.media.session.MediaSession.Token} object, 1266 * or null if none. 1267 */ 1268 public Object getToken() { 1269 return mInner; 1270 } 1271 1272 /** 1273 * @hide 1274 */ 1275 @RestrictTo(LIBRARY_GROUP) 1276 public IMediaSession getExtraBinder() { 1277 return mExtraBinder; 1278 } 1279 1280 public static final Parcelable.Creator<Token> CREATOR 1281 = new Parcelable.Creator<Token>() { 1282 @Override 1283 public Token createFromParcel(Parcel in) { 1284 Object inner; 1285 if (android.os.Build.VERSION.SDK_INT >= 21) { 1286 inner = in.readParcelable(null); 1287 } else { 1288 inner = in.readStrongBinder(); 1289 } 1290 return new Token(inner); 1291 } 1292 1293 @Override 1294 public Token[] newArray(int size) { 1295 return new Token[size]; 1296 } 1297 }; 1298 } 1299 1300 /** 1301 * A single item that is part of the play queue. It contains a description 1302 * of the item and its id in the queue. 1303 */ 1304 public static final class QueueItem implements Parcelable { 1305 /** 1306 * This id is reserved. No items can be explicitly assigned this id. 1307 */ 1308 public static final int UNKNOWN_ID = -1; 1309 1310 private final MediaDescriptionCompat mDescription; 1311 private final long mId; 1312 1313 private Object mItem; 1314 1315 /** 1316 * Create a new {@link MediaSessionCompat.QueueItem}. 1317 * 1318 * @param description The {@link MediaDescriptionCompat} for this item. 1319 * @param id An identifier for this item. It must be unique within the 1320 * play queue and cannot be {@link #UNKNOWN_ID}. 1321 */ 1322 public QueueItem(MediaDescriptionCompat description, long id) { 1323 this(null, description, id); 1324 } 1325 1326 private QueueItem(Object queueItem, MediaDescriptionCompat description, long id) { 1327 if (description == null) { 1328 throw new IllegalArgumentException("Description cannot be null."); 1329 } 1330 if (id == UNKNOWN_ID) { 1331 throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID"); 1332 } 1333 mDescription = description; 1334 mId = id; 1335 mItem = queueItem; 1336 } 1337 1338 QueueItem(Parcel in) { 1339 mDescription = MediaDescriptionCompat.CREATOR.createFromParcel(in); 1340 mId = in.readLong(); 1341 } 1342 1343 /** 1344 * Get the description for this item. 1345 */ 1346 public MediaDescriptionCompat getDescription() { 1347 return mDescription; 1348 } 1349 1350 /** 1351 * Get the queue id for this item. 1352 */ 1353 public long getQueueId() { 1354 return mId; 1355 } 1356 1357 @Override 1358 public void writeToParcel(Parcel dest, int flags) { 1359 mDescription.writeToParcel(dest, flags); 1360 dest.writeLong(mId); 1361 } 1362 1363 @Override 1364 public int describeContents() { 1365 return 0; 1366 } 1367 1368 /** 1369 * Get the underlying 1370 * {@link android.media.session.MediaSession.QueueItem}. 1371 * <p> 1372 * On builds before {@link android.os.Build.VERSION_CODES#LOLLIPOP} null 1373 * is returned. 1374 * 1375 * @return The underlying 1376 * {@link android.media.session.MediaSession.QueueItem} or null. 1377 */ 1378 public Object getQueueItem() { 1379 if (mItem != null || android.os.Build.VERSION.SDK_INT < 21) { 1380 return mItem; 1381 } 1382 mItem = MediaSessionCompatApi21.QueueItem.createItem(mDescription.getMediaDescription(), 1383 mId); 1384 return mItem; 1385 } 1386 1387 /** 1388 * Creates an instance from a framework {@link android.media.session.MediaSession.QueueItem} 1389 * object. 1390 * <p> 1391 * This method is only supported on API 21+. On API 20 and below, it returns null. 1392 * </p> 1393 * 1394 * @param queueItem A {@link android.media.session.MediaSession.QueueItem} object. 1395 * @return An equivalent {@link QueueItem} object, or null if none. 1396 */ 1397 public static QueueItem fromQueueItem(Object queueItem) { 1398 if (queueItem == null || Build.VERSION.SDK_INT < 21) { 1399 return null; 1400 } 1401 Object descriptionObj = MediaSessionCompatApi21.QueueItem.getDescription(queueItem); 1402 MediaDescriptionCompat description = MediaDescriptionCompat.fromMediaDescription( 1403 descriptionObj); 1404 long id = MediaSessionCompatApi21.QueueItem.getQueueId(queueItem); 1405 return new QueueItem(queueItem, description, id); 1406 } 1407 1408 /** 1409 * Creates a list of {@link QueueItem} objects from a framework 1410 * {@link android.media.session.MediaSession.QueueItem} object list. 1411 * <p> 1412 * This method is only supported on API 21+. On API 20 and below, it returns null. 1413 * </p> 1414 * 1415 * @param itemList A list of {@link android.media.session.MediaSession.QueueItem} objects. 1416 * @return An equivalent list of {@link QueueItem} objects, or null if none. 1417 */ 1418 public static List<QueueItem> fromQueueItemList(List<?> itemList) { 1419 if (itemList == null || Build.VERSION.SDK_INT < 21) { 1420 return null; 1421 } 1422 List<QueueItem> items = new ArrayList<>(); 1423 for (Object itemObj : itemList) { 1424 items.add(fromQueueItem(itemObj)); 1425 } 1426 return items; 1427 } 1428 1429 public static final Creator<MediaSessionCompat.QueueItem> CREATOR 1430 = new Creator<MediaSessionCompat.QueueItem>() { 1431 1432 @Override 1433 public MediaSessionCompat.QueueItem createFromParcel(Parcel p) { 1434 return new MediaSessionCompat.QueueItem(p); 1435 } 1436 1437 @Override 1438 public MediaSessionCompat.QueueItem[] newArray(int size) { 1439 return new MediaSessionCompat.QueueItem[size]; 1440 } 1441 }; 1442 1443 @Override 1444 public String toString() { 1445 return "MediaSession.QueueItem {" + 1446 "Description=" + mDescription + 1447 ", Id=" + mId + " }"; 1448 } 1449 } 1450 1451 /** 1452 * This is a wrapper for {@link ResultReceiver} for sending over aidl 1453 * interfaces. The framework version was not exposed to aidls until 1454 * {@link android.os.Build.VERSION_CODES#LOLLIPOP}. 1455 */ 1456 static final class ResultReceiverWrapper implements Parcelable { 1457 private ResultReceiver mResultReceiver; 1458 1459 public ResultReceiverWrapper(ResultReceiver resultReceiver) { 1460 mResultReceiver = resultReceiver; 1461 } 1462 1463 ResultReceiverWrapper(Parcel in) { 1464 mResultReceiver = ResultReceiver.CREATOR.createFromParcel(in); 1465 } 1466 1467 public static final Creator<ResultReceiverWrapper> 1468 CREATOR = new Creator<ResultReceiverWrapper>() { 1469 @Override 1470 public ResultReceiverWrapper createFromParcel(Parcel p) { 1471 return new ResultReceiverWrapper(p); 1472 } 1473 1474 @Override 1475 public ResultReceiverWrapper[] newArray(int size) { 1476 return new ResultReceiverWrapper[size]; 1477 } 1478 }; 1479 1480 @Override 1481 public int describeContents() { 1482 return 0; 1483 } 1484 1485 @Override 1486 public void writeToParcel(Parcel dest, int flags) { 1487 mResultReceiver.writeToParcel(dest, flags); 1488 } 1489 } 1490 1491 public interface OnActiveChangeListener { 1492 void onActiveChanged(); 1493 } 1494 1495 interface MediaSessionImpl { 1496 void setCallback(Callback callback, Handler handler); 1497 void setFlags(@SessionFlags int flags); 1498 void setPlaybackToLocal(int stream); 1499 void setPlaybackToRemote(VolumeProviderCompat volumeProvider); 1500 void setActive(boolean active); 1501 boolean isActive(); 1502 void sendSessionEvent(String event, Bundle extras); 1503 void release(); 1504 Token getSessionToken(); 1505 void setPlaybackState(PlaybackStateCompat state); 1506 void setMetadata(MediaMetadataCompat metadata); 1507 1508 void setSessionActivity(PendingIntent pi); 1509 1510 void setMediaButtonReceiver(PendingIntent mbr); 1511 void setQueue(List<QueueItem> queue); 1512 void setQueueTitle(CharSequence title); 1513 1514 void setRatingType(@RatingCompat.Style int type); 1515 void setCaptioningEnabled(boolean enabled); 1516 void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode); 1517 void setShuffleModeEnabled(boolean enabled); 1518 void setExtras(Bundle extras); 1519 1520 Object getMediaSession(); 1521 1522 Object getRemoteControlClient(); 1523 1524 String getCallingPackage(); 1525 } 1526 1527 static class MediaSessionImplBase implements MediaSessionImpl { 1528 /***** RemoteControlClient States, we only need none as the others were public *******/ 1529 static final int RCC_PLAYSTATE_NONE = 0; 1530 1531 private final Context mContext; 1532 private final ComponentName mMediaButtonReceiverComponentName; 1533 private final PendingIntent mMediaButtonReceiverIntent; 1534 private final MediaSessionStub mStub; 1535 private final Token mToken; 1536 final String mPackageName; 1537 final String mTag; 1538 final AudioManager mAudioManager; 1539 final RemoteControlClient mRcc; 1540 1541 final Object mLock = new Object(); 1542 final RemoteCallbackList<IMediaControllerCallback> mControllerCallbacks 1543 = new RemoteCallbackList<>(); 1544 1545 private MessageHandler mHandler; 1546 boolean mDestroyed = false; 1547 boolean mIsActive = false; 1548 private boolean mIsMbrRegistered = false; 1549 private boolean mIsRccRegistered = false; 1550 volatile Callback mCallback; 1551 1552 @SessionFlags int mFlags; 1553 1554 MediaMetadataCompat mMetadata; 1555 PlaybackStateCompat mState; 1556 PendingIntent mSessionActivity; 1557 List<QueueItem> mQueue; 1558 CharSequence mQueueTitle; 1559 @RatingCompat.Style int mRatingType; 1560 boolean mCaptioningEnabled; 1561 @PlaybackStateCompat.RepeatMode int mRepeatMode; 1562 boolean mShuffleModeEnabled; 1563 Bundle mExtras; 1564 1565 int mVolumeType; 1566 int mLocalStream; 1567 VolumeProviderCompat mVolumeProvider; 1568 1569 private VolumeProviderCompat.Callback mVolumeCallback 1570 = new VolumeProviderCompat.Callback() { 1571 @Override 1572 public void onVolumeChanged(VolumeProviderCompat volumeProvider) { 1573 if (mVolumeProvider != volumeProvider) { 1574 return; 1575 } 1576 ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream, 1577 volumeProvider.getVolumeControl(), volumeProvider.getMaxVolume(), 1578 volumeProvider.getCurrentVolume()); 1579 sendVolumeInfoChanged(info); 1580 } 1581 }; 1582 1583 public MediaSessionImplBase(Context context, String tag, ComponentName mbrComponent, 1584 PendingIntent mbrIntent) { 1585 if (mbrComponent == null) { 1586 throw new IllegalArgumentException( 1587 "MediaButtonReceiver component may not be null."); 1588 } 1589 mContext = context; 1590 mPackageName = context.getPackageName(); 1591 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 1592 mTag = tag; 1593 mMediaButtonReceiverComponentName = mbrComponent; 1594 mMediaButtonReceiverIntent = mbrIntent; 1595 mStub = new MediaSessionStub(); 1596 mToken = new Token(mStub); 1597 1598 mRatingType = RatingCompat.RATING_NONE; 1599 mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL; 1600 mLocalStream = AudioManager.STREAM_MUSIC; 1601 mRcc = new RemoteControlClient(mbrIntent); 1602 } 1603 1604 @Override 1605 public void setCallback(Callback callback, Handler handler) { 1606 mCallback = callback; 1607 if (callback != null) { 1608 if (handler == null) { 1609 handler = new Handler(); 1610 } 1611 synchronized (mLock) { 1612 mHandler = new MessageHandler(handler.getLooper()); 1613 } 1614 } 1615 } 1616 1617 void postToHandler(int what) { 1618 postToHandler(what, null); 1619 } 1620 1621 void postToHandler(int what, int arg1) { 1622 postToHandler(what, null, arg1); 1623 } 1624 1625 void postToHandler(int what, Object obj) { 1626 postToHandler(what, obj, null); 1627 } 1628 1629 void postToHandler(int what, Object obj, int arg1) { 1630 synchronized (mLock) { 1631 if (mHandler != null) { 1632 mHandler.post(what, obj, arg1); 1633 } 1634 } 1635 } 1636 1637 void postToHandler(int what, Object obj, Bundle extras) { 1638 synchronized (mLock) { 1639 if (mHandler != null) { 1640 mHandler.post(what, obj, extras); 1641 } 1642 } 1643 } 1644 1645 @Override 1646 public void setFlags(@SessionFlags int flags) { 1647 synchronized (mLock) { 1648 mFlags = flags; 1649 } 1650 update(); 1651 } 1652 1653 @Override 1654 public void setPlaybackToLocal(int stream) { 1655 if (mVolumeProvider != null) { 1656 mVolumeProvider.setCallback(null); 1657 } 1658 mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL; 1659 ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream, 1660 VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, 1661 mAudioManager.getStreamMaxVolume(mLocalStream), 1662 mAudioManager.getStreamVolume(mLocalStream)); 1663 sendVolumeInfoChanged(info); 1664 } 1665 1666 @Override 1667 public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) { 1668 if (volumeProvider == null) { 1669 throw new IllegalArgumentException("volumeProvider may not be null"); 1670 } 1671 if (mVolumeProvider != null) { 1672 mVolumeProvider.setCallback(null); 1673 } 1674 mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE; 1675 mVolumeProvider = volumeProvider; 1676 ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream, 1677 mVolumeProvider.getVolumeControl(), mVolumeProvider.getMaxVolume(), 1678 mVolumeProvider.getCurrentVolume()); 1679 sendVolumeInfoChanged(info); 1680 1681 volumeProvider.setCallback(mVolumeCallback); 1682 } 1683 1684 @Override 1685 public void setActive(boolean active) { 1686 if (active == mIsActive) { 1687 return; 1688 } 1689 mIsActive = active; 1690 if (update()) { 1691 setMetadata(mMetadata); 1692 setPlaybackState(mState); 1693 } 1694 } 1695 1696 @Override 1697 public boolean isActive() { 1698 return mIsActive; 1699 } 1700 1701 @Override 1702 public void sendSessionEvent(String event, Bundle extras) { 1703 sendEvent(event, extras); 1704 } 1705 1706 @Override 1707 public void release() { 1708 mIsActive = false; 1709 mDestroyed = true; 1710 update(); 1711 sendSessionDestroyed(); 1712 } 1713 1714 @Override 1715 public Token getSessionToken() { 1716 return mToken; 1717 } 1718 1719 @Override 1720 public void setPlaybackState(PlaybackStateCompat state) { 1721 synchronized (mLock) { 1722 mState = state; 1723 } 1724 sendState(state); 1725 if (!mIsActive) { 1726 // Don't set the state until after the RCC is registered 1727 return; 1728 } 1729 if (state == null) { 1730 mRcc.setPlaybackState(0); 1731 mRcc.setTransportControlFlags(0); 1732 } else { 1733 // Set state 1734 setRccState(state); 1735 1736 // Set transport control flags 1737 mRcc.setTransportControlFlags( 1738 getRccTransportControlFlagsFromActions(state.getActions())); 1739 } 1740 } 1741 1742 void setRccState(PlaybackStateCompat state) { 1743 mRcc.setPlaybackState(getRccStateFromState(state.getState())); 1744 } 1745 1746 int getRccStateFromState(int state) { 1747 switch (state) { 1748 case PlaybackStateCompat.STATE_CONNECTING: 1749 case PlaybackStateCompat.STATE_BUFFERING: 1750 return RemoteControlClient.PLAYSTATE_BUFFERING; 1751 case PlaybackStateCompat.STATE_ERROR: 1752 return RemoteControlClient.PLAYSTATE_ERROR; 1753 case PlaybackStateCompat.STATE_FAST_FORWARDING: 1754 return RemoteControlClient.PLAYSTATE_FAST_FORWARDING; 1755 case PlaybackStateCompat.STATE_NONE: 1756 return RCC_PLAYSTATE_NONE; 1757 case PlaybackStateCompat.STATE_PAUSED: 1758 return RemoteControlClient.PLAYSTATE_PAUSED; 1759 case PlaybackStateCompat.STATE_PLAYING: 1760 return RemoteControlClient.PLAYSTATE_PLAYING; 1761 case PlaybackStateCompat.STATE_REWINDING: 1762 return RemoteControlClient.PLAYSTATE_REWINDING; 1763 case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: 1764 return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS; 1765 case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: 1766 case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: 1767 return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS; 1768 case PlaybackStateCompat.STATE_STOPPED: 1769 return RemoteControlClient.PLAYSTATE_STOPPED; 1770 default: 1771 return -1; 1772 } 1773 } 1774 1775 int getRccTransportControlFlagsFromActions(long actions) { 1776 int transportControlFlags = 0; 1777 if ((actions & PlaybackStateCompat.ACTION_STOP) != 0) { 1778 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_STOP; 1779 } 1780 if ((actions & PlaybackStateCompat.ACTION_PAUSE) != 0) { 1781 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PAUSE; 1782 } 1783 if ((actions & PlaybackStateCompat.ACTION_PLAY) != 0) { 1784 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY; 1785 } 1786 if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) { 1787 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_REWIND; 1788 } 1789 if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) { 1790 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS; 1791 } 1792 if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) { 1793 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_NEXT; 1794 } 1795 if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) { 1796 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD; 1797 } 1798 if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) { 1799 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE; 1800 } 1801 return transportControlFlags; 1802 } 1803 1804 @Override 1805 public void setMetadata(MediaMetadataCompat metadata) { 1806 if (metadata != null) { 1807 // Clones the given {@link MediaMetadataCompat}, deep-copying bitmaps in the 1808 // metadata if necessary. Bitmaps can be scaled down if they are large. 1809 metadata = new MediaMetadataCompat.Builder(metadata, sMaxBitmapSize).build(); 1810 } 1811 1812 synchronized (mLock) { 1813 mMetadata = metadata; 1814 } 1815 sendMetadata(metadata); 1816 if (!mIsActive) { 1817 // Don't set metadata until after the rcc has been registered 1818 return; 1819 } 1820 RemoteControlClient.MetadataEditor editor = buildRccMetadata( 1821 metadata == null ? null : metadata.getBundle()); 1822 editor.apply(); 1823 } 1824 1825 RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) { 1826 RemoteControlClient.MetadataEditor editor = mRcc.editMetadata(true); 1827 if (metadata == null) { 1828 return editor; 1829 } 1830 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)) { 1831 Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ART); 1832 editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art); 1833 } else if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) { 1834 // Fall back to album art if the track art wasn't available 1835 Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ALBUM_ART); 1836 editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art); 1837 } 1838 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM)) { 1839 editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, 1840 metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)); 1841 } 1842 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) { 1843 editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, 1844 metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)); 1845 } 1846 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ARTIST)) { 1847 editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, 1848 metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)); 1849 } 1850 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_AUTHOR)) { 1851 editor.putString(MediaMetadataRetriever.METADATA_KEY_AUTHOR, 1852 metadata.getString(MediaMetadataCompat.METADATA_KEY_AUTHOR)); 1853 } 1854 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPILATION)) { 1855 editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPILATION, 1856 metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPILATION)); 1857 } 1858 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPOSER)) { 1859 editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPOSER, 1860 metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPOSER)); 1861 } 1862 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DATE)) { 1863 editor.putString(MediaMetadataRetriever.METADATA_KEY_DATE, 1864 metadata.getString(MediaMetadataCompat.METADATA_KEY_DATE)); 1865 } 1866 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)) { 1867 editor.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, 1868 metadata.getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)); 1869 } 1870 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) { 1871 editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, 1872 metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)); 1873 } 1874 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_GENRE)) { 1875 editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE, 1876 metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)); 1877 } 1878 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) { 1879 editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, 1880 metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)); 1881 } 1882 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)) { 1883 editor.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, 1884 metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)); 1885 } 1886 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_WRITER)) { 1887 editor.putString(MediaMetadataRetriever.METADATA_KEY_WRITER, 1888 metadata.getString(MediaMetadataCompat.METADATA_KEY_WRITER)); 1889 } 1890 return editor; 1891 } 1892 1893 @Override 1894 public void setSessionActivity(PendingIntent pi) { 1895 synchronized (mLock) { 1896 mSessionActivity = pi; 1897 } 1898 } 1899 1900 @Override 1901 public void setMediaButtonReceiver(PendingIntent mbr) { 1902 // Do nothing, changing this is not supported before API 21. 1903 } 1904 1905 @Override 1906 public void setQueue(List<QueueItem> queue) { 1907 mQueue = queue; 1908 sendQueue(queue); 1909 } 1910 1911 @Override 1912 public void setQueueTitle(CharSequence title) { 1913 mQueueTitle = title; 1914 sendQueueTitle(title); 1915 } 1916 1917 @Override 1918 public Object getMediaSession() { 1919 return null; 1920 } 1921 1922 @Override 1923 public Object getRemoteControlClient() { 1924 return null; 1925 } 1926 1927 @Override 1928 public String getCallingPackage() { 1929 return null; 1930 } 1931 1932 @Override 1933 public void setRatingType(@RatingCompat.Style int type) { 1934 mRatingType = type; 1935 } 1936 1937 @Override 1938 public void setCaptioningEnabled(boolean enabled) { 1939 if (mCaptioningEnabled != enabled) { 1940 mCaptioningEnabled = enabled; 1941 sendCaptioningEnabled(enabled); 1942 } 1943 } 1944 1945 @Override 1946 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 1947 if (mRepeatMode != repeatMode) { 1948 mRepeatMode = repeatMode; 1949 sendRepeatMode(repeatMode); 1950 } 1951 } 1952 1953 @Override 1954 public void setShuffleModeEnabled(boolean enabled) { 1955 if (mShuffleModeEnabled != enabled) { 1956 mShuffleModeEnabled = enabled; 1957 sendShuffleModeEnabled(enabled); 1958 } 1959 } 1960 1961 @Override 1962 public void setExtras(Bundle extras) { 1963 mExtras = extras; 1964 sendExtras(extras); 1965 } 1966 1967 // Registers/unregisters components as needed. 1968 boolean update() { 1969 boolean registeredRcc = false; 1970 if (mIsActive) { 1971 // Register a MBR if it's supported, unregister it if support was removed. 1972 if (!mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) != 0) { 1973 registerMediaButtonEventReceiver(mMediaButtonReceiverIntent, 1974 mMediaButtonReceiverComponentName); 1975 mIsMbrRegistered = true; 1976 } else if (mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) == 0) { 1977 unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent, 1978 mMediaButtonReceiverComponentName); 1979 mIsMbrRegistered = false; 1980 } 1981 // Register a RCC if it's supported, unregister it if support was removed. 1982 if (!mIsRccRegistered && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) { 1983 mAudioManager.registerRemoteControlClient(mRcc); 1984 mIsRccRegistered = true; 1985 registeredRcc = true; 1986 } else if (mIsRccRegistered 1987 && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) { 1988 // RCC keeps the state while the system resets its state internally when 1989 // we register RCC. Reset the state so that the states in RCC and the system 1990 // are in sync when we re-register the RCC. 1991 mRcc.setPlaybackState(0); 1992 mAudioManager.unregisterRemoteControlClient(mRcc); 1993 mIsRccRegistered = false; 1994 } 1995 } else { 1996 // When inactive remove any registered components. 1997 if (mIsMbrRegistered) { 1998 unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent, 1999 mMediaButtonReceiverComponentName); 2000 mIsMbrRegistered = false; 2001 } 2002 if (mIsRccRegistered) { 2003 // RCC keeps the state while the system resets its state internally when 2004 // we register RCC. Reset the state so that the states in RCC and the system 2005 // are in sync when we re-register the RCC. 2006 mRcc.setPlaybackState(0); 2007 mAudioManager.unregisterRemoteControlClient(mRcc); 2008 mIsRccRegistered = false; 2009 } 2010 } 2011 return registeredRcc; 2012 } 2013 2014 void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) { 2015 mAudioManager.registerMediaButtonEventReceiver(mbrComponent); 2016 } 2017 2018 void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent, 2019 ComponentName mbrComponent) { 2020 mAudioManager.unregisterMediaButtonEventReceiver(mbrComponent); 2021 } 2022 2023 void adjustVolume(int direction, int flags) { 2024 if (mVolumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 2025 if (mVolumeProvider != null) { 2026 mVolumeProvider.onAdjustVolume(direction); 2027 } 2028 } else { 2029 mAudioManager.adjustStreamVolume(mLocalStream, direction, flags); 2030 } 2031 } 2032 2033 void setVolumeTo(int value, int flags) { 2034 if (mVolumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 2035 if (mVolumeProvider != null) { 2036 mVolumeProvider.onSetVolumeTo(value); 2037 } 2038 } else { 2039 mAudioManager.setStreamVolume(mLocalStream, value, flags); 2040 } 2041 } 2042 2043 PlaybackStateCompat getStateWithUpdatedPosition() { 2044 PlaybackStateCompat state; 2045 long duration = -1; 2046 synchronized (mLock) { 2047 state = mState; 2048 if (mMetadata != null 2049 && mMetadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) { 2050 duration = mMetadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION); 2051 } 2052 } 2053 2054 PlaybackStateCompat result = null; 2055 if (state != null) { 2056 if (state.getState() == PlaybackStateCompat.STATE_PLAYING 2057 || state.getState() == PlaybackStateCompat.STATE_FAST_FORWARDING 2058 || state.getState() == PlaybackStateCompat.STATE_REWINDING) { 2059 long updateTime = state.getLastPositionUpdateTime(); 2060 long currentTime = SystemClock.elapsedRealtime(); 2061 if (updateTime > 0) { 2062 long position = (long) (state.getPlaybackSpeed() 2063 * (currentTime - updateTime)) + state.getPosition(); 2064 if (duration >= 0 && position > duration) { 2065 position = duration; 2066 } else if (position < 0) { 2067 position = 0; 2068 } 2069 PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder( 2070 state); 2071 builder.setState(state.getState(), position, state.getPlaybackSpeed(), 2072 currentTime); 2073 result = builder.build(); 2074 } 2075 } 2076 } 2077 return result == null ? state : result; 2078 } 2079 2080 void sendVolumeInfoChanged(ParcelableVolumeInfo info) { 2081 int size = mControllerCallbacks.beginBroadcast(); 2082 for (int i = size - 1; i >= 0; i--) { 2083 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2084 try { 2085 cb.onVolumeInfoChanged(info); 2086 } catch (RemoteException e) { 2087 } 2088 } 2089 mControllerCallbacks.finishBroadcast(); 2090 } 2091 2092 private void sendSessionDestroyed() { 2093 int size = mControllerCallbacks.beginBroadcast(); 2094 for (int i = size - 1; i >= 0; i--) { 2095 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2096 try { 2097 cb.onSessionDestroyed(); 2098 } catch (RemoteException e) { 2099 } 2100 } 2101 mControllerCallbacks.finishBroadcast(); 2102 mControllerCallbacks.kill(); 2103 } 2104 2105 private void sendEvent(String event, Bundle extras) { 2106 int size = mControllerCallbacks.beginBroadcast(); 2107 for (int i = size - 1; i >= 0; i--) { 2108 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2109 try { 2110 cb.onEvent(event, extras); 2111 } catch (RemoteException e) { 2112 } 2113 } 2114 mControllerCallbacks.finishBroadcast(); 2115 } 2116 2117 private void sendState(PlaybackStateCompat state) { 2118 int size = mControllerCallbacks.beginBroadcast(); 2119 for (int i = size - 1; i >= 0; i--) { 2120 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2121 try { 2122 cb.onPlaybackStateChanged(state); 2123 } catch (RemoteException e) { 2124 } 2125 } 2126 mControllerCallbacks.finishBroadcast(); 2127 } 2128 2129 private void sendMetadata(MediaMetadataCompat metadata) { 2130 int size = mControllerCallbacks.beginBroadcast(); 2131 for (int i = size - 1; i >= 0; i--) { 2132 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2133 try { 2134 cb.onMetadataChanged(metadata); 2135 } catch (RemoteException e) { 2136 } 2137 } 2138 mControllerCallbacks.finishBroadcast(); 2139 } 2140 2141 private void sendQueue(List<QueueItem> queue) { 2142 int size = mControllerCallbacks.beginBroadcast(); 2143 for (int i = size - 1; i >= 0; i--) { 2144 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2145 try { 2146 cb.onQueueChanged(queue); 2147 } catch (RemoteException e) { 2148 } 2149 } 2150 mControllerCallbacks.finishBroadcast(); 2151 } 2152 2153 private void sendQueueTitle(CharSequence queueTitle) { 2154 int size = mControllerCallbacks.beginBroadcast(); 2155 for (int i = size - 1; i >= 0; i--) { 2156 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2157 try { 2158 cb.onQueueTitleChanged(queueTitle); 2159 } catch (RemoteException e) { 2160 } 2161 } 2162 mControllerCallbacks.finishBroadcast(); 2163 } 2164 2165 private void sendCaptioningEnabled(boolean enabled) { 2166 int size = mControllerCallbacks.beginBroadcast(); 2167 for (int i = size - 1; i >= 0; i--) { 2168 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2169 try { 2170 cb.onCaptioningEnabledChanged(enabled); 2171 } catch (RemoteException e) { 2172 } 2173 } 2174 mControllerCallbacks.finishBroadcast(); 2175 } 2176 2177 private void sendRepeatMode(int repeatMode) { 2178 int size = mControllerCallbacks.beginBroadcast(); 2179 for (int i = size - 1; i >= 0; i--) { 2180 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2181 try { 2182 cb.onRepeatModeChanged(repeatMode); 2183 } catch (RemoteException e) { 2184 } 2185 } 2186 mControllerCallbacks.finishBroadcast(); 2187 } 2188 2189 private void sendShuffleModeEnabled(boolean enabled) { 2190 int size = mControllerCallbacks.beginBroadcast(); 2191 for (int i = size - 1; i >= 0; i--) { 2192 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2193 try { 2194 cb.onShuffleModeChanged(enabled); 2195 } catch (RemoteException e) { 2196 } 2197 } 2198 mControllerCallbacks.finishBroadcast(); 2199 } 2200 2201 private void sendExtras(Bundle extras) { 2202 int size = mControllerCallbacks.beginBroadcast(); 2203 for (int i = size - 1; i >= 0; i--) { 2204 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2205 try { 2206 cb.onExtrasChanged(extras); 2207 } catch (RemoteException e) { 2208 } 2209 } 2210 mControllerCallbacks.finishBroadcast(); 2211 } 2212 2213 class MediaSessionStub extends IMediaSession.Stub { 2214 @Override 2215 public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) { 2216 postToHandler(MessageHandler.MSG_COMMAND, 2217 new Command(command, args, cb.mResultReceiver)); 2218 } 2219 2220 @Override 2221 public boolean sendMediaButton(KeyEvent mediaButton) { 2222 boolean handlesMediaButtons = 2223 (mFlags & MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS) != 0; 2224 if (handlesMediaButtons) { 2225 postToHandler(MessageHandler.MSG_MEDIA_BUTTON, mediaButton); 2226 } 2227 return handlesMediaButtons; 2228 } 2229 2230 @Override 2231 public void registerCallbackListener(IMediaControllerCallback cb) { 2232 // If this session is already destroyed tell the caller and 2233 // don't add them. 2234 if (mDestroyed) { 2235 try { 2236 cb.onSessionDestroyed(); 2237 } catch (Exception e) { 2238 // ignored 2239 } 2240 return; 2241 } 2242 mControllerCallbacks.register(cb); 2243 } 2244 2245 @Override 2246 public void unregisterCallbackListener(IMediaControllerCallback cb) { 2247 mControllerCallbacks.unregister(cb); 2248 } 2249 2250 @Override 2251 public String getPackageName() { 2252 // mPackageName is final so doesn't need synchronize block 2253 return mPackageName; 2254 } 2255 2256 @Override 2257 public String getTag() { 2258 // mTag is final so doesn't need synchronize block 2259 return mTag; 2260 } 2261 2262 @Override 2263 public PendingIntent getLaunchPendingIntent() { 2264 synchronized (mLock) { 2265 return mSessionActivity; 2266 } 2267 } 2268 2269 @Override 2270 @SessionFlags 2271 public long getFlags() { 2272 synchronized (mLock) { 2273 return mFlags; 2274 } 2275 } 2276 2277 @Override 2278 public ParcelableVolumeInfo getVolumeAttributes() { 2279 int controlType; 2280 int max; 2281 int current; 2282 int stream; 2283 int volumeType; 2284 synchronized (mLock) { 2285 volumeType = mVolumeType; 2286 stream = mLocalStream; 2287 VolumeProviderCompat vp = mVolumeProvider; 2288 if (volumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 2289 controlType = vp.getVolumeControl(); 2290 max = vp.getMaxVolume(); 2291 current = vp.getCurrentVolume(); 2292 } else { 2293 controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; 2294 max = mAudioManager.getStreamMaxVolume(stream); 2295 current = mAudioManager.getStreamVolume(stream); 2296 } 2297 } 2298 return new ParcelableVolumeInfo(volumeType, stream, controlType, max, current); 2299 } 2300 2301 @Override 2302 public void adjustVolume(int direction, int flags, String packageName) { 2303 MediaSessionImplBase.this.adjustVolume(direction, flags); 2304 } 2305 2306 @Override 2307 public void setVolumeTo(int value, int flags, String packageName) { 2308 MediaSessionImplBase.this.setVolumeTo(value, flags); 2309 } 2310 2311 @Override 2312 public void prepare() throws RemoteException { 2313 postToHandler(MessageHandler.MSG_PREPARE); 2314 } 2315 2316 @Override 2317 public void prepareFromMediaId(String mediaId, Bundle extras) throws RemoteException { 2318 postToHandler(MessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras); 2319 } 2320 2321 @Override 2322 public void prepareFromSearch(String query, Bundle extras) throws RemoteException { 2323 postToHandler(MessageHandler.MSG_PREPARE_SEARCH, query, extras); 2324 } 2325 2326 @Override 2327 public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException { 2328 postToHandler(MessageHandler.MSG_PREPARE_URI, uri, extras); 2329 } 2330 2331 @Override 2332 public void play() throws RemoteException { 2333 postToHandler(MessageHandler.MSG_PLAY); 2334 } 2335 2336 @Override 2337 public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException { 2338 postToHandler(MessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras); 2339 } 2340 2341 @Override 2342 public void playFromSearch(String query, Bundle extras) throws RemoteException { 2343 postToHandler(MessageHandler.MSG_PLAY_SEARCH, query, extras); 2344 } 2345 2346 @Override 2347 public void playFromUri(Uri uri, Bundle extras) throws RemoteException { 2348 postToHandler(MessageHandler.MSG_PLAY_URI, uri, extras); 2349 } 2350 2351 @Override 2352 public void skipToQueueItem(long id) { 2353 postToHandler(MessageHandler.MSG_SKIP_TO_ITEM, id); 2354 } 2355 2356 @Override 2357 public void pause() throws RemoteException { 2358 postToHandler(MessageHandler.MSG_PAUSE); 2359 } 2360 2361 @Override 2362 public void stop() throws RemoteException { 2363 postToHandler(MessageHandler.MSG_STOP); 2364 } 2365 2366 @Override 2367 public void next() throws RemoteException { 2368 postToHandler(MessageHandler.MSG_NEXT); 2369 } 2370 2371 @Override 2372 public void previous() throws RemoteException { 2373 postToHandler(MessageHandler.MSG_PREVIOUS); 2374 } 2375 2376 @Override 2377 public void fastForward() throws RemoteException { 2378 postToHandler(MessageHandler.MSG_FAST_FORWARD); 2379 } 2380 2381 @Override 2382 public void rewind() throws RemoteException { 2383 postToHandler(MessageHandler.MSG_REWIND); 2384 } 2385 2386 @Override 2387 public void seekTo(long pos) throws RemoteException { 2388 postToHandler(MessageHandler.MSG_SEEK_TO, pos); 2389 } 2390 2391 @Override 2392 public void rate(RatingCompat rating) throws RemoteException { 2393 postToHandler(MessageHandler.MSG_RATE, rating); 2394 } 2395 2396 @Override 2397 public void setCaptioningEnabled(boolean enabled) throws RemoteException { 2398 postToHandler(MessageHandler.MSG_SET_CAPTIONING_ENABLED, enabled); 2399 } 2400 2401 @Override 2402 public void setRepeatMode(int repeatMode) throws RemoteException { 2403 postToHandler(MessageHandler.MSG_SET_REPEAT_MODE, repeatMode); 2404 } 2405 2406 @Override 2407 public void setShuffleModeEnabled(boolean enabled) throws RemoteException { 2408 postToHandler(MessageHandler.MSG_SET_SHUFFLE_MODE_ENABLED, enabled); 2409 } 2410 2411 @Override 2412 public void sendCustomAction(String action, Bundle args) 2413 throws RemoteException { 2414 postToHandler(MessageHandler.MSG_CUSTOM_ACTION, action, args); 2415 } 2416 2417 @Override 2418 public MediaMetadataCompat getMetadata() { 2419 return mMetadata; 2420 } 2421 2422 @Override 2423 public PlaybackStateCompat getPlaybackState() { 2424 return getStateWithUpdatedPosition(); 2425 } 2426 2427 @Override 2428 public List<QueueItem> getQueue() { 2429 synchronized (mLock) { 2430 return mQueue; 2431 } 2432 } 2433 2434 @Override 2435 public void addQueueItem(MediaDescriptionCompat description) { 2436 postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM, description); 2437 } 2438 2439 @Override 2440 public void addQueueItemAt(MediaDescriptionCompat description, int index) { 2441 postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index); 2442 } 2443 2444 @Override 2445 public void removeQueueItem(MediaDescriptionCompat description) { 2446 postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM, description); 2447 } 2448 2449 @Override 2450 public void removeQueueItemAt(int index) { 2451 postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index); 2452 } 2453 2454 @Override 2455 public CharSequence getQueueTitle() { 2456 return mQueueTitle; 2457 } 2458 2459 @Override 2460 public Bundle getExtras() { 2461 synchronized (mLock) { 2462 return mExtras; 2463 } 2464 } 2465 2466 @Override 2467 @RatingCompat.Style 2468 public int getRatingType() { 2469 return mRatingType; 2470 } 2471 2472 @Override 2473 public boolean isCaptioningEnabled() { 2474 return mCaptioningEnabled; 2475 } 2476 2477 @Override 2478 @PlaybackStateCompat.RepeatMode 2479 public int getRepeatMode() { 2480 return mRepeatMode; 2481 } 2482 2483 @Override 2484 public boolean isShuffleModeEnabled() { 2485 return mShuffleModeEnabled; 2486 } 2487 2488 @Override 2489 public boolean isTransportControlEnabled() { 2490 return (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0; 2491 } 2492 } 2493 2494 private static final class Command { 2495 public final String command; 2496 public final Bundle extras; 2497 public final ResultReceiver stub; 2498 2499 public Command(String command, Bundle extras, ResultReceiver stub) { 2500 this.command = command; 2501 this.extras = extras; 2502 this.stub = stub; 2503 } 2504 } 2505 2506 class MessageHandler extends Handler { 2507 2508 private static final int MSG_COMMAND = 1; 2509 private static final int MSG_ADJUST_VOLUME = 2; 2510 private static final int MSG_PREPARE = 3; 2511 private static final int MSG_PREPARE_MEDIA_ID = 4; 2512 private static final int MSG_PREPARE_SEARCH = 5; 2513 private static final int MSG_PREPARE_URI = 6; 2514 private static final int MSG_PLAY = 7; 2515 private static final int MSG_PLAY_MEDIA_ID = 8; 2516 private static final int MSG_PLAY_SEARCH = 9; 2517 private static final int MSG_PLAY_URI = 10; 2518 private static final int MSG_SKIP_TO_ITEM = 11; 2519 private static final int MSG_PAUSE = 12; 2520 private static final int MSG_STOP = 13; 2521 private static final int MSG_NEXT = 14; 2522 private static final int MSG_PREVIOUS = 15; 2523 private static final int MSG_FAST_FORWARD = 16; 2524 private static final int MSG_REWIND = 17; 2525 private static final int MSG_SEEK_TO = 18; 2526 private static final int MSG_RATE = 19; 2527 private static final int MSG_CUSTOM_ACTION = 20; 2528 private static final int MSG_MEDIA_BUTTON = 21; 2529 private static final int MSG_SET_VOLUME = 22; 2530 private static final int MSG_SET_REPEAT_MODE = 23; 2531 private static final int MSG_SET_SHUFFLE_MODE_ENABLED = 24; 2532 private static final int MSG_ADD_QUEUE_ITEM = 25; 2533 private static final int MSG_ADD_QUEUE_ITEM_AT = 26; 2534 private static final int MSG_REMOVE_QUEUE_ITEM = 27; 2535 private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28; 2536 private static final int MSG_SET_CAPTIONING_ENABLED = 29; 2537 2538 // KeyEvent constants only available on API 11+ 2539 private static final int KEYCODE_MEDIA_PAUSE = 127; 2540 private static final int KEYCODE_MEDIA_PLAY = 126; 2541 2542 public MessageHandler(Looper looper) { 2543 super(looper); 2544 } 2545 2546 public void post(int what, Object obj, Bundle bundle) { 2547 Message msg = obtainMessage(what, obj); 2548 msg.setData(bundle); 2549 msg.sendToTarget(); 2550 } 2551 2552 public void post(int what, Object obj) { 2553 obtainMessage(what, obj).sendToTarget(); 2554 } 2555 2556 public void post(int what) { 2557 post(what, null); 2558 } 2559 2560 public void post(int what, Object obj, int arg1) { 2561 obtainMessage(what, arg1, 0, obj).sendToTarget(); 2562 } 2563 2564 @Override 2565 public void handleMessage(Message msg) { 2566 MediaSessionCompat.Callback cb = mCallback; 2567 if (cb == null) { 2568 return; 2569 } 2570 switch (msg.what) { 2571 case MSG_COMMAND: 2572 Command cmd = (Command) msg.obj; 2573 cb.onCommand(cmd.command, cmd.extras, cmd.stub); 2574 break; 2575 case MSG_MEDIA_BUTTON: 2576 KeyEvent keyEvent = (KeyEvent) msg.obj; 2577 Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); 2578 intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 2579 // Let the Callback handle events first before using the default behavior 2580 if (!cb.onMediaButtonEvent(intent)) { 2581 onMediaButtonEvent(keyEvent, cb); 2582 } 2583 break; 2584 case MSG_PREPARE: 2585 cb.onPrepare(); 2586 break; 2587 case MSG_PREPARE_MEDIA_ID: 2588 cb.onPrepareFromMediaId((String) msg.obj, msg.getData()); 2589 break; 2590 case MSG_PREPARE_SEARCH: 2591 cb.onPrepareFromSearch((String) msg.obj, msg.getData()); 2592 break; 2593 case MSG_PREPARE_URI: 2594 cb.onPrepareFromUri((Uri) msg.obj, msg.getData()); 2595 break; 2596 case MSG_PLAY: 2597 cb.onPlay(); 2598 break; 2599 case MSG_PLAY_MEDIA_ID: 2600 cb.onPlayFromMediaId((String) msg.obj, msg.getData()); 2601 break; 2602 case MSG_PLAY_SEARCH: 2603 cb.onPlayFromSearch((String) msg.obj, msg.getData()); 2604 break; 2605 case MSG_PLAY_URI: 2606 cb.onPlayFromUri((Uri) msg.obj, msg.getData()); 2607 break; 2608 case MSG_SKIP_TO_ITEM: 2609 cb.onSkipToQueueItem((Long) msg.obj); 2610 break; 2611 case MSG_PAUSE: 2612 cb.onPause(); 2613 break; 2614 case MSG_STOP: 2615 cb.onStop(); 2616 break; 2617 case MSG_NEXT: 2618 cb.onSkipToNext(); 2619 break; 2620 case MSG_PREVIOUS: 2621 cb.onSkipToPrevious(); 2622 break; 2623 case MSG_FAST_FORWARD: 2624 cb.onFastForward(); 2625 break; 2626 case MSG_REWIND: 2627 cb.onRewind(); 2628 break; 2629 case MSG_SEEK_TO: 2630 cb.onSeekTo((Long) msg.obj); 2631 break; 2632 case MSG_RATE: 2633 cb.onSetRating((RatingCompat) msg.obj); 2634 break; 2635 case MSG_CUSTOM_ACTION: 2636 cb.onCustomAction((String) msg.obj, msg.getData()); 2637 break; 2638 case MSG_ADD_QUEUE_ITEM: 2639 cb.onAddQueueItem((MediaDescriptionCompat) msg.obj); 2640 break; 2641 case MSG_ADD_QUEUE_ITEM_AT: 2642 cb.onAddQueueItem((MediaDescriptionCompat) msg.obj, msg.arg1); 2643 break; 2644 case MSG_REMOVE_QUEUE_ITEM: 2645 cb.onRemoveQueueItem((MediaDescriptionCompat) msg.obj); 2646 break; 2647 case MSG_REMOVE_QUEUE_ITEM_AT: 2648 if (mQueue != null) { 2649 QueueItem item = (msg.arg1 >= 0 && msg.arg1 < mQueue.size()) 2650 ? mQueue.get(msg.arg1) : null; 2651 if (item != null) { 2652 cb.onRemoveQueueItem(item.getDescription()); 2653 } 2654 } 2655 break; 2656 case MSG_ADJUST_VOLUME: 2657 adjustVolume(msg.arg1, 0); 2658 break; 2659 case MSG_SET_VOLUME: 2660 setVolumeTo(msg.arg1, 0); 2661 break; 2662 case MSG_SET_CAPTIONING_ENABLED: 2663 cb.onSetCaptioningEnabled((boolean) msg.obj); 2664 break; 2665 case MSG_SET_REPEAT_MODE: 2666 cb.onSetRepeatMode(msg.arg1); 2667 break; 2668 case MSG_SET_SHUFFLE_MODE_ENABLED: 2669 cb.onSetShuffleModeEnabled((boolean) msg.obj); 2670 break; 2671 } 2672 } 2673 2674 private void onMediaButtonEvent(KeyEvent ke, MediaSessionCompat.Callback cb) { 2675 if (ke == null || ke.getAction() != KeyEvent.ACTION_DOWN) { 2676 return; 2677 } 2678 long validActions = mState == null ? 0 : mState.getActions(); 2679 switch (ke.getKeyCode()) { 2680 // Note KeyEvent.KEYCODE_MEDIA_PLAY is API 11+ 2681 case KEYCODE_MEDIA_PLAY: 2682 if ((validActions & PlaybackStateCompat.ACTION_PLAY) != 0) { 2683 cb.onPlay(); 2684 } 2685 break; 2686 // Note KeyEvent.KEYCODE_MEDIA_PAUSE is API 11+ 2687 case KEYCODE_MEDIA_PAUSE: 2688 if ((validActions & PlaybackStateCompat.ACTION_PAUSE) != 0) { 2689 cb.onPause(); 2690 } 2691 break; 2692 case KeyEvent.KEYCODE_MEDIA_NEXT: 2693 if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) { 2694 cb.onSkipToNext(); 2695 } 2696 break; 2697 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 2698 if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) { 2699 cb.onSkipToPrevious(); 2700 } 2701 break; 2702 case KeyEvent.KEYCODE_MEDIA_STOP: 2703 if ((validActions & PlaybackStateCompat.ACTION_STOP) != 0) { 2704 cb.onStop(); 2705 } 2706 break; 2707 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 2708 if ((validActions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) { 2709 cb.onFastForward(); 2710 } 2711 break; 2712 case KeyEvent.KEYCODE_MEDIA_REWIND: 2713 if ((validActions & PlaybackStateCompat.ACTION_REWIND) != 0) { 2714 cb.onRewind(); 2715 } 2716 break; 2717 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 2718 case KeyEvent.KEYCODE_HEADSETHOOK: 2719 boolean isPlaying = mState != null 2720 && mState.getState() == PlaybackStateCompat.STATE_PLAYING; 2721 boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE 2722 | PlaybackStateCompat.ACTION_PLAY)) != 0; 2723 boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE 2724 | PlaybackStateCompat.ACTION_PAUSE)) != 0; 2725 if (isPlaying && canPause) { 2726 cb.onPause(); 2727 } else if (!isPlaying && canPlay) { 2728 cb.onPlay(); 2729 } 2730 break; 2731 } 2732 } 2733 } 2734 } 2735 2736 @RequiresApi(18) 2737 static class MediaSessionImplApi18 extends MediaSessionImplBase { 2738 private static boolean sIsMbrPendingIntentSupported = true; 2739 2740 MediaSessionImplApi18(Context context, String tag, ComponentName mbrComponent, 2741 PendingIntent mbrIntent) { 2742 super(context, tag, mbrComponent, mbrIntent); 2743 } 2744 2745 @Override 2746 public void setCallback(Callback callback, Handler handler) { 2747 super.setCallback(callback, handler); 2748 if (callback == null) { 2749 mRcc.setPlaybackPositionUpdateListener(null); 2750 } else { 2751 RemoteControlClient.OnPlaybackPositionUpdateListener listener = 2752 new RemoteControlClient.OnPlaybackPositionUpdateListener() { 2753 @Override 2754 public void onPlaybackPositionUpdate(long newPositionMs) { 2755 postToHandler(MessageHandler.MSG_SEEK_TO, newPositionMs); 2756 } 2757 }; 2758 mRcc.setPlaybackPositionUpdateListener(listener); 2759 } 2760 } 2761 2762 @Override 2763 void setRccState(PlaybackStateCompat state) { 2764 long position = state.getPosition(); 2765 float speed = state.getPlaybackSpeed(); 2766 long updateTime = state.getLastPositionUpdateTime(); 2767 long currTime = SystemClock.elapsedRealtime(); 2768 if (state.getState() == PlaybackStateCompat.STATE_PLAYING && position > 0) { 2769 long diff = 0; 2770 if (updateTime > 0) { 2771 diff = currTime - updateTime; 2772 if (speed > 0 && speed != 1f) { 2773 diff = (long) (diff * speed); 2774 } 2775 } 2776 position += diff; 2777 } 2778 mRcc.setPlaybackState(getRccStateFromState(state.getState()), position, speed); 2779 } 2780 2781 @Override 2782 int getRccTransportControlFlagsFromActions(long actions) { 2783 int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions); 2784 if ((actions & PlaybackStateCompat.ACTION_SEEK_TO) != 0) { 2785 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE; 2786 } 2787 return transportControlFlags; 2788 } 2789 2790 @Override 2791 void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) { 2792 // Some Android implementations are not able to register a media button event receiver 2793 // using a PendingIntent but need a ComponentName instead. These will raise a 2794 // NullPointerException. 2795 if (sIsMbrPendingIntentSupported) { 2796 try { 2797 mAudioManager.registerMediaButtonEventReceiver(mbrIntent); 2798 } catch (NullPointerException e) { 2799 Log.w(TAG, "Unable to register media button event receiver with " 2800 + "PendingIntent, falling back to ComponentName."); 2801 sIsMbrPendingIntentSupported = false; 2802 } 2803 } 2804 2805 if (!sIsMbrPendingIntentSupported) { 2806 super.registerMediaButtonEventReceiver(mbrIntent, mbrComponent); 2807 } 2808 } 2809 2810 @Override 2811 void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent, 2812 ComponentName mbrComponent) { 2813 if (sIsMbrPendingIntentSupported) { 2814 mAudioManager.unregisterMediaButtonEventReceiver(mbrIntent); 2815 } else { 2816 super.unregisterMediaButtonEventReceiver(mbrIntent, mbrComponent); 2817 } 2818 } 2819 } 2820 2821 @RequiresApi(19) 2822 static class MediaSessionImplApi19 extends MediaSessionImplApi18 { 2823 MediaSessionImplApi19(Context context, String tag, ComponentName mbrComponent, 2824 PendingIntent mbrIntent) { 2825 super(context, tag, mbrComponent, mbrIntent); 2826 } 2827 2828 @Override 2829 public void setCallback(Callback callback, Handler handler) { 2830 super.setCallback(callback, handler); 2831 if (callback == null) { 2832 mRcc.setMetadataUpdateListener(null); 2833 } else { 2834 RemoteControlClient.OnMetadataUpdateListener listener = 2835 new RemoteControlClient.OnMetadataUpdateListener() { 2836 @Override 2837 public void onMetadataUpdate(int key, Object newValue) { 2838 if (key == MediaMetadataEditor.RATING_KEY_BY_USER 2839 && newValue instanceof Rating) { 2840 postToHandler(MessageHandler.MSG_RATE, 2841 RatingCompat.fromRating(newValue)); 2842 } 2843 } 2844 }; 2845 mRcc.setMetadataUpdateListener(listener); 2846 } 2847 } 2848 2849 @Override 2850 int getRccTransportControlFlagsFromActions(long actions) { 2851 int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions); 2852 if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) { 2853 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_RATING; 2854 } 2855 return transportControlFlags; 2856 } 2857 2858 @Override 2859 RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) { 2860 RemoteControlClient.MetadataEditor editor = super.buildRccMetadata(metadata); 2861 long actions = mState == null ? 0 : mState.getActions(); 2862 if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) { 2863 editor.addEditableKey(RemoteControlClient.MetadataEditor.RATING_KEY_BY_USER); 2864 } 2865 2866 if (metadata == null) { 2867 return editor; 2868 } 2869 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_YEAR)) { 2870 editor.putLong(MediaMetadataRetriever.METADATA_KEY_YEAR, 2871 metadata.getLong(MediaMetadataCompat.METADATA_KEY_YEAR)); 2872 } 2873 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_RATING)) { 2874 editor.putObject(MediaMetadataEditor.RATING_KEY_BY_OTHERS, 2875 metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_RATING)); 2876 } 2877 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_USER_RATING)) { 2878 editor.putObject(MediaMetadataEditor.RATING_KEY_BY_USER, 2879 metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_USER_RATING)); 2880 } 2881 return editor; 2882 } 2883 } 2884 2885 @RequiresApi(21) 2886 static class MediaSessionImplApi21 implements MediaSessionImpl { 2887 private final Object mSessionObj; 2888 private final Token mToken; 2889 2890 private boolean mDestroyed = false; 2891 private final RemoteCallbackList<IMediaControllerCallback> mExtraControllerCallbacks = 2892 new RemoteCallbackList<>(); 2893 2894 private PlaybackStateCompat mPlaybackState; 2895 private List<QueueItem> mQueue; 2896 @RatingCompat.Style int mRatingType; 2897 boolean mCaptioningEnabled; 2898 @PlaybackStateCompat.RepeatMode int mRepeatMode; 2899 boolean mShuffleModeEnabled; 2900 2901 public MediaSessionImplApi21(Context context, String tag) { 2902 mSessionObj = MediaSessionCompatApi21.createSession(context, tag); 2903 mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj), 2904 new ExtraSession()); 2905 } 2906 2907 public MediaSessionImplApi21(Object mediaSession) { 2908 mSessionObj = MediaSessionCompatApi21.verifySession(mediaSession); 2909 mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj), 2910 new ExtraSession()); 2911 } 2912 2913 @Override 2914 public void setCallback(Callback callback, Handler handler) { 2915 MediaSessionCompatApi21.setCallback(mSessionObj, 2916 callback == null ? null : callback.mCallbackObj, handler); 2917 if (callback != null) { 2918 callback.mSessionImpl = new WeakReference<MediaSessionImpl>(this); 2919 } 2920 } 2921 2922 @Override 2923 public void setFlags(@SessionFlags int flags) { 2924 MediaSessionCompatApi21.setFlags(mSessionObj, flags); 2925 } 2926 2927 @Override 2928 public void setPlaybackToLocal(int stream) { 2929 MediaSessionCompatApi21.setPlaybackToLocal(mSessionObj, stream); 2930 } 2931 2932 @Override 2933 public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) { 2934 MediaSessionCompatApi21.setPlaybackToRemote(mSessionObj, 2935 volumeProvider.getVolumeProvider()); 2936 } 2937 2938 @Override 2939 public void setActive(boolean active) { 2940 MediaSessionCompatApi21.setActive(mSessionObj, active); 2941 } 2942 2943 @Override 2944 public boolean isActive() { 2945 return MediaSessionCompatApi21.isActive(mSessionObj); 2946 } 2947 2948 @Override 2949 public void sendSessionEvent(String event, Bundle extras) { 2950 if (android.os.Build.VERSION.SDK_INT < 23) { 2951 int size = mExtraControllerCallbacks.beginBroadcast(); 2952 for (int i = size - 1; i >= 0; i--) { 2953 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 2954 try { 2955 cb.onEvent(event, extras); 2956 } catch (RemoteException e) { 2957 } 2958 } 2959 mExtraControllerCallbacks.finishBroadcast(); 2960 } 2961 MediaSessionCompatApi21.sendSessionEvent(mSessionObj, event, extras); 2962 } 2963 2964 @Override 2965 public void release() { 2966 mDestroyed = true; 2967 MediaSessionCompatApi21.release(mSessionObj); 2968 } 2969 2970 @Override 2971 public Token getSessionToken() { 2972 return mToken; 2973 } 2974 2975 @Override 2976 public void setPlaybackState(PlaybackStateCompat state) { 2977 mPlaybackState = state; 2978 int size = mExtraControllerCallbacks.beginBroadcast(); 2979 for (int i = size - 1; i >= 0; i--) { 2980 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 2981 try { 2982 cb.onPlaybackStateChanged(state); 2983 } catch (RemoteException e) { 2984 } 2985 } 2986 mExtraControllerCallbacks.finishBroadcast(); 2987 MediaSessionCompatApi21.setPlaybackState(mSessionObj, 2988 state == null ? null : state.getPlaybackState()); 2989 } 2990 2991 @Override 2992 public void setMetadata(MediaMetadataCompat metadata) { 2993 MediaSessionCompatApi21.setMetadata(mSessionObj, 2994 metadata == null ? null : metadata.getMediaMetadata()); 2995 } 2996 2997 @Override 2998 public void setSessionActivity(PendingIntent pi) { 2999 MediaSessionCompatApi21.setSessionActivity(mSessionObj, pi); 3000 } 3001 3002 @Override 3003 public void setMediaButtonReceiver(PendingIntent mbr) { 3004 MediaSessionCompatApi21.setMediaButtonReceiver(mSessionObj, mbr); 3005 } 3006 3007 @Override 3008 public void setQueue(List<QueueItem> queue) { 3009 mQueue = queue; 3010 List<Object> queueObjs = null; 3011 if (queue != null) { 3012 queueObjs = new ArrayList<>(); 3013 for (QueueItem item : queue) { 3014 queueObjs.add(item.getQueueItem()); 3015 } 3016 } 3017 MediaSessionCompatApi21.setQueue(mSessionObj, queueObjs); 3018 } 3019 3020 @Override 3021 public void setQueueTitle(CharSequence title) { 3022 MediaSessionCompatApi21.setQueueTitle(mSessionObj, title); 3023 } 3024 3025 @Override 3026 public void setRatingType(@RatingCompat.Style int type) { 3027 if (android.os.Build.VERSION.SDK_INT < 22) { 3028 mRatingType = type; 3029 } else { 3030 MediaSessionCompatApi22.setRatingType(mSessionObj, type); 3031 } 3032 } 3033 3034 @Override 3035 public void setCaptioningEnabled(boolean enabled) { 3036 if (mCaptioningEnabled != enabled) { 3037 mCaptioningEnabled = enabled; 3038 int size = mExtraControllerCallbacks.beginBroadcast(); 3039 for (int i = size - 1; i >= 0; i--) { 3040 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 3041 try { 3042 cb.onCaptioningEnabledChanged(enabled); 3043 } catch (RemoteException e) { 3044 } 3045 } 3046 mExtraControllerCallbacks.finishBroadcast(); 3047 } 3048 } 3049 3050 @Override 3051 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 3052 if (mRepeatMode != repeatMode) { 3053 mRepeatMode = repeatMode; 3054 int size = mExtraControllerCallbacks.beginBroadcast(); 3055 for (int i = size - 1; i >= 0; i--) { 3056 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 3057 try { 3058 cb.onRepeatModeChanged(repeatMode); 3059 } catch (RemoteException e) { 3060 } 3061 } 3062 mExtraControllerCallbacks.finishBroadcast(); 3063 } 3064 } 3065 3066 @Override 3067 public void setShuffleModeEnabled(boolean enabled) { 3068 if (mShuffleModeEnabled != enabled) { 3069 mShuffleModeEnabled = enabled; 3070 int size = mExtraControllerCallbacks.beginBroadcast(); 3071 for (int i = size - 1; i >= 0; i--) { 3072 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 3073 try { 3074 cb.onShuffleModeChanged(enabled); 3075 } catch (RemoteException e) { 3076 } 3077 } 3078 mExtraControllerCallbacks.finishBroadcast(); 3079 } 3080 } 3081 3082 @Override 3083 public void setExtras(Bundle extras) { 3084 MediaSessionCompatApi21.setExtras(mSessionObj, extras); 3085 } 3086 3087 @Override 3088 public Object getMediaSession() { 3089 return mSessionObj; 3090 } 3091 3092 @Override 3093 public Object getRemoteControlClient() { 3094 return null; 3095 } 3096 3097 @Override 3098 public String getCallingPackage() { 3099 if (android.os.Build.VERSION.SDK_INT < 24) { 3100 return null; 3101 } else { 3102 return MediaSessionCompatApi24.getCallingPackage(mSessionObj); 3103 } 3104 } 3105 3106 class ExtraSession extends IMediaSession.Stub { 3107 @Override 3108 public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) { 3109 // Will not be called. 3110 throw new AssertionError(); 3111 } 3112 3113 @Override 3114 public boolean sendMediaButton(KeyEvent mediaButton) { 3115 // Will not be called. 3116 throw new AssertionError(); 3117 } 3118 3119 @Override 3120 public void registerCallbackListener(IMediaControllerCallback cb) { 3121 if (!mDestroyed) { 3122 mExtraControllerCallbacks.register(cb); 3123 } 3124 } 3125 3126 @Override 3127 public void unregisterCallbackListener(IMediaControllerCallback cb) { 3128 mExtraControllerCallbacks.unregister(cb); 3129 } 3130 3131 @Override 3132 public String getPackageName() { 3133 // Will not be called. 3134 throw new AssertionError(); 3135 } 3136 3137 @Override 3138 public String getTag() { 3139 // Will not be called. 3140 throw new AssertionError(); 3141 } 3142 3143 @Override 3144 public PendingIntent getLaunchPendingIntent() { 3145 // Will not be called. 3146 throw new AssertionError(); 3147 } 3148 3149 @Override 3150 @SessionFlags 3151 public long getFlags() { 3152 // Will not be called. 3153 throw new AssertionError(); 3154 } 3155 3156 @Override 3157 public ParcelableVolumeInfo getVolumeAttributes() { 3158 // Will not be called. 3159 throw new AssertionError(); 3160 } 3161 3162 @Override 3163 public void adjustVolume(int direction, int flags, String packageName) { 3164 // Will not be called. 3165 throw new AssertionError(); 3166 } 3167 3168 @Override 3169 public void setVolumeTo(int value, int flags, String packageName) { 3170 // Will not be called. 3171 throw new AssertionError(); 3172 } 3173 3174 @Override 3175 public void prepare() throws RemoteException { 3176 // Will not be called. 3177 throw new AssertionError(); 3178 } 3179 3180 @Override 3181 public void prepareFromMediaId(String mediaId, Bundle extras) throws RemoteException { 3182 // Will not be called. 3183 throw new AssertionError(); 3184 } 3185 3186 @Override 3187 public void prepareFromSearch(String query, Bundle extras) throws RemoteException { 3188 // Will not be called. 3189 throw new AssertionError(); 3190 } 3191 3192 @Override 3193 public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException { 3194 // Will not be called. 3195 throw new AssertionError(); 3196 } 3197 3198 @Override 3199 public void play() throws RemoteException { 3200 // Will not be called. 3201 throw new AssertionError(); 3202 } 3203 3204 @Override 3205 public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException { 3206 // Will not be called. 3207 throw new AssertionError(); 3208 } 3209 3210 @Override 3211 public void playFromSearch(String query, Bundle extras) throws RemoteException { 3212 // Will not be called. 3213 throw new AssertionError(); 3214 } 3215 3216 @Override 3217 public void playFromUri(Uri uri, Bundle extras) throws RemoteException { 3218 // Will not be called. 3219 throw new AssertionError(); 3220 } 3221 3222 @Override 3223 public void skipToQueueItem(long id) { 3224 // Will not be called. 3225 throw new AssertionError(); 3226 } 3227 3228 @Override 3229 public void pause() throws RemoteException { 3230 // Will not be called. 3231 throw new AssertionError(); 3232 } 3233 3234 @Override 3235 public void stop() throws RemoteException { 3236 // Will not be called. 3237 throw new AssertionError(); 3238 } 3239 3240 @Override 3241 public void next() throws RemoteException { 3242 // Will not be called. 3243 throw new AssertionError(); 3244 } 3245 3246 @Override 3247 public void previous() throws RemoteException { 3248 // Will not be called. 3249 throw new AssertionError(); 3250 } 3251 3252 @Override 3253 public void fastForward() throws RemoteException { 3254 // Will not be called. 3255 throw new AssertionError(); 3256 } 3257 3258 @Override 3259 public void rewind() throws RemoteException { 3260 // Will not be called. 3261 throw new AssertionError(); 3262 } 3263 3264 @Override 3265 public void seekTo(long pos) throws RemoteException { 3266 // Will not be called. 3267 throw new AssertionError(); 3268 } 3269 3270 @Override 3271 public void rate(RatingCompat rating) throws RemoteException { 3272 // Will not be called. 3273 throw new AssertionError(); 3274 } 3275 3276 @Override 3277 public void setCaptioningEnabled(boolean enabled) throws RemoteException { 3278 // Will not be called. 3279 throw new AssertionError(); 3280 } 3281 3282 @Override 3283 public void setRepeatMode(int repeatMode) throws RemoteException { 3284 // Will not be called. 3285 throw new AssertionError(); 3286 } 3287 3288 @Override 3289 public void setShuffleModeEnabled(boolean enabled) throws RemoteException { 3290 // Will not be called. 3291 throw new AssertionError(); 3292 } 3293 3294 @Override 3295 public void sendCustomAction(String action, Bundle args) throws RemoteException { 3296 // Will not be called. 3297 throw new AssertionError(); 3298 } 3299 3300 @Override 3301 public MediaMetadataCompat getMetadata() { 3302 // Will not be called. 3303 throw new AssertionError(); 3304 } 3305 3306 @Override 3307 public PlaybackStateCompat getPlaybackState() { 3308 return mPlaybackState; 3309 } 3310 3311 @Override 3312 public List<QueueItem> getQueue() { 3313 // Will not be called. 3314 return null; 3315 } 3316 3317 @Override 3318 public void addQueueItem(MediaDescriptionCompat descriptionCompat) { 3319 // Will not be called. 3320 throw new AssertionError(); 3321 } 3322 3323 @Override 3324 public void addQueueItemAt(MediaDescriptionCompat descriptionCompat, int index) { 3325 // Will not be called. 3326 throw new AssertionError(); 3327 } 3328 3329 @Override 3330 public void removeQueueItem(MediaDescriptionCompat description) { 3331 // Will not be called. 3332 throw new AssertionError(); 3333 } 3334 3335 @Override 3336 public void removeQueueItemAt(int index) { 3337 // Will not be called. 3338 throw new AssertionError(); 3339 } 3340 3341 @Override 3342 public CharSequence getQueueTitle() { 3343 // Will not be called. 3344 throw new AssertionError(); 3345 } 3346 3347 @Override 3348 public Bundle getExtras() { 3349 // Will not be called. 3350 throw new AssertionError(); 3351 } 3352 3353 @Override 3354 @RatingCompat.Style 3355 public int getRatingType() { 3356 return mRatingType; 3357 } 3358 3359 @Override 3360 public boolean isCaptioningEnabled() { 3361 return mCaptioningEnabled; 3362 } 3363 3364 @Override 3365 @PlaybackStateCompat.RepeatMode 3366 public int getRepeatMode() { 3367 return mRepeatMode; 3368 } 3369 3370 @Override 3371 public boolean isShuffleModeEnabled() { 3372 return mShuffleModeEnabled; 3373 } 3374 3375 @Override 3376 public boolean isTransportControlEnabled() { 3377 // Will not be called. 3378 throw new AssertionError(); 3379 } 3380 } 3381 } 3382} 3383