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