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