MediaSessionRecord.java revision 6b171b36f87268cd954f753458c06704fe30d8bf
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.media; 18 19import android.app.PendingIntent; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.Intent; 23import android.content.pm.ParceledListSlice; 24import android.media.AudioManager; 25import android.media.AudioManagerInternal; 26import android.media.MediaMetadata; 27import android.media.Rating; 28import android.media.VolumeProvider; 29import android.media.routing.IMediaRouter; 30import android.media.routing.IMediaRouterDelegate; 31import android.media.routing.IMediaRouterStateCallback; 32import android.media.session.ISession; 33import android.media.session.ISessionCallback; 34import android.media.session.ISessionController; 35import android.media.session.ISessionControllerCallback; 36import android.media.session.MediaController; 37import android.media.session.MediaController.PlaybackInfo; 38import android.media.session.MediaSession; 39import android.media.session.ParcelableVolumeInfo; 40import android.media.session.PlaybackState; 41import android.media.AudioAttributes; 42import android.net.Uri; 43import android.os.Binder; 44import android.os.Bundle; 45import android.os.DeadObjectException; 46import android.os.Handler; 47import android.os.IBinder; 48import android.os.Looper; 49import android.os.Message; 50import android.os.RemoteException; 51import android.os.ResultReceiver; 52import android.os.SystemClock; 53import android.util.Log; 54import android.util.Slog; 55import android.view.KeyEvent; 56 57import com.android.server.LocalServices; 58 59import java.io.PrintWriter; 60import java.util.ArrayList; 61import java.util.UUID; 62 63/** 64 * This is the system implementation of a Session. Apps will interact with the 65 * MediaSession wrapper class instead. 66 */ 67public class MediaSessionRecord implements IBinder.DeathRecipient { 68 private static final String TAG = "MediaSessionRecord"; 69 private static final boolean DEBUG = false; 70 71 /** 72 * The length of time a session will still be considered active after 73 * pausing in ms. 74 */ 75 private static final int ACTIVE_BUFFER = 30000; 76 77 /** 78 * The amount of time we'll send an assumed volume after the last volume 79 * command before reverting to the last reported volume. 80 */ 81 private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000; 82 83 private final MessageHandler mHandler; 84 85 private final int mOwnerPid; 86 private final int mOwnerUid; 87 private final int mUserId; 88 private final String mPackageName; 89 private final String mTag; 90 private final ControllerStub mController; 91 private final SessionStub mSession; 92 private final SessionCb mSessionCb; 93 private final MediaSessionService mService; 94 95 private final Object mLock = new Object(); 96 private final ArrayList<ISessionControllerCallback> mControllerCallbacks = 97 new ArrayList<ISessionControllerCallback>(); 98 99 private long mFlags; 100 private IMediaRouter mMediaRouter; 101 private PendingIntent mMediaButtonReceiver; 102 private PendingIntent mLaunchIntent; 103 104 // TransportPerformer fields 105 106 private Bundle mExtras; 107 private MediaMetadata mMetadata; 108 private PlaybackState mPlaybackState; 109 private ParceledListSlice mQueue; 110 private CharSequence mQueueTitle; 111 private int mRatingType; 112 private long mLastActiveTime; 113 // End TransportPerformer fields 114 115 // Volume handling fields 116 private AudioAttributes mAudioAttrs; 117 private AudioManager mAudioManager; 118 private AudioManagerInternal mAudioManagerInternal; 119 private int mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; 120 private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; 121 private int mMaxVolume = 0; 122 private int mCurrentVolume = 0; 123 private int mOptimisticVolume = -1; 124 // End volume handling fields 125 126 private boolean mIsActive = false; 127 private boolean mDestroyed = false; 128 129 public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, 130 ISessionCallback cb, String tag, MediaSessionService service, Handler handler) { 131 mOwnerPid = ownerPid; 132 mOwnerUid = ownerUid; 133 mUserId = userId; 134 mPackageName = ownerPackageName; 135 mTag = tag; 136 mController = new ControllerStub(); 137 mSession = new SessionStub(); 138 mSessionCb = new SessionCb(cb); 139 mService = service; 140 mHandler = new MessageHandler(handler.getLooper()); 141 mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE); 142 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); 143 mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(); 144 } 145 146 /** 147 * Get the binder for the {@link MediaSession}. 148 * 149 * @return The session binder apps talk to. 150 */ 151 public ISession getSessionBinder() { 152 return mSession; 153 } 154 155 /** 156 * Get the binder for the {@link MediaController}. 157 * 158 * @return The controller binder apps talk to. 159 */ 160 public ISessionController getControllerBinder() { 161 return mController; 162 } 163 164 /** 165 * Get the info for this session. 166 * 167 * @return Info that identifies this session. 168 */ 169 public String getPackageName() { 170 return mPackageName; 171 } 172 173 /** 174 * Get the tag for the session. 175 * 176 * @return The session's tag. 177 */ 178 public String getTag() { 179 return mTag; 180 } 181 182 /** 183 * Get the intent the app set for their media button receiver. 184 * 185 * @return The pending intent set by the app or null. 186 */ 187 public PendingIntent getMediaButtonReceiver() { 188 return mMediaButtonReceiver; 189 } 190 191 /** 192 * Get this session's flags. 193 * 194 * @return The flags for this session. 195 */ 196 public long getFlags() { 197 return mFlags; 198 } 199 200 /** 201 * Check if this session has the specified flag. 202 * 203 * @param flag The flag to check. 204 * @return True if this session has that flag set, false otherwise. 205 */ 206 public boolean hasFlag(int flag) { 207 return (mFlags & flag) != 0; 208 } 209 210 /** 211 * Get the user id this session was created for. 212 * 213 * @return The user id for this session. 214 */ 215 public int getUserId() { 216 return mUserId; 217 } 218 219 /** 220 * Check if this session has system priorty and should receive media buttons 221 * before any other sessions. 222 * 223 * @return True if this is a system priority session, false otherwise 224 */ 225 public boolean isSystemPriority() { 226 return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0; 227 } 228 229 /** 230 * Send a volume adjustment to the session owner. Direction must be one of 231 * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, 232 * {@link AudioManager#ADJUST_SAME}. 233 * 234 * @param direction The direction to adjust volume in. 235 */ 236 public void adjustVolume(int direction, int flags, String packageName, int uid) { 237 if (isPlaybackActive(false)) { 238 flags &= ~AudioManager.FLAG_PLAY_SOUND; 239 } 240 if (direction > 1) { 241 direction = 1; 242 } else if (direction < -1) { 243 direction = -1; 244 } 245 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { 246 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); 247 mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, packageName, 248 uid); 249 } else { 250 if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) { 251 // Nothing to do, the volume cannot be changed 252 return; 253 } 254 mSessionCb.adjustVolume(direction); 255 256 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 257 mOptimisticVolume = volumeBefore + direction; 258 mOptimisticVolume = Math.max(0, Math.min(mOptimisticVolume, mMaxVolume)); 259 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 260 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 261 if (volumeBefore != mOptimisticVolume) { 262 pushVolumeUpdate(); 263 } 264 265 if (DEBUG) { 266 Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is " 267 + mMaxVolume); 268 } 269 } 270 } 271 272 public void setVolumeTo(int value, int flags, String packageName, int uid) { 273 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { 274 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); 275 mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid); 276 } else { 277 if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) { 278 // Nothing to do. The volume can't be set directly. 279 return; 280 } 281 value = Math.max(0, Math.min(value, mMaxVolume)); 282 mSessionCb.setVolumeTo(value); 283 284 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 285 mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume)); 286 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 287 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 288 if (volumeBefore != mOptimisticVolume) { 289 pushVolumeUpdate(); 290 } 291 292 if (DEBUG) { 293 Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is " 294 + mMaxVolume); 295 } 296 } 297 } 298 299 /** 300 * Check if this session has been set to active by the app. 301 * 302 * @return True if the session is active, false otherwise. 303 */ 304 public boolean isActive() { 305 return mIsActive && !mDestroyed; 306 } 307 308 /** 309 * Check if the session is currently performing playback. This will also 310 * return true if the session was recently paused. 311 * 312 * @param includeRecentlyActive True if playback that was recently paused 313 * should count, false if it shouldn't. 314 * @return True if the session is performing playback, false otherwise. 315 */ 316 public boolean isPlaybackActive(boolean includeRecentlyActive) { 317 int state = mPlaybackState == null ? 0 : mPlaybackState.getState(); 318 if (MediaSession.isActiveState(state)) { 319 return true; 320 } 321 if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) { 322 long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime; 323 if (inactiveTime < ACTIVE_BUFFER) { 324 return true; 325 } 326 } 327 return false; 328 } 329 330 /** 331 * Get the type of playback, either local or remote. 332 * 333 * @return The current type of playback. 334 */ 335 public int getPlaybackType() { 336 return mVolumeType; 337 } 338 339 /** 340 * Get the local audio stream being used. Only valid if playback type is 341 * local. 342 * 343 * @return The audio stream the session is using. 344 */ 345 public AudioAttributes getAudioAttributes() { 346 return mAudioAttrs; 347 } 348 349 /** 350 * Get the type of volume control. Only valid if playback type is remote. 351 * 352 * @return The volume control type being used. 353 */ 354 public int getVolumeControl() { 355 return mVolumeControlType; 356 } 357 358 /** 359 * Get the max volume that can be set. Only valid if playback type is 360 * remote. 361 * 362 * @return The max volume that can be set. 363 */ 364 public int getMaxVolume() { 365 return mMaxVolume; 366 } 367 368 /** 369 * Get the current volume for this session. Only valid if playback type is 370 * remote. 371 * 372 * @return The current volume of the remote playback. 373 */ 374 public int getCurrentVolume() { 375 return mCurrentVolume; 376 } 377 378 /** 379 * Get the volume we'd like it to be set to. This is only valid for a short 380 * while after a call to adjust or set volume. 381 * 382 * @return The current optimistic volume or -1. 383 */ 384 public int getOptimisticVolume() { 385 return mOptimisticVolume; 386 } 387 388 public boolean isTransportControlEnabled() { 389 return hasFlag(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 390 } 391 392 @Override 393 public void binderDied() { 394 mService.sessionDied(this); 395 } 396 397 /** 398 * Finish cleaning up this session, including disconnecting if connected and 399 * removing the death observer from the callback binder. 400 */ 401 public void onDestroy() { 402 synchronized (mLock) { 403 if (mDestroyed) { 404 return; 405 } 406 mDestroyed = true; 407 mHandler.post(MessageHandler.MSG_DESTROYED); 408 } 409 } 410 411 public ISessionCallback getCallback() { 412 return mSessionCb.mCb; 413 } 414 415 public void sendMediaButton(KeyEvent ke, int sequenceId, ResultReceiver cb) { 416 mSessionCb.sendMediaButton(ke, sequenceId, cb); 417 } 418 419 public void dump(PrintWriter pw, String prefix) { 420 pw.println(prefix + mTag + " " + this); 421 422 final String indent = prefix + " "; 423 pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid 424 + ", userId=" + mUserId); 425 pw.println(indent + "package=" + mPackageName); 426 pw.println(indent + "launchIntent=" + mLaunchIntent); 427 pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiver); 428 pw.println(indent + "active=" + mIsActive); 429 pw.println(indent + "flags=" + mFlags); 430 pw.println(indent + "rating type=" + mRatingType); 431 pw.println(indent + "controllers: " + mControllerCallbacks.size()); 432 pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString())); 433 pw.println(indent + "audioAttrs=" + mAudioAttrs); 434 pw.println(indent + "volumeType=" + mVolumeType + ", controlType=" + mVolumeControlType 435 + ", max=" + mMaxVolume + ", current=" + mCurrentVolume); 436 pw.println(indent + "metadata:" + getShortMetadataString()); 437 pw.println(indent + "queueTitle=" + mQueueTitle + ", size=" 438 + (mQueue == null ? 0 : mQueue.getList().size())); 439 } 440 441 @Override 442 public String toString() { 443 return mPackageName + "/" + mTag; 444 } 445 446 private String getShortMetadataString() { 447 int fields = mMetadata == null ? 0 : mMetadata.size(); 448 MediaMetadata.Description description = mMetadata == null ? null : mMetadata 449 .getDescription(); 450 return "size=" + fields + ", description=" + description; 451 } 452 453 private void pushPlaybackStateUpdate() { 454 synchronized (mLock) { 455 if (mDestroyed) { 456 return; 457 } 458 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 459 ISessionControllerCallback cb = mControllerCallbacks.get(i); 460 try { 461 cb.onPlaybackStateChanged(mPlaybackState); 462 } catch (DeadObjectException e) { 463 mControllerCallbacks.remove(i); 464 Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate.", e); 465 } catch (RemoteException e) { 466 Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e); 467 } 468 } 469 } 470 } 471 472 private void pushMetadataUpdate() { 473 synchronized (mLock) { 474 if (mDestroyed) { 475 return; 476 } 477 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 478 ISessionControllerCallback cb = mControllerCallbacks.get(i); 479 try { 480 cb.onMetadataChanged(mMetadata); 481 } catch (DeadObjectException e) { 482 Log.w(TAG, "Removing dead callback in pushMetadataUpdate. ", e); 483 mControllerCallbacks.remove(i); 484 } catch (RemoteException e) { 485 Log.w(TAG, "unexpected exception in pushMetadataUpdate. ", e); 486 } 487 } 488 } 489 } 490 491 private void pushQueueUpdate() { 492 synchronized (mLock) { 493 if (mDestroyed) { 494 return; 495 } 496 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 497 ISessionControllerCallback cb = mControllerCallbacks.get(i); 498 try { 499 cb.onQueueChanged(mQueue); 500 } catch (DeadObjectException e) { 501 mControllerCallbacks.remove(i); 502 Log.w(TAG, "Removed dead callback in pushQueueUpdate.", e); 503 } catch (RemoteException e) { 504 Log.w(TAG, "unexpected exception in pushQueueUpdate.", e); 505 } 506 } 507 } 508 } 509 510 private void pushQueueTitleUpdate() { 511 synchronized (mLock) { 512 if (mDestroyed) { 513 return; 514 } 515 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 516 ISessionControllerCallback cb = mControllerCallbacks.get(i); 517 try { 518 cb.onQueueTitleChanged(mQueueTitle); 519 } catch (DeadObjectException e) { 520 mControllerCallbacks.remove(i); 521 Log.w(TAG, "Removed dead callback in pushQueueTitleUpdate.", e); 522 } catch (RemoteException e) { 523 Log.w(TAG, "unexpected exception in pushQueueTitleUpdate.", e); 524 } 525 } 526 } 527 } 528 529 private void pushExtrasUpdate() { 530 synchronized (mLock) { 531 if (mDestroyed) { 532 return; 533 } 534 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 535 ISessionControllerCallback cb = mControllerCallbacks.get(i); 536 try { 537 cb.onExtrasChanged(mExtras); 538 } catch (DeadObjectException e) { 539 mControllerCallbacks.remove(i); 540 Log.w(TAG, "Removed dead callback in pushExtrasUpdate.", e); 541 } catch (RemoteException e) { 542 Log.w(TAG, "unexpected exception in pushExtrasUpdate.", e); 543 } 544 } 545 } 546 } 547 548 private void pushVolumeUpdate() { 549 synchronized (mLock) { 550 if (mDestroyed) { 551 return; 552 } 553 ParcelableVolumeInfo info = mController.getVolumeAttributes(); 554 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 555 ISessionControllerCallback cb = mControllerCallbacks.get(i); 556 try { 557 cb.onVolumeInfoChanged(info); 558 } catch (DeadObjectException e) { 559 Log.w(TAG, "Removing dead callback in pushVolumeUpdate. ", e); 560 } catch (RemoteException e) { 561 Log.w(TAG, "Unexpected exception in pushVolumeUpdate. ", e); 562 } 563 } 564 } 565 } 566 567 private void pushEvent(String event, Bundle data) { 568 synchronized (mLock) { 569 if (mDestroyed) { 570 return; 571 } 572 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 573 ISessionControllerCallback cb = mControllerCallbacks.get(i); 574 try { 575 cb.onEvent(event, data); 576 } catch (DeadObjectException e) { 577 Log.w(TAG, "Removing dead callback in pushEvent.", e); 578 mControllerCallbacks.remove(i); 579 } catch (RemoteException e) { 580 Log.w(TAG, "unexpected exception in pushEvent.", e); 581 } 582 } 583 } 584 } 585 586 private void pushSessionDestroyed() { 587 synchronized (mLock) { 588 // This is the only method that may be (and can only be) called 589 // after the session is destroyed. 590 if (!mDestroyed) { 591 return; 592 } 593 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 594 ISessionControllerCallback cb = mControllerCallbacks.get(i); 595 try { 596 cb.onSessionDestroyed(); 597 } catch (DeadObjectException e) { 598 Log.w(TAG, "Removing dead callback in pushEvent.", e); 599 mControllerCallbacks.remove(i); 600 } catch (RemoteException e) { 601 Log.w(TAG, "unexpected exception in pushEvent.", e); 602 } 603 } 604 // After notifying clear all listeners 605 mControllerCallbacks.clear(); 606 } 607 } 608 609 private PlaybackState getStateWithUpdatedPosition() { 610 PlaybackState state = mPlaybackState; 611 long duration = -1; 612 if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { 613 duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); 614 } 615 PlaybackState result = null; 616 if (state != null) { 617 if (state.getState() == PlaybackState.STATE_PLAYING 618 || state.getState() == PlaybackState.STATE_FAST_FORWARDING 619 || state.getState() == PlaybackState.STATE_REWINDING) { 620 long updateTime = state.getLastPositionUpdateTime(); 621 long currentTime = SystemClock.elapsedRealtime(); 622 if (updateTime > 0) { 623 long position = (long) (state.getPlaybackSpeed() 624 * (currentTime - updateTime)) + state.getPosition(); 625 if (duration >= 0 && position > duration) { 626 position = duration; 627 } else if (position < 0) { 628 position = 0; 629 } 630 PlaybackState.Builder builder = new PlaybackState.Builder(state); 631 builder.setState(state.getState(), position, state.getPlaybackSpeed(), 632 currentTime); 633 result = builder.build(); 634 } 635 } 636 } 637 return result == null ? state : result; 638 } 639 640 private int getControllerCbIndexForCb(ISessionControllerCallback cb) { 641 IBinder binder = cb.asBinder(); 642 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 643 if (binder.equals(mControllerCallbacks.get(i).asBinder())) { 644 return i; 645 } 646 } 647 return -1; 648 } 649 650 private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { 651 @Override 652 public void run() { 653 boolean needUpdate = (mOptimisticVolume != mCurrentVolume); 654 mOptimisticVolume = -1; 655 if (needUpdate) { 656 pushVolumeUpdate(); 657 } 658 } 659 }; 660 661 private final class SessionStub extends ISession.Stub { 662 @Override 663 public void destroy() { 664 mService.destroySession(MediaSessionRecord.this); 665 } 666 667 @Override 668 public void sendEvent(String event, Bundle data) { 669 mHandler.post(MessageHandler.MSG_SEND_EVENT, event, data); 670 } 671 672 @Override 673 public ISessionController getController() { 674 return mController; 675 } 676 677 @Override 678 public void setActive(boolean active) { 679 mIsActive = active; 680 mService.updateSession(MediaSessionRecord.this); 681 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 682 } 683 684 @Override 685 public void setFlags(int flags) { 686 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 687 int pid = getCallingPid(); 688 int uid = getCallingUid(); 689 mService.enforcePhoneStatePermission(pid, uid); 690 } 691 mFlags = flags; 692 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 693 } 694 695 @Override 696 public void setMediaRouter(IMediaRouter router) { 697 mMediaRouter = router; 698 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 699 } 700 701 @Override 702 public void setMediaButtonReceiver(PendingIntent pi) { 703 mMediaButtonReceiver = pi; 704 } 705 706 @Override 707 public void setLaunchPendingIntent(PendingIntent pi) { 708 mLaunchIntent = pi; 709 } 710 711 @Override 712 public void setMetadata(MediaMetadata metadata) { 713 mMetadata = metadata; 714 mHandler.post(MessageHandler.MSG_UPDATE_METADATA); 715 } 716 717 @Override 718 public void setPlaybackState(PlaybackState state) { 719 int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState(); 720 int newState = state == null ? 0 : state.getState(); 721 if (MediaSession.isActiveState(oldState) && newState == PlaybackState.STATE_PAUSED) { 722 mLastActiveTime = SystemClock.elapsedRealtime(); 723 } 724 mPlaybackState = state; 725 mService.onSessionPlaystateChange(MediaSessionRecord.this, oldState, newState); 726 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); 727 } 728 729 @Override 730 public void setQueue(ParceledListSlice queue) { 731 mQueue = queue; 732 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); 733 } 734 735 @Override 736 public void setQueueTitle(CharSequence title) { 737 mQueueTitle = title; 738 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE); 739 } 740 741 @Override 742 public void setExtras(Bundle extras) { 743 mExtras = extras; 744 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS); 745 } 746 747 @Override 748 public void setRatingType(int type) { 749 mRatingType = type; 750 } 751 752 @Override 753 public void setCurrentVolume(int volume) { 754 mCurrentVolume = volume; 755 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 756 } 757 758 @Override 759 public void setPlaybackToLocal(AudioAttributes attributes) { 760 boolean typeChanged; 761 synchronized (mLock) { 762 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE; 763 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; 764 if (attributes != null) { 765 mAudioAttrs = attributes; 766 } else { 767 Log.e(TAG, "Received null audio attributes, using existing attributes"); 768 } 769 } 770 if (typeChanged) { 771 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 772 } 773 } 774 775 @Override 776 public void setPlaybackToRemote(int control, int max) { 777 boolean typeChanged; 778 synchronized (mLock) { 779 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; 780 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE; 781 mVolumeControlType = control; 782 mMaxVolume = max; 783 } 784 if (typeChanged) { 785 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 786 } 787 } 788 } 789 790 class SessionCb { 791 private final ISessionCallback mCb; 792 793 public SessionCb(ISessionCallback cb) { 794 mCb = cb; 795 } 796 797 public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { 798 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 799 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 800 try { 801 mCb.onMediaButton(mediaButtonIntent, sequenceId, cb); 802 return true; 803 } catch (RemoteException e) { 804 Slog.e(TAG, "Remote failure in sendMediaRequest.", e); 805 } 806 return false; 807 } 808 809 public void sendCommand(String command, Bundle args, ResultReceiver cb) { 810 try { 811 mCb.onCommand(command, args, cb); 812 } catch (RemoteException e) { 813 Slog.e(TAG, "Remote failure in sendCommand.", e); 814 } 815 } 816 817 public void sendCustomAction(String action, Bundle args) { 818 try { 819 mCb.onCustomAction(action, args); 820 } catch (RemoteException e) { 821 Slog.e(TAG, "Remote failure in sendCustomAction.", e); 822 } 823 } 824 825 public void play() { 826 try { 827 mCb.onPlay(); 828 } catch (RemoteException e) { 829 Slog.e(TAG, "Remote failure in play.", e); 830 } 831 } 832 833 public void playUri(Uri uri, Bundle extras) { 834 try { 835 mCb.onPlayUri(uri, extras); 836 } catch (RemoteException e) { 837 Slog.e(TAG, "Remote failure in playUri.", e); 838 } 839 } 840 841 public void playFromSearch(String query, Bundle extras) { 842 try { 843 mCb.onPlayFromSearch(query, extras); 844 } catch (RemoteException e) { 845 Slog.e(TAG, "Remote failure in playFromSearch.", e); 846 } 847 } 848 849 public void skipToTrack(long id) { 850 try { 851 mCb.onSkipToTrack(id); 852 } catch (RemoteException e) { 853 Slog.e(TAG, "Remote failure in skipToTrack", e); 854 } 855 } 856 857 public void pause() { 858 try { 859 mCb.onPause(); 860 } catch (RemoteException e) { 861 Slog.e(TAG, "Remote failure in pause.", e); 862 } 863 } 864 865 public void stop() { 866 try { 867 mCb.onStop(); 868 } catch (RemoteException e) { 869 Slog.e(TAG, "Remote failure in stop.", e); 870 } 871 } 872 873 public void next() { 874 try { 875 mCb.onNext(); 876 } catch (RemoteException e) { 877 Slog.e(TAG, "Remote failure in next.", e); 878 } 879 } 880 881 public void previous() { 882 try { 883 mCb.onPrevious(); 884 } catch (RemoteException e) { 885 Slog.e(TAG, "Remote failure in previous.", e); 886 } 887 } 888 889 public void fastForward() { 890 try { 891 mCb.onFastForward(); 892 } catch (RemoteException e) { 893 Slog.e(TAG, "Remote failure in fastForward.", e); 894 } 895 } 896 897 public void rewind() { 898 try { 899 mCb.onRewind(); 900 } catch (RemoteException e) { 901 Slog.e(TAG, "Remote failure in rewind.", e); 902 } 903 } 904 905 public void seekTo(long pos) { 906 try { 907 mCb.onSeekTo(pos); 908 } catch (RemoteException e) { 909 Slog.e(TAG, "Remote failure in seekTo.", e); 910 } 911 } 912 913 public void rate(Rating rating) { 914 try { 915 mCb.onRate(rating); 916 } catch (RemoteException e) { 917 Slog.e(TAG, "Remote failure in rate.", e); 918 } 919 } 920 921 public void adjustVolume(int direction) { 922 try { 923 mCb.onAdjustVolume(direction); 924 } catch (RemoteException e) { 925 Slog.e(TAG, "Remote failure in adjustVolume.", e); 926 } 927 } 928 929 public void setVolumeTo(int value) { 930 try { 931 mCb.onSetVolumeTo(value); 932 } catch (RemoteException e) { 933 Slog.e(TAG, "Remote failure in setVolumeTo.", e); 934 } 935 } 936 } 937 938 class ControllerStub extends ISessionController.Stub { 939 @Override 940 public void sendCommand(String command, Bundle args, ResultReceiver cb) 941 throws RemoteException { 942 mSessionCb.sendCommand(command, args, cb); 943 } 944 945 @Override 946 public boolean sendMediaButton(KeyEvent mediaButtonIntent) { 947 return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null); 948 } 949 950 @Override 951 public void registerCallbackListener(ISessionControllerCallback cb) { 952 synchronized (mLock) { 953 // If this session is already destroyed tell the caller and 954 // don't add them. 955 if (mDestroyed) { 956 try { 957 cb.onSessionDestroyed(); 958 } catch (Exception e) { 959 // ignored 960 } 961 return; 962 } 963 if (getControllerCbIndexForCb(cb) < 0) { 964 mControllerCallbacks.add(cb); 965 if (DEBUG) { 966 Log.d(TAG, "registering controller callback " + cb); 967 } 968 } 969 } 970 } 971 972 @Override 973 public void unregisterCallbackListener(ISessionControllerCallback cb) 974 throws RemoteException { 975 synchronized (mLock) { 976 int index = getControllerCbIndexForCb(cb); 977 if (index != -1) { 978 mControllerCallbacks.remove(index); 979 } 980 if (DEBUG) { 981 Log.d(TAG, "unregistering callback " + cb + ". index=" + index); 982 } 983 } 984 } 985 986 @Override 987 public String getPackageName() { 988 return mPackageName; 989 } 990 991 @Override 992 public String getTag() { 993 return mTag; 994 } 995 996 @Override 997 public PendingIntent getLaunchPendingIntent() { 998 return mLaunchIntent; 999 } 1000 1001 @Override 1002 public long getFlags() { 1003 return mFlags; 1004 } 1005 1006 @Override 1007 public ParcelableVolumeInfo getVolumeAttributes() { 1008 synchronized (mLock) { 1009 int type; 1010 int max; 1011 int current; 1012 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 1013 type = mVolumeControlType; 1014 max = mMaxVolume; 1015 current = mOptimisticVolume != -1 ? mOptimisticVolume 1016 : mCurrentVolume; 1017 } else { 1018 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); 1019 type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; 1020 max = mAudioManager.getStreamMaxVolume(stream); 1021 current = mAudioManager.getStreamVolume(stream); 1022 } 1023 return new ParcelableVolumeInfo(mVolumeType, mAudioAttrs, type, max, current); 1024 } 1025 } 1026 1027 @Override 1028 public void adjustVolume(int direction, int flags, String packageName) { 1029 int uid = Binder.getCallingUid(); 1030 final long token = Binder.clearCallingIdentity(); 1031 try { 1032 MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid); 1033 } finally { 1034 Binder.restoreCallingIdentity(token); 1035 } 1036 } 1037 1038 @Override 1039 public void setVolumeTo(int value, int flags, String packageName) { 1040 int uid = Binder.getCallingUid(); 1041 final long token = Binder.clearCallingIdentity(); 1042 try { 1043 MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid); 1044 } finally { 1045 Binder.restoreCallingIdentity(token); 1046 } 1047 } 1048 1049 @Override 1050 public void play() throws RemoteException { 1051 mSessionCb.play(); 1052 } 1053 1054 @Override 1055 public void playUri(Uri uri, Bundle extras) throws RemoteException { 1056 mSessionCb.playUri(uri, extras); 1057 } 1058 1059 @Override 1060 public void playFromSearch(String query, Bundle extras) throws RemoteException { 1061 mSessionCb.playFromSearch(query, extras); 1062 } 1063 1064 @Override 1065 public void skipToTrack(long id) { 1066 mSessionCb.skipToTrack(id); 1067 } 1068 1069 1070 @Override 1071 public void pause() throws RemoteException { 1072 mSessionCb.pause(); 1073 } 1074 1075 @Override 1076 public void stop() throws RemoteException { 1077 mSessionCb.stop(); 1078 } 1079 1080 @Override 1081 public void next() throws RemoteException { 1082 mSessionCb.next(); 1083 } 1084 1085 @Override 1086 public void previous() throws RemoteException { 1087 mSessionCb.previous(); 1088 } 1089 1090 @Override 1091 public void fastForward() throws RemoteException { 1092 mSessionCb.fastForward(); 1093 } 1094 1095 @Override 1096 public void rewind() throws RemoteException { 1097 mSessionCb.rewind(); 1098 } 1099 1100 @Override 1101 public void seekTo(long pos) throws RemoteException { 1102 mSessionCb.seekTo(pos); 1103 } 1104 1105 @Override 1106 public void rate(Rating rating) throws RemoteException { 1107 mSessionCb.rate(rating); 1108 } 1109 1110 @Override 1111 public void sendCustomAction(String action, Bundle args) 1112 throws RemoteException { 1113 mSessionCb.sendCustomAction(action, args); 1114 } 1115 1116 1117 @Override 1118 public MediaMetadata getMetadata() { 1119 return mMetadata; 1120 } 1121 1122 @Override 1123 public PlaybackState getPlaybackState() { 1124 return getStateWithUpdatedPosition(); 1125 } 1126 1127 @Override 1128 public ParceledListSlice getQueue() { 1129 return mQueue; 1130 } 1131 1132 @Override 1133 public CharSequence getQueueTitle() { 1134 return mQueueTitle; 1135 } 1136 1137 @Override 1138 public Bundle getExtras() { 1139 return mExtras; 1140 } 1141 1142 @Override 1143 public int getRatingType() { 1144 return mRatingType; 1145 } 1146 1147 @Override 1148 public boolean isTransportControlEnabled() { 1149 return MediaSessionRecord.this.isTransportControlEnabled(); 1150 } 1151 1152 @Override 1153 public IMediaRouterDelegate createMediaRouterDelegate( 1154 IMediaRouterStateCallback callback) { 1155 // todo 1156 return null; 1157 } 1158 } 1159 1160 private class MessageHandler extends Handler { 1161 private static final int MSG_UPDATE_METADATA = 1; 1162 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 1163 private static final int MSG_UPDATE_QUEUE = 3; 1164 private static final int MSG_UPDATE_QUEUE_TITLE = 4; 1165 private static final int MSG_UPDATE_EXTRAS = 5; 1166 private static final int MSG_SEND_EVENT = 6; 1167 private static final int MSG_UPDATE_SESSION_STATE = 7; 1168 private static final int MSG_UPDATE_VOLUME = 8; 1169 private static final int MSG_DESTROYED = 9; 1170 1171 public MessageHandler(Looper looper) { 1172 super(looper); 1173 } 1174 @Override 1175 public void handleMessage(Message msg) { 1176 switch (msg.what) { 1177 case MSG_UPDATE_METADATA: 1178 pushMetadataUpdate(); 1179 break; 1180 case MSG_UPDATE_PLAYBACK_STATE: 1181 pushPlaybackStateUpdate(); 1182 break; 1183 case MSG_UPDATE_QUEUE: 1184 pushQueueUpdate(); 1185 break; 1186 case MSG_UPDATE_QUEUE_TITLE: 1187 pushQueueTitleUpdate(); 1188 break; 1189 case MSG_UPDATE_EXTRAS: 1190 pushExtrasUpdate(); 1191 break; 1192 case MSG_SEND_EVENT: 1193 pushEvent((String) msg.obj, msg.getData()); 1194 break; 1195 case MSG_UPDATE_SESSION_STATE: 1196 // TODO add session state 1197 break; 1198 case MSG_UPDATE_VOLUME: 1199 pushVolumeUpdate(); 1200 break; 1201 case MSG_DESTROYED: 1202 pushSessionDestroyed(); 1203 } 1204 } 1205 1206 public void post(int what) { 1207 post(what, null); 1208 } 1209 1210 public void post(int what, Object obj) { 1211 obtainMessage(what, obj).sendToTarget(); 1212 } 1213 1214 public void post(int what, Object obj, Bundle data) { 1215 Message msg = obtainMessage(what, obj); 1216 msg.setData(data); 1217 msg.sendToTarget(); 1218 } 1219 } 1220 1221} 1222