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