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