MediaSessionRecord.java revision 9db9bf7034d7dcdf596dc22d521b18975d0dd2b9
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.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.media.routing.IMediaRouter; 23import android.media.routing.IMediaRouterDelegate; 24import android.media.routing.IMediaRouterStateCallback; 25import android.media.session.ISessionController; 26import android.media.session.ISessionControllerCallback; 27import android.media.session.ISession; 28import android.media.session.ISessionCallback; 29import android.media.session.MediaController; 30import android.media.session.MediaSession; 31import android.media.session.MediaSessionInfo; 32import android.media.session.PlaybackState; 33import android.media.session.ParcelableVolumeInfo; 34import android.media.AudioAttributes; 35import android.media.AudioManager; 36import android.media.MediaMetadata; 37import android.media.Rating; 38import android.media.VolumeProvider; 39import android.os.Binder; 40import android.os.Bundle; 41import android.os.DeadObjectException; 42import android.os.Handler; 43import android.os.IBinder; 44import android.os.Looper; 45import android.os.Message; 46import android.os.RemoteException; 47import android.os.ResultReceiver; 48import android.os.SystemClock; 49import android.text.TextUtils; 50import android.util.Log; 51import android.util.Pair; 52import android.util.Slog; 53import android.view.KeyEvent; 54 55import java.io.PrintWriter; 56import java.util.ArrayList; 57import java.util.List; 58import java.util.UUID; 59 60/** 61 * This is the system implementation of a Session. Apps will interact with the 62 * MediaSession wrapper class instead. 63 */ 64public class MediaSessionRecord implements IBinder.DeathRecipient { 65 private static final String TAG = "MediaSessionRecord"; 66 private static final boolean DEBUG = false; 67 68 /** 69 * The length of time a session will still be considered active after 70 * pausing in ms. 71 */ 72 private static final int ACTIVE_BUFFER = 30000; 73 74 /** 75 * The amount of time we'll send an assumed volume after the last volume 76 * command before reverting to the last reported volume. 77 */ 78 private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000; 79 80 private final MessageHandler mHandler; 81 82 private final int mOwnerPid; 83 private final int mOwnerUid; 84 private final int mUserId; 85 private final MediaSessionInfo mSessionInfo; 86 private final String mTag; 87 private final ControllerStub mController; 88 private final SessionStub mSession; 89 private final SessionCb mSessionCb; 90 private final MediaSessionService mService; 91 92 private final Object mLock = new Object(); 93 private final ArrayList<ISessionControllerCallback> mControllerCallbacks = 94 new ArrayList<ISessionControllerCallback>(); 95 96 private long mFlags; 97 private IMediaRouter mMediaRouter; 98 private ComponentName mMediaButtonReceiver; 99 100 // TransportPerformer fields 101 102 private MediaMetadata mMetadata; 103 private PlaybackState mPlaybackState; 104 private int mRatingType; 105 private long mLastActiveTime; 106 // End TransportPerformer fields 107 108 // Volume handling fields 109 private AudioAttributes mAudioAttrs; 110 private AudioManager mAudioManager; 111 private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL; 112 private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; 113 private int mMaxVolume = 0; 114 private int mCurrentVolume = 0; 115 private int mOptimisticVolume = -1; 116 // End volume handling fields 117 118 private boolean mIsActive = false; 119 private boolean mDestroyed = false; 120 121 public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, 122 ISessionCallback cb, String tag, MediaSessionService service, Handler handler) { 123 mOwnerPid = ownerPid; 124 mOwnerUid = ownerUid; 125 mUserId = userId; 126 mSessionInfo = new MediaSessionInfo(UUID.randomUUID().toString(), ownerPackageName, 127 ownerPid); 128 mTag = tag; 129 mController = new ControllerStub(); 130 mSession = new SessionStub(); 131 mSessionCb = new SessionCb(cb); 132 mService = service; 133 mHandler = new MessageHandler(handler.getLooper()); 134 mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE); 135 mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(); 136 } 137 138 /** 139 * Get the binder for the {@link MediaSession}. 140 * 141 * @return The session binder apps talk to. 142 */ 143 public ISession getSessionBinder() { 144 return mSession; 145 } 146 147 /** 148 * Get the binder for the {@link MediaController}. 149 * 150 * @return The controller binder apps talk to. 151 */ 152 public ISessionController getControllerBinder() { 153 return mController; 154 } 155 156 /** 157 * Get the info for this session. 158 * 159 * @return Info that identifies this session. 160 */ 161 public MediaSessionInfo getSessionInfo() { 162 return mSessionInfo; 163 } 164 165 public ComponentName getMediaButtonReceiver() { 166 return mMediaButtonReceiver; 167 } 168 169 /** 170 * Get this session's flags. 171 * 172 * @return The flags for this session. 173 */ 174 public long getFlags() { 175 return mFlags; 176 } 177 178 /** 179 * Check if this session has the specified flag. 180 * 181 * @param flag The flag to check. 182 * @return True if this session has that flag set, false otherwise. 183 */ 184 public boolean hasFlag(int flag) { 185 return (mFlags & flag) != 0; 186 } 187 188 /** 189 * Get the user id this session was created for. 190 * 191 * @return The user id for this session. 192 */ 193 public int getUserId() { 194 return mUserId; 195 } 196 197 /** 198 * Check if this session has system priorty and should receive media buttons 199 * before any other sessions. 200 * 201 * @return True if this is a system priority session, false otherwise 202 */ 203 public boolean isSystemPriority() { 204 return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0; 205 } 206 207 /** 208 * Send a volume adjustment to the session owner. Direction must be one of 209 * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, 210 * {@link AudioManager#ADJUST_SAME}. 211 * 212 * @param direction The direction to adjust volume in. 213 */ 214 public void adjustVolume(int direction, int flags) { 215 if (isPlaybackActive(false)) { 216 flags &= ~AudioManager.FLAG_PLAY_SOUND; 217 } 218 if (direction > 1) { 219 direction = 1; 220 } else if (direction < -1) { 221 direction = -1; 222 } 223 if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) { 224 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); 225 mAudioManager.adjustStreamVolume(stream, direction, flags); 226 } else { 227 if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) { 228 // Nothing to do, the volume cannot be changed 229 return; 230 } 231 mSessionCb.adjustVolume(direction); 232 233 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 234 mOptimisticVolume = volumeBefore + direction; 235 mOptimisticVolume = Math.max(0, Math.min(mOptimisticVolume, mMaxVolume)); 236 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 237 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 238 if (volumeBefore != mOptimisticVolume) { 239 pushVolumeUpdate(); 240 } 241 242 if (DEBUG) { 243 Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is " 244 + mMaxVolume); 245 } 246 } 247 } 248 249 public void setVolumeTo(int value, int flags) { 250 if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) { 251 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); 252 mAudioManager.setStreamVolume(stream, value, flags); 253 } else { 254 if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) { 255 // Nothing to do. The volume can't be set directly. 256 return; 257 } 258 value = Math.max(0, Math.min(value, mMaxVolume)); 259 mSessionCb.setVolumeTo(value); 260 261 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 262 mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume)); 263 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 264 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 265 if (volumeBefore != mOptimisticVolume) { 266 pushVolumeUpdate(); 267 } 268 269 if (DEBUG) { 270 Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is " 271 + mMaxVolume); 272 } 273 } 274 } 275 276 /** 277 * Check if this session has been set to active by the app. 278 * 279 * @return True if the session is active, false otherwise. 280 */ 281 public boolean isActive() { 282 return mIsActive && !mDestroyed; 283 } 284 285 /** 286 * Check if the session is currently performing playback. This will also 287 * return true if the session was recently paused. 288 * 289 * @param includeRecentlyActive True if playback that was recently paused 290 * should count, false if it shouldn't. 291 * @return True if the session is performing playback, false otherwise. 292 */ 293 public boolean isPlaybackActive(boolean includeRecentlyActive) { 294 int state = mPlaybackState == null ? 0 : mPlaybackState.getState(); 295 if (MediaSession.isActiveState(state)) { 296 return true; 297 } 298 if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) { 299 long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime; 300 if (inactiveTime < ACTIVE_BUFFER) { 301 return true; 302 } 303 } 304 return false; 305 } 306 307 /** 308 * Get the type of playback, either local or remote. 309 * 310 * @return The current type of playback. 311 */ 312 public int getPlaybackType() { 313 return mVolumeType; 314 } 315 316 /** 317 * Get the local audio stream being used. Only valid if playback type is 318 * local. 319 * 320 * @return The audio stream the session is using. 321 */ 322 public AudioAttributes getAudioAttributes() { 323 return mAudioAttrs; 324 } 325 326 /** 327 * Get the type of volume control. Only valid if playback type is remote. 328 * 329 * @return The volume control type being used. 330 */ 331 public int getVolumeControl() { 332 return mVolumeControlType; 333 } 334 335 /** 336 * Get the max volume that can be set. Only valid if playback type is 337 * remote. 338 * 339 * @return The max volume that can be set. 340 */ 341 public int getMaxVolume() { 342 return mMaxVolume; 343 } 344 345 /** 346 * Get the current volume for this session. Only valid if playback type is 347 * remote. 348 * 349 * @return The current volume of the remote playback. 350 */ 351 public int getCurrentVolume() { 352 return mCurrentVolume; 353 } 354 355 /** 356 * Get the volume we'd like it to be set to. This is only valid for a short 357 * while after a call to adjust or set volume. 358 * 359 * @return The current optimistic volume or -1. 360 */ 361 public int getOptimisticVolume() { 362 return mOptimisticVolume; 363 } 364 365 public boolean isTransportControlEnabled() { 366 return hasFlag(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 367 } 368 369 @Override 370 public void binderDied() { 371 mService.sessionDied(this); 372 } 373 374 /** 375 * Finish cleaning up this session, including disconnecting if connected and 376 * removing the death observer from the callback binder. 377 */ 378 public void onDestroy() { 379 synchronized (mLock) { 380 if (mDestroyed) { 381 return; 382 } 383 mDestroyed = true; 384 } 385 } 386 387 public ISessionCallback getCallback() { 388 return mSessionCb.mCb; 389 } 390 391 public void sendMediaButton(KeyEvent ke, int sequenceId, ResultReceiver cb) { 392 mSessionCb.sendMediaButton(ke, sequenceId, cb); 393 } 394 395 public void dump(PrintWriter pw, String prefix) { 396 pw.println(prefix + mTag + " " + this); 397 398 final String indent = prefix + " "; 399 pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid 400 + ", userId=" + mUserId); 401 pw.println(indent + "info=" + mSessionInfo.toString()); 402 pw.println(indent + "active=" + mIsActive); 403 pw.println(indent + "flags=" + mFlags); 404 pw.println(indent + "rating type=" + mRatingType); 405 pw.println(indent + "controllers: " + mControllerCallbacks.size()); 406 pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString())); 407 pw.println(indent + "metadata:" + getShortMetadataString()); 408 } 409 410 private String getShortMetadataString() { 411 int fields = mMetadata == null ? 0 : mMetadata.size(); 412 String title = mMetadata == null ? null : mMetadata 413 .getString(MediaMetadata.METADATA_KEY_TITLE); 414 return "size=" + fields + ", title=" + title; 415 } 416 417 private void pushPlaybackStateUpdate() { 418 synchronized (mLock) { 419 if (mDestroyed) { 420 return; 421 } 422 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 423 ISessionControllerCallback cb = mControllerCallbacks.get(i); 424 try { 425 cb.onPlaybackStateChanged(mPlaybackState); 426 } catch (DeadObjectException e) { 427 mControllerCallbacks.remove(i); 428 Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate.", e); 429 } catch (RemoteException e) { 430 Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e); 431 } 432 } 433 } 434 } 435 436 private void pushMetadataUpdate() { 437 synchronized (mLock) { 438 if (mDestroyed) { 439 return; 440 } 441 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 442 ISessionControllerCallback cb = mControllerCallbacks.get(i); 443 try { 444 cb.onMetadataChanged(mMetadata); 445 } catch (DeadObjectException e) { 446 Log.w(TAG, "Removing dead callback in pushMetadataUpdate. ", e); 447 mControllerCallbacks.remove(i); 448 } catch (RemoteException e) { 449 Log.w(TAG, "unexpected exception in pushMetadataUpdate. ", e); 450 } 451 } 452 } 453 } 454 455 private void pushVolumeUpdate() { 456 synchronized (mLock) { 457 if (mDestroyed) { 458 return; 459 } 460 ParcelableVolumeInfo info = mController.getVolumeAttributes(); 461 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 462 ISessionControllerCallback cb = mControllerCallbacks.get(i); 463 try { 464 cb.onVolumeInfoChanged(info); 465 } catch (DeadObjectException e) { 466 Log.w(TAG, "Removing dead callback in pushVolumeUpdate. ", e); 467 } catch (RemoteException e) { 468 Log.w(TAG, "Unexpected exception in pushVolumeUpdate. ", e); 469 } 470 } 471 } 472 } 473 474 private void pushEvent(String event, Bundle data) { 475 synchronized (mLock) { 476 if (mDestroyed) { 477 return; 478 } 479 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 480 ISessionControllerCallback cb = mControllerCallbacks.get(i); 481 try { 482 cb.onEvent(event, data); 483 } catch (DeadObjectException e) { 484 Log.w(TAG, "Removing dead callback in pushEvent.", e); 485 mControllerCallbacks.remove(i); 486 } catch (RemoteException e) { 487 Log.w(TAG, "unexpected exception in pushEvent.", e); 488 } 489 } 490 } 491 } 492 493 private PlaybackState getStateWithUpdatedPosition() { 494 PlaybackState state = mPlaybackState; 495 long duration = -1; 496 if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { 497 duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); 498 } 499 PlaybackState result = null; 500 if (state != null) { 501 if (state.getState() == PlaybackState.STATE_PLAYING 502 || state.getState() == PlaybackState.STATE_FAST_FORWARDING 503 || state.getState() == PlaybackState.STATE_REWINDING) { 504 long updateTime = state.getLastPositionUpdateTime(); 505 long currentTime = SystemClock.elapsedRealtime(); 506 if (updateTime > 0) { 507 long position = (long) (state.getPlaybackSpeed() 508 * (currentTime - updateTime)) + state.getPosition(); 509 if (duration >= 0 && position > duration) { 510 position = duration; 511 } else if (position < 0) { 512 position = 0; 513 } 514 PlaybackState.Builder builder = new PlaybackState.Builder(state); 515 builder.setState(state.getState(), position, state.getPlaybackSpeed(), 516 currentTime); 517 result = builder.build(); 518 } 519 } 520 } 521 return result == null ? state : result; 522 } 523 524 private int getControllerCbIndexForCb(ISessionControllerCallback cb) { 525 IBinder binder = cb.asBinder(); 526 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 527 if (binder.equals(mControllerCallbacks.get(i).asBinder())) { 528 return i; 529 } 530 } 531 return -1; 532 } 533 534 private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { 535 @Override 536 public void run() { 537 boolean needUpdate = (mOptimisticVolume != mCurrentVolume); 538 mOptimisticVolume = -1; 539 if (needUpdate) { 540 pushVolumeUpdate(); 541 } 542 } 543 }; 544 545 private final class SessionStub extends ISession.Stub { 546 @Override 547 public void destroy() { 548 mService.destroySession(MediaSessionRecord.this); 549 } 550 551 @Override 552 public void sendEvent(String event, Bundle data) { 553 mHandler.post(MessageHandler.MSG_SEND_EVENT, event, data); 554 } 555 556 @Override 557 public ISessionController getController() { 558 return mController; 559 } 560 561 @Override 562 public void setActive(boolean active) { 563 mIsActive = active; 564 mService.updateSession(MediaSessionRecord.this); 565 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 566 } 567 568 @Override 569 public void setFlags(int flags) { 570 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 571 int pid = getCallingPid(); 572 int uid = getCallingUid(); 573 mService.enforcePhoneStatePermission(pid, uid); 574 } 575 mFlags = flags; 576 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 577 } 578 579 @Override 580 public void setMediaRouter(IMediaRouter router) { 581 mMediaRouter = router; 582 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 583 } 584 585 @Override 586 public void setMediaButtonReceiver(ComponentName mbr) { 587 mMediaButtonReceiver = mbr; 588 } 589 590 @Override 591 public void setMetadata(MediaMetadata metadata) { 592 mMetadata = metadata; 593 mHandler.post(MessageHandler.MSG_UPDATE_METADATA); 594 } 595 596 @Override 597 public void setPlaybackState(PlaybackState state) { 598 int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState(); 599 int newState = state == null ? 0 : state.getState(); 600 if (MediaSession.isActiveState(oldState) && newState == PlaybackState.STATE_PAUSED) { 601 mLastActiveTime = SystemClock.elapsedRealtime(); 602 } 603 mPlaybackState = state; 604 mService.onSessionPlaystateChange(MediaSessionRecord.this, oldState, newState); 605 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); 606 } 607 608 @Override 609 public void setRatingType(int type) { 610 mRatingType = type; 611 } 612 613 @Override 614 public void setCurrentVolume(int volume) { 615 mCurrentVolume = volume; 616 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 617 } 618 619 @Override 620 public void setPlaybackToLocal(AudioAttributes attributes) { 621 boolean typeChanged; 622 synchronized (mLock) { 623 typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE; 624 mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL; 625 if (attributes != null) { 626 mAudioAttrs = attributes; 627 } else { 628 Log.e(TAG, "Received null audio attributes, using existing attributes"); 629 } 630 } 631 if (typeChanged) { 632 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 633 } 634 } 635 636 @Override 637 public void setPlaybackToRemote(int control, int max) { 638 boolean typeChanged; 639 synchronized (mLock) { 640 typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL; 641 mVolumeType = MediaSession.PLAYBACK_TYPE_REMOTE; 642 mVolumeControlType = control; 643 mMaxVolume = max; 644 } 645 if (typeChanged) { 646 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 647 } 648 } 649 } 650 651 class SessionCb { 652 private final ISessionCallback mCb; 653 654 public SessionCb(ISessionCallback cb) { 655 mCb = cb; 656 } 657 658 public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { 659 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 660 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 661 try { 662 mCb.onMediaButton(mediaButtonIntent, sequenceId, cb); 663 return true; 664 } catch (RemoteException e) { 665 Slog.e(TAG, "Remote failure in sendMediaRequest.", e); 666 } 667 return false; 668 } 669 670 public void sendCommand(String command, Bundle extras, ResultReceiver cb) { 671 try { 672 mCb.onCommand(command, extras, cb); 673 } catch (RemoteException e) { 674 Slog.e(TAG, "Remote failure in sendCommand.", e); 675 } 676 } 677 678 public void play() { 679 try { 680 mCb.onPlay(); 681 } catch (RemoteException e) { 682 Slog.e(TAG, "Remote failure in play.", e); 683 } 684 } 685 686 public void pause() { 687 try { 688 mCb.onPause(); 689 } catch (RemoteException e) { 690 Slog.e(TAG, "Remote failure in pause.", e); 691 } 692 } 693 694 public void stop() { 695 try { 696 mCb.onStop(); 697 } catch (RemoteException e) { 698 Slog.e(TAG, "Remote failure in stop.", e); 699 } 700 } 701 702 public void next() { 703 try { 704 mCb.onNext(); 705 } catch (RemoteException e) { 706 Slog.e(TAG, "Remote failure in next.", e); 707 } 708 } 709 710 public void previous() { 711 try { 712 mCb.onPrevious(); 713 } catch (RemoteException e) { 714 Slog.e(TAG, "Remote failure in previous.", e); 715 } 716 } 717 718 public void fastForward() { 719 try { 720 mCb.onFastForward(); 721 } catch (RemoteException e) { 722 Slog.e(TAG, "Remote failure in fastForward.", e); 723 } 724 } 725 726 public void rewind() { 727 try { 728 mCb.onRewind(); 729 } catch (RemoteException e) { 730 Slog.e(TAG, "Remote failure in rewind.", e); 731 } 732 } 733 734 public void seekTo(long pos) { 735 try { 736 mCb.onSeekTo(pos); 737 } catch (RemoteException e) { 738 Slog.e(TAG, "Remote failure in seekTo.", e); 739 } 740 } 741 742 public void rate(Rating rating) { 743 try { 744 mCb.onRate(rating); 745 } catch (RemoteException e) { 746 Slog.e(TAG, "Remote failure in rate.", e); 747 } 748 } 749 750 public void adjustVolume(int direction) { 751 try { 752 mCb.onAdjustVolume(direction); 753 } catch (RemoteException e) { 754 Slog.e(TAG, "Remote failure in adjustVolume.", e); 755 } 756 } 757 758 public void setVolumeTo(int value) { 759 try { 760 mCb.onSetVolumeTo(value); 761 } catch (RemoteException e) { 762 Slog.e(TAG, "Remote failure in setVolumeTo.", e); 763 } 764 } 765 } 766 767 class ControllerStub extends ISessionController.Stub { 768 @Override 769 public void sendCommand(String command, Bundle extras, ResultReceiver cb) 770 throws RemoteException { 771 mSessionCb.sendCommand(command, extras, cb); 772 } 773 774 @Override 775 public boolean sendMediaButton(KeyEvent mediaButtonIntent) { 776 return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null); 777 } 778 779 @Override 780 public void registerCallbackListener(ISessionControllerCallback cb) { 781 synchronized (mLock) { 782 if (getControllerCbIndexForCb(cb) < 0) { 783 mControllerCallbacks.add(cb); 784 if (DEBUG) { 785 Log.d(TAG, "registering controller callback " + cb); 786 } 787 } 788 } 789 } 790 791 @Override 792 public void unregisterCallbackListener(ISessionControllerCallback cb) 793 throws RemoteException { 794 synchronized (mLock) { 795 int index = getControllerCbIndexForCb(cb); 796 if (index != -1) { 797 mControllerCallbacks.remove(index); 798 } 799 if (DEBUG) { 800 Log.d(TAG, "unregistering callback " + cb + ". index=" + index); 801 } 802 } 803 } 804 805 @Override 806 public MediaSessionInfo getSessionInfo() { 807 return mSessionInfo; 808 } 809 810 @Override 811 public long getFlags() { 812 return mFlags; 813 } 814 815 @Override 816 public ParcelableVolumeInfo getVolumeAttributes() { 817 synchronized (mLock) { 818 int type; 819 int max; 820 int current; 821 if (mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE) { 822 type = mVolumeControlType; 823 max = mMaxVolume; 824 current = mOptimisticVolume != -1 ? mOptimisticVolume 825 : mCurrentVolume; 826 } else { 827 int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); 828 type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; 829 max = mAudioManager.getStreamMaxVolume(stream); 830 current = mAudioManager.getStreamVolume(stream); 831 } 832 return new ParcelableVolumeInfo(mVolumeType, mAudioAttrs, type, max, current); 833 } 834 } 835 836 @Override 837 public void adjustVolume(int direction, int flags) { 838 final long token = Binder.clearCallingIdentity(); 839 try { 840 MediaSessionRecord.this.adjustVolume(direction, flags); 841 } finally { 842 Binder.restoreCallingIdentity(token); 843 } 844 } 845 846 @Override 847 public void setVolumeTo(int value, int flags) { 848 final long token = Binder.clearCallingIdentity(); 849 try { 850 MediaSessionRecord.this.setVolumeTo(value, flags); 851 } finally { 852 Binder.restoreCallingIdentity(token); 853 } 854 } 855 856 @Override 857 public void play() throws RemoteException { 858 mSessionCb.play(); 859 } 860 861 @Override 862 public void pause() throws RemoteException { 863 mSessionCb.pause(); 864 } 865 866 @Override 867 public void stop() throws RemoteException { 868 mSessionCb.stop(); 869 } 870 871 @Override 872 public void next() throws RemoteException { 873 mSessionCb.next(); 874 } 875 876 @Override 877 public void previous() throws RemoteException { 878 mSessionCb.previous(); 879 } 880 881 @Override 882 public void fastForward() throws RemoteException { 883 mSessionCb.fastForward(); 884 } 885 886 @Override 887 public void rewind() throws RemoteException { 888 mSessionCb.rewind(); 889 } 890 891 @Override 892 public void seekTo(long pos) throws RemoteException { 893 mSessionCb.seekTo(pos); 894 } 895 896 @Override 897 public void rate(Rating rating) throws RemoteException { 898 mSessionCb.rate(rating); 899 } 900 901 902 @Override 903 public MediaMetadata getMetadata() { 904 return mMetadata; 905 } 906 907 @Override 908 public PlaybackState getPlaybackState() { 909 return getStateWithUpdatedPosition(); 910 } 911 912 @Override 913 public int getRatingType() { 914 return mRatingType; 915 } 916 917 @Override 918 public boolean isTransportControlEnabled() { 919 return MediaSessionRecord.this.isTransportControlEnabled(); 920 } 921 922 @Override 923 public IMediaRouterDelegate createMediaRouterDelegate( 924 IMediaRouterStateCallback callback) { 925 // todo 926 return null; 927 } 928 } 929 930 private class MessageHandler extends Handler { 931 private static final int MSG_UPDATE_METADATA = 1; 932 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 933 private static final int MSG_SEND_EVENT = 3; 934 private static final int MSG_UPDATE_SESSION_STATE = 4; 935 private static final int MSG_UPDATE_VOLUME = 5; 936 937 public MessageHandler(Looper looper) { 938 super(looper); 939 } 940 @Override 941 public void handleMessage(Message msg) { 942 switch (msg.what) { 943 case MSG_UPDATE_METADATA: 944 pushMetadataUpdate(); 945 break; 946 case MSG_UPDATE_PLAYBACK_STATE: 947 pushPlaybackStateUpdate(); 948 break; 949 case MSG_SEND_EVENT: 950 pushEvent((String) msg.obj, msg.getData()); 951 break; 952 case MSG_UPDATE_SESSION_STATE: 953 // TODO add session state 954 break; 955 case MSG_UPDATE_VOLUME: 956 pushVolumeUpdate(); 957 break; 958 } 959 } 960 961 public void post(int what) { 962 post(what, null); 963 } 964 965 public void post(int what, Object obj) { 966 obtainMessage(what, obj).sendToTarget(); 967 } 968 969 public void post(int what, Object obj, Bundle data) { 970 Message msg = obtainMessage(what, obj); 971 msg.setData(data); 972 msg.sendToTarget(); 973 } 974 } 975 976} 977