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 if (useSuggested) { 466 if (AudioSystem.isStreamActive(stream, 0)) { 467 mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction, 468 flags, packageName, uid); 469 } else { 470 mAudioManagerInternal.adjustSuggestedStreamVolumeForUid( 471 AudioManager.USE_DEFAULT_STREAM_TYPE, direction, 472 flags | previousFlagPlaySound, packageName, uid); 473 } 474 } else { 475 mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, 476 packageName, uid); 477 } 478 } 479 }); 480 } 481 482 private String getShortMetadataString() { 483 int fields = mMetadata == null ? 0 : mMetadata.size(); 484 MediaDescription description = mMetadata == null ? null : mMetadata 485 .getDescription(); 486 return "size=" + fields + ", description=" + description; 487 } 488 489 private void logCallbackException( 490 String msg, ISessionControllerCallbackHolder holder, Exception e) { 491 Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName 492 + ", exception=" + e); 493 } 494 495 private void pushPlaybackStateUpdate() { 496 synchronized (mLock) { 497 if (mDestroyed) { 498 return; 499 } 500 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 501 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 502 try { 503 holder.mCallback.onPlaybackStateChanged(mPlaybackState); 504 } catch (DeadObjectException e) { 505 mControllerCallbackHolders.remove(i); 506 logCallbackException("Removed dead callback in pushPlaybackStateUpdate", 507 holder, e); 508 } catch (RemoteException e) { 509 logCallbackException("unexpected exception in pushPlaybackStateUpdate", 510 holder, e); 511 } 512 } 513 } 514 } 515 516 private void pushMetadataUpdate() { 517 synchronized (mLock) { 518 if (mDestroyed) { 519 return; 520 } 521 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 522 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 523 try { 524 holder.mCallback.onMetadataChanged(mMetadata); 525 } catch (DeadObjectException e) { 526 logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e); 527 mControllerCallbackHolders.remove(i); 528 } catch (RemoteException e) { 529 logCallbackException("unexpected exception in pushMetadataUpdate", holder, e); 530 } 531 } 532 } 533 } 534 535 private void pushQueueUpdate() { 536 synchronized (mLock) { 537 if (mDestroyed) { 538 return; 539 } 540 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 541 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 542 try { 543 holder.mCallback.onQueueChanged(mQueue); 544 } catch (DeadObjectException e) { 545 mControllerCallbackHolders.remove(i); 546 logCallbackException("Removed dead callback in pushQueueUpdate", holder, e); 547 } catch (RemoteException e) { 548 logCallbackException("unexpected exception in pushQueueUpdate", holder, e); 549 } 550 } 551 } 552 } 553 554 private void pushQueueTitleUpdate() { 555 synchronized (mLock) { 556 if (mDestroyed) { 557 return; 558 } 559 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 560 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 561 try { 562 holder.mCallback.onQueueTitleChanged(mQueueTitle); 563 } catch (DeadObjectException e) { 564 mControllerCallbackHolders.remove(i); 565 logCallbackException("Removed dead callback in pushQueueTitleUpdate", 566 holder, e); 567 } catch (RemoteException e) { 568 logCallbackException("unexpected exception in pushQueueTitleUpdate", 569 holder, e); 570 } 571 } 572 } 573 } 574 575 private void pushExtrasUpdate() { 576 synchronized (mLock) { 577 if (mDestroyed) { 578 return; 579 } 580 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 581 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 582 try { 583 holder.mCallback.onExtrasChanged(mExtras); 584 } catch (DeadObjectException e) { 585 mControllerCallbackHolders.remove(i); 586 logCallbackException("Removed dead callback in pushExtrasUpdate", holder, e); 587 } catch (RemoteException e) { 588 logCallbackException("unexpected exception in pushExtrasUpdate", holder, e); 589 } 590 } 591 } 592 } 593 594 private void pushVolumeUpdate() { 595 synchronized (mLock) { 596 if (mDestroyed) { 597 return; 598 } 599 ParcelableVolumeInfo info = mController.getVolumeAttributes(); 600 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 601 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 602 try { 603 holder.mCallback.onVolumeInfoChanged(info); 604 } catch (DeadObjectException e) { 605 logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e); 606 } catch (RemoteException e) { 607 logCallbackException("Unexpected exception in pushVolumeUpdate", holder, e); 608 } 609 } 610 } 611 } 612 613 private void pushEvent(String event, Bundle data) { 614 synchronized (mLock) { 615 if (mDestroyed) { 616 return; 617 } 618 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 619 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 620 try { 621 holder.mCallback.onEvent(event, data); 622 } catch (DeadObjectException e) { 623 logCallbackException("Removing dead callback in pushEvent", holder, e); 624 mControllerCallbackHolders.remove(i); 625 } catch (RemoteException e) { 626 logCallbackException("unexpected exception in pushEvent", holder, e); 627 } 628 } 629 } 630 } 631 632 private void pushSessionDestroyed() { 633 synchronized (mLock) { 634 // This is the only method that may be (and can only be) called 635 // after the session is destroyed. 636 if (!mDestroyed) { 637 return; 638 } 639 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 640 ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); 641 try { 642 holder.mCallback.onSessionDestroyed(); 643 } catch (DeadObjectException e) { 644 logCallbackException("Removing dead callback in pushEvent", holder, e); 645 mControllerCallbackHolders.remove(i); 646 } catch (RemoteException e) { 647 logCallbackException("unexpected exception in pushEvent", holder, e); 648 } 649 } 650 // After notifying clear all listeners 651 mControllerCallbackHolders.clear(); 652 } 653 } 654 655 private PlaybackState getStateWithUpdatedPosition() { 656 PlaybackState state; 657 long duration = -1; 658 synchronized (mLock) { 659 state = mPlaybackState; 660 if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { 661 duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); 662 } 663 } 664 PlaybackState result = null; 665 if (state != null) { 666 if (state.getState() == PlaybackState.STATE_PLAYING 667 || state.getState() == PlaybackState.STATE_FAST_FORWARDING 668 || state.getState() == PlaybackState.STATE_REWINDING) { 669 long updateTime = state.getLastPositionUpdateTime(); 670 long currentTime = SystemClock.elapsedRealtime(); 671 if (updateTime > 0) { 672 long position = (long) (state.getPlaybackSpeed() 673 * (currentTime - updateTime)) + state.getPosition(); 674 if (duration >= 0 && position > duration) { 675 position = duration; 676 } else if (position < 0) { 677 position = 0; 678 } 679 PlaybackState.Builder builder = new PlaybackState.Builder(state); 680 builder.setState(state.getState(), position, state.getPlaybackSpeed(), 681 currentTime); 682 result = builder.build(); 683 } 684 } 685 } 686 return result == null ? state : result; 687 } 688 689 private int getControllerHolderIndexForCb(ISessionControllerCallback cb) { 690 IBinder binder = cb.asBinder(); 691 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 692 if (binder.equals(mControllerCallbackHolders.get(i).mCallback.asBinder())) { 693 return i; 694 } 695 } 696 return -1; 697 } 698 699 private void updateCallingPackage() { 700 updateCallingPackage(UID_NOT_SET, null); 701 } 702 703 private void updateCallingPackage(int uid, String packageName) { 704 if (uid == UID_NOT_SET) { 705 uid = Binder.getCallingUid(); 706 } 707 synchronized (mLock) { 708 if (mCallingUid == UID_NOT_SET || mCallingUid != uid) { 709 mCallingUid = uid; 710 mCallingPackage = packageName != null ? packageName : getPackageName(uid); 711 } 712 } 713 } 714 715 private String getPackageName(int uid) { 716 Context context = mService.getContext(); 717 if (context == null) { 718 return null; 719 } 720 String[] packages = context.getPackageManager().getPackagesForUid(uid); 721 if (packages != null && packages.length > 0) { 722 return packages[0]; 723 } 724 return null; 725 } 726 727 private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { 728 @Override 729 public void run() { 730 boolean needUpdate = (mOptimisticVolume != mCurrentVolume); 731 mOptimisticVolume = -1; 732 if (needUpdate) { 733 pushVolumeUpdate(); 734 } 735 } 736 }; 737 738 private final class SessionStub extends ISession.Stub { 739 @Override 740 public void destroy() { 741 final long token = Binder.clearCallingIdentity(); 742 try { 743 mService.destroySession(MediaSessionRecord.this); 744 } finally { 745 Binder.restoreCallingIdentity(token); 746 } 747 } 748 749 @Override 750 public void sendEvent(String event, Bundle data) { 751 mHandler.post(MessageHandler.MSG_SEND_EVENT, event, 752 data == null ? null : new Bundle(data)); 753 } 754 755 @Override 756 public ISessionController getController() { 757 return mController; 758 } 759 760 @Override 761 public void setActive(boolean active) { 762 mIsActive = active; 763 final long token = Binder.clearCallingIdentity(); 764 try { 765 mService.updateSession(MediaSessionRecord.this); 766 } finally { 767 Binder.restoreCallingIdentity(token); 768 } 769 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 770 } 771 772 @Override 773 public void setFlags(int flags) { 774 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 775 int pid = getCallingPid(); 776 int uid = getCallingUid(); 777 mService.enforcePhoneStatePermission(pid, uid); 778 } 779 mFlags = flags; 780 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 781 } 782 783 @Override 784 public void setMediaButtonReceiver(PendingIntent pi) { 785 mMediaButtonReceiver = pi; 786 final long token = Binder.clearCallingIdentity(); 787 try { 788 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); 789 } finally { 790 Binder.restoreCallingIdentity(token); 791 } 792 } 793 794 @Override 795 public void setLaunchPendingIntent(PendingIntent pi) { 796 mLaunchIntent = pi; 797 } 798 799 @Override 800 public void setMetadata(MediaMetadata metadata) { 801 synchronized (mLock) { 802 MediaMetadata temp = metadata == null ? null : new MediaMetadata.Builder(metadata) 803 .build(); 804 // This is to guarantee that the underlying bundle is unparceled 805 // before we set it to prevent concurrent reads from throwing an 806 // exception 807 if (temp != null) { 808 temp.size(); 809 } 810 mMetadata = temp; 811 } 812 mHandler.post(MessageHandler.MSG_UPDATE_METADATA); 813 } 814 815 @Override 816 public void setPlaybackState(PlaybackState state) { 817 int oldState = mPlaybackState == null 818 ? PlaybackState.STATE_NONE : mPlaybackState.getState(); 819 int newState = state == null 820 ? PlaybackState.STATE_NONE : state.getState(); 821 synchronized (mLock) { 822 mPlaybackState = state; 823 } 824 final long token = Binder.clearCallingIdentity(); 825 try { 826 mService.onSessionPlaystateChanged(MediaSessionRecord.this, oldState, newState); 827 } finally { 828 Binder.restoreCallingIdentity(token); 829 } 830 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); 831 } 832 833 @Override 834 public void setQueue(ParceledListSlice queue) { 835 synchronized (mLock) { 836 mQueue = queue; 837 } 838 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); 839 } 840 841 @Override 842 public void setQueueTitle(CharSequence title) { 843 mQueueTitle = title; 844 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE); 845 } 846 847 @Override 848 public void setExtras(Bundle extras) { 849 synchronized (mLock) { 850 mExtras = extras == null ? null : new Bundle(extras); 851 } 852 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS); 853 } 854 855 @Override 856 public void setRatingType(int type) { 857 mRatingType = type; 858 } 859 860 @Override 861 public void setCurrentVolume(int volume) { 862 mCurrentVolume = volume; 863 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 864 } 865 866 @Override 867 public void setPlaybackToLocal(AudioAttributes attributes) { 868 boolean typeChanged; 869 synchronized (mLock) { 870 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE; 871 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; 872 if (attributes != null) { 873 mAudioAttrs = attributes; 874 } else { 875 Log.e(TAG, "Received null audio attributes, using existing attributes"); 876 } 877 } 878 if (typeChanged) { 879 final long token = Binder.clearCallingIdentity(); 880 try { 881 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 882 } finally { 883 Binder.restoreCallingIdentity(token); 884 } 885 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 886 } 887 } 888 889 @Override 890 public void setPlaybackToRemote(int control, int max) { 891 boolean typeChanged; 892 synchronized (mLock) { 893 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; 894 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE; 895 mVolumeControlType = control; 896 mMaxVolume = max; 897 } 898 if (typeChanged) { 899 final long token = Binder.clearCallingIdentity(); 900 try { 901 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 902 } finally { 903 Binder.restoreCallingIdentity(token); 904 } 905 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 906 } 907 } 908 909 @Override 910 public String getCallingPackage() { 911 return mCallingPackage; 912 } 913 } 914 915 class SessionCb { 916 private final ISessionCallback mCb; 917 918 public SessionCb(ISessionCallback cb) { 919 mCb = cb; 920 } 921 922 public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { 923 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 924 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 925 try { 926 mCb.onMediaButton(mediaButtonIntent, sequenceId, cb); 927 return true; 928 } catch (RemoteException e) { 929 Slog.e(TAG, "Remote failure in sendMediaRequest.", e); 930 } 931 return false; 932 } 933 934 public void sendCommand(String command, Bundle args, ResultReceiver cb) { 935 try { 936 mCb.onCommand(command, args, cb); 937 } catch (RemoteException e) { 938 Slog.e(TAG, "Remote failure in sendCommand.", e); 939 } 940 } 941 942 public void sendCustomAction(String action, Bundle args) { 943 try { 944 mCb.onCustomAction(action, args); 945 } catch (RemoteException e) { 946 Slog.e(TAG, "Remote failure in sendCustomAction.", e); 947 } 948 } 949 950 public void prepare() { 951 try { 952 mCb.onPrepare(); 953 } catch (RemoteException e) { 954 Slog.e(TAG, "Remote failure in prepare.", e); 955 } 956 } 957 958 public void prepareFromMediaId(String mediaId, Bundle extras) { 959 try { 960 mCb.onPrepareFromMediaId(mediaId, extras); 961 } catch (RemoteException e) { 962 Slog.e(TAG, "Remote failure in prepareFromMediaId.", e); 963 } 964 } 965 966 public void prepareFromSearch(String query, Bundle extras) { 967 try { 968 mCb.onPrepareFromSearch(query, extras); 969 } catch (RemoteException e) { 970 Slog.e(TAG, "Remote failure in prepareFromSearch.", e); 971 } 972 } 973 974 public void prepareFromUri(Uri uri, Bundle extras) { 975 try { 976 mCb.onPrepareFromUri(uri, extras); 977 } catch (RemoteException e) { 978 Slog.e(TAG, "Remote failure in prepareFromUri.", e); 979 } 980 } 981 982 public void play() { 983 try { 984 mCb.onPlay(); 985 } catch (RemoteException e) { 986 Slog.e(TAG, "Remote failure in play.", e); 987 } 988 } 989 990 public void playFromMediaId(String mediaId, Bundle extras) { 991 try { 992 mCb.onPlayFromMediaId(mediaId, extras); 993 } catch (RemoteException e) { 994 Slog.e(TAG, "Remote failure in playFromMediaId.", e); 995 } 996 } 997 998 public void playFromSearch(String query, Bundle extras) { 999 try { 1000 mCb.onPlayFromSearch(query, extras); 1001 } catch (RemoteException e) { 1002 Slog.e(TAG, "Remote failure in playFromSearch.", e); 1003 } 1004 } 1005 1006 public void playFromUri(Uri uri, Bundle extras) { 1007 try { 1008 mCb.onPlayFromUri(uri, extras); 1009 } catch (RemoteException e) { 1010 Slog.e(TAG, "Remote failure in playFromUri.", e); 1011 } 1012 } 1013 1014 public void skipToTrack(long id) { 1015 try { 1016 mCb.onSkipToTrack(id); 1017 } catch (RemoteException e) { 1018 Slog.e(TAG, "Remote failure in skipToTrack", e); 1019 } 1020 } 1021 1022 public void pause() { 1023 try { 1024 mCb.onPause(); 1025 } catch (RemoteException e) { 1026 Slog.e(TAG, "Remote failure in pause.", e); 1027 } 1028 } 1029 1030 public void stop() { 1031 try { 1032 mCb.onStop(); 1033 } catch (RemoteException e) { 1034 Slog.e(TAG, "Remote failure in stop.", e); 1035 } 1036 } 1037 1038 public void next() { 1039 try { 1040 mCb.onNext(); 1041 } catch (RemoteException e) { 1042 Slog.e(TAG, "Remote failure in next.", e); 1043 } 1044 } 1045 1046 public void previous() { 1047 try { 1048 mCb.onPrevious(); 1049 } catch (RemoteException e) { 1050 Slog.e(TAG, "Remote failure in previous.", e); 1051 } 1052 } 1053 1054 public void fastForward() { 1055 try { 1056 mCb.onFastForward(); 1057 } catch (RemoteException e) { 1058 Slog.e(TAG, "Remote failure in fastForward.", e); 1059 } 1060 } 1061 1062 public void rewind() { 1063 try { 1064 mCb.onRewind(); 1065 } catch (RemoteException e) { 1066 Slog.e(TAG, "Remote failure in rewind.", e); 1067 } 1068 } 1069 1070 public void seekTo(long pos) { 1071 try { 1072 mCb.onSeekTo(pos); 1073 } catch (RemoteException e) { 1074 Slog.e(TAG, "Remote failure in seekTo.", e); 1075 } 1076 } 1077 1078 public void rate(Rating rating) { 1079 try { 1080 mCb.onRate(rating); 1081 } catch (RemoteException e) { 1082 Slog.e(TAG, "Remote failure in rate.", e); 1083 } 1084 } 1085 1086 public void adjustVolume(int direction) { 1087 try { 1088 mCb.onAdjustVolume(direction); 1089 } catch (RemoteException e) { 1090 Slog.e(TAG, "Remote failure in adjustVolume.", e); 1091 } 1092 } 1093 1094 public void setVolumeTo(int value) { 1095 try { 1096 mCb.onSetVolumeTo(value); 1097 } catch (RemoteException e) { 1098 Slog.e(TAG, "Remote failure in setVolumeTo.", e); 1099 } 1100 } 1101 } 1102 1103 class ControllerStub extends ISessionController.Stub { 1104 @Override 1105 public void sendCommand(String command, Bundle args, ResultReceiver cb) 1106 throws RemoteException { 1107 updateCallingPackage(); 1108 mSessionCb.sendCommand(command, args, cb); 1109 } 1110 1111 @Override 1112 public boolean sendMediaButton(KeyEvent mediaButtonIntent) { 1113 updateCallingPackage(); 1114 return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null); 1115 } 1116 1117 @Override 1118 public void registerCallbackListener(ISessionControllerCallback cb) { 1119 synchronized (mLock) { 1120 // If this session is already destroyed tell the caller and 1121 // don't add them. 1122 if (mDestroyed) { 1123 try { 1124 cb.onSessionDestroyed(); 1125 } catch (Exception e) { 1126 // ignored 1127 } 1128 return; 1129 } 1130 if (getControllerHolderIndexForCb(cb) < 0) { 1131 mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb, 1132 Binder.getCallingUid())); 1133 if (DEBUG) { 1134 Log.d(TAG, "registering controller callback " + cb); 1135 } 1136 } 1137 } 1138 } 1139 1140 @Override 1141 public void unregisterCallbackListener(ISessionControllerCallback cb) 1142 throws RemoteException { 1143 synchronized (mLock) { 1144 int index = getControllerHolderIndexForCb(cb); 1145 if (index != -1) { 1146 mControllerCallbackHolders.remove(index); 1147 } 1148 if (DEBUG) { 1149 Log.d(TAG, "unregistering callback " + cb + ". index=" + index); 1150 } 1151 } 1152 } 1153 1154 @Override 1155 public String getPackageName() { 1156 return mPackageName; 1157 } 1158 1159 @Override 1160 public String getTag() { 1161 return mTag; 1162 } 1163 1164 @Override 1165 public PendingIntent getLaunchPendingIntent() { 1166 return mLaunchIntent; 1167 } 1168 1169 @Override 1170 public long getFlags() { 1171 return mFlags; 1172 } 1173 1174 @Override 1175 public ParcelableVolumeInfo getVolumeAttributes() { 1176 int volumeType; 1177 AudioAttributes attributes; 1178 synchronized (mLock) { 1179 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 1180 int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume; 1181 return new ParcelableVolumeInfo( 1182 mVolumeType, mAudioAttrs, mVolumeControlType, mMaxVolume, current); 1183 } 1184 volumeType = mVolumeType; 1185 attributes = mAudioAttrs; 1186 } 1187 int stream = AudioAttributes.toLegacyStreamType(attributes); 1188 int max = mAudioManager.getStreamMaxVolume(stream); 1189 int current = mAudioManager.getStreamVolume(stream); 1190 return new ParcelableVolumeInfo( 1191 volumeType, attributes, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, current); 1192 } 1193 1194 @Override 1195 public void adjustVolume(int direction, int flags, String packageName) { 1196 updateCallingPackage(); 1197 int uid = Binder.getCallingUid(); 1198 final long token = Binder.clearCallingIdentity(); 1199 try { 1200 MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid, false); 1201 } finally { 1202 Binder.restoreCallingIdentity(token); 1203 } 1204 } 1205 1206 @Override 1207 public void setVolumeTo(int value, int flags, String packageName) { 1208 updateCallingPackage(); 1209 int uid = Binder.getCallingUid(); 1210 final long token = Binder.clearCallingIdentity(); 1211 try { 1212 MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid); 1213 } finally { 1214 Binder.restoreCallingIdentity(token); 1215 } 1216 } 1217 1218 @Override 1219 public void prepare() throws RemoteException { 1220 updateCallingPackage(); 1221 mSessionCb.prepare(); 1222 } 1223 1224 @Override 1225 public void prepareFromMediaId(String mediaId, Bundle extras) 1226 throws RemoteException { 1227 updateCallingPackage(); 1228 mSessionCb.prepareFromMediaId(mediaId, extras); 1229 } 1230 1231 @Override 1232 public void prepareFromSearch(String query, Bundle extras) throws RemoteException { 1233 updateCallingPackage(); 1234 mSessionCb.prepareFromSearch(query, extras); 1235 } 1236 1237 @Override 1238 public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException { 1239 updateCallingPackage(); 1240 mSessionCb.prepareFromUri(uri, extras); 1241 } 1242 1243 @Override 1244 public void play() throws RemoteException { 1245 updateCallingPackage(); 1246 mSessionCb.play(); 1247 } 1248 1249 @Override 1250 public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException { 1251 updateCallingPackage(); 1252 mSessionCb.playFromMediaId(mediaId, extras); 1253 } 1254 1255 @Override 1256 public void playFromSearch(String query, Bundle extras) throws RemoteException { 1257 updateCallingPackage(); 1258 mSessionCb.playFromSearch(query, extras); 1259 } 1260 1261 @Override 1262 public void playFromUri(Uri uri, Bundle extras) throws RemoteException { 1263 updateCallingPackage(); 1264 mSessionCb.playFromUri(uri, extras); 1265 } 1266 1267 @Override 1268 public void skipToQueueItem(long id) { 1269 updateCallingPackage(); 1270 mSessionCb.skipToTrack(id); 1271 } 1272 1273 @Override 1274 public void pause() throws RemoteException { 1275 updateCallingPackage(); 1276 mSessionCb.pause(); 1277 } 1278 1279 @Override 1280 public void stop() throws RemoteException { 1281 updateCallingPackage(); 1282 mSessionCb.stop(); 1283 } 1284 1285 @Override 1286 public void next() throws RemoteException { 1287 updateCallingPackage(); 1288 mSessionCb.next(); 1289 } 1290 1291 @Override 1292 public void previous() throws RemoteException { 1293 updateCallingPackage(); 1294 mSessionCb.previous(); 1295 } 1296 1297 @Override 1298 public void fastForward() throws RemoteException { 1299 updateCallingPackage(); 1300 mSessionCb.fastForward(); 1301 } 1302 1303 @Override 1304 public void rewind() throws RemoteException { 1305 updateCallingPackage(); 1306 mSessionCb.rewind(); 1307 } 1308 1309 @Override 1310 public void seekTo(long pos) throws RemoteException { 1311 updateCallingPackage(); 1312 mSessionCb.seekTo(pos); 1313 } 1314 1315 @Override 1316 public void rate(Rating rating) throws RemoteException { 1317 updateCallingPackage(); 1318 mSessionCb.rate(rating); 1319 } 1320 1321 @Override 1322 public void sendCustomAction(String action, Bundle args) 1323 throws RemoteException { 1324 updateCallingPackage(); 1325 mSessionCb.sendCustomAction(action, args); 1326 } 1327 1328 1329 @Override 1330 public MediaMetadata getMetadata() { 1331 synchronized (mLock) { 1332 return mMetadata; 1333 } 1334 } 1335 1336 @Override 1337 public PlaybackState getPlaybackState() { 1338 return getStateWithUpdatedPosition(); 1339 } 1340 1341 @Override 1342 public ParceledListSlice getQueue() { 1343 synchronized (mLock) { 1344 return mQueue; 1345 } 1346 } 1347 1348 @Override 1349 public CharSequence getQueueTitle() { 1350 return mQueueTitle; 1351 } 1352 1353 @Override 1354 public Bundle getExtras() { 1355 synchronized (mLock) { 1356 return mExtras; 1357 } 1358 } 1359 1360 @Override 1361 public int getRatingType() { 1362 return mRatingType; 1363 } 1364 1365 @Override 1366 public boolean isTransportControlEnabled() { 1367 return MediaSessionRecord.this.isTransportControlEnabled(); 1368 } 1369 } 1370 1371 private class ISessionControllerCallbackHolder { 1372 private final ISessionControllerCallback mCallback; 1373 private final String mPackageName; 1374 1375 ISessionControllerCallbackHolder(ISessionControllerCallback callback, int uid) { 1376 mCallback = callback; 1377 mPackageName = getPackageName(uid); 1378 } 1379 } 1380 1381 private class MessageHandler extends Handler { 1382 private static final int MSG_UPDATE_METADATA = 1; 1383 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 1384 private static final int MSG_UPDATE_QUEUE = 3; 1385 private static final int MSG_UPDATE_QUEUE_TITLE = 4; 1386 private static final int MSG_UPDATE_EXTRAS = 5; 1387 private static final int MSG_SEND_EVENT = 6; 1388 private static final int MSG_UPDATE_SESSION_STATE = 7; 1389 private static final int MSG_UPDATE_VOLUME = 8; 1390 private static final int MSG_DESTROYED = 9; 1391 1392 public MessageHandler(Looper looper) { 1393 super(looper); 1394 } 1395 @Override 1396 public void handleMessage(Message msg) { 1397 switch (msg.what) { 1398 case MSG_UPDATE_METADATA: 1399 pushMetadataUpdate(); 1400 break; 1401 case MSG_UPDATE_PLAYBACK_STATE: 1402 pushPlaybackStateUpdate(); 1403 break; 1404 case MSG_UPDATE_QUEUE: 1405 pushQueueUpdate(); 1406 break; 1407 case MSG_UPDATE_QUEUE_TITLE: 1408 pushQueueTitleUpdate(); 1409 break; 1410 case MSG_UPDATE_EXTRAS: 1411 pushExtrasUpdate(); 1412 break; 1413 case MSG_SEND_EVENT: 1414 pushEvent((String) msg.obj, msg.getData()); 1415 break; 1416 case MSG_UPDATE_SESSION_STATE: 1417 // TODO add session state 1418 break; 1419 case MSG_UPDATE_VOLUME: 1420 pushVolumeUpdate(); 1421 break; 1422 case MSG_DESTROYED: 1423 pushSessionDestroyed(); 1424 } 1425 } 1426 1427 public void post(int what) { 1428 post(what, null); 1429 } 1430 1431 public void post(int what, Object obj) { 1432 obtainMessage(what, obj).sendToTarget(); 1433 } 1434 1435 public void post(int what, Object obj, Bundle data) { 1436 Message msg = obtainMessage(what, obj); 1437 msg.setData(data); 1438 msg.sendToTarget(); 1439 } 1440 } 1441 1442 } 1443