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