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