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