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.annotation.Nullable; 20 import android.app.PendingIntent; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ParceledListSlice; 25 import android.media.AudioAttributes; 26 import android.media.AudioManager; 27 import android.media.AudioSystem; 28 import android.media.MediaMetadata; 29 import android.media.MediaRouter2Manager; 30 import android.media.Rating; 31 import android.media.RoutingSessionInfo; 32 import android.media.VolumeProvider; 33 import android.media.session.ISession; 34 import android.media.session.ISessionCallback; 35 import android.media.session.ISessionController; 36 import android.media.session.ISessionControllerCallback; 37 import android.media.session.MediaController; 38 import android.media.session.MediaController.PlaybackInfo; 39 import android.media.session.MediaSession; 40 import android.media.session.MediaSession.QueueItem; 41 import android.media.session.ParcelableListBinder; 42 import android.media.session.PlaybackState; 43 import android.net.Uri; 44 import android.os.Binder; 45 import android.os.Bundle; 46 import android.os.DeadObjectException; 47 import android.os.Handler; 48 import android.os.IBinder; 49 import android.os.Looper; 50 import android.os.Message; 51 import android.os.Process; 52 import android.os.RemoteException; 53 import android.os.ResultReceiver; 54 import android.os.SystemClock; 55 import android.text.TextUtils; 56 import android.util.Log; 57 import android.view.KeyEvent; 58 59 import java.io.PrintWriter; 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.Collection; 63 import java.util.List; 64 import java.util.concurrent.CopyOnWriteArrayList; 65 66 /** 67 * This is the system implementation of a Session. Apps will interact with the 68 * MediaSession wrapper class instead. 69 */ 70 // TODO(jaewan): Do not call service method directly -- introduce listener instead. 71 public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl { 72 private static final String TAG = "MediaSessionRecord"; 73 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 74 75 /** 76 * The amount of time we'll send an assumed volume after the last volume 77 * command before reverting to the last reported volume. 78 */ 79 private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000; 80 81 /** 82 * These are states that usually indicate the user took an action and should 83 * bump priority regardless of the old state. 84 */ 85 private static final List<Integer> ALWAYS_PRIORITY_STATES = Arrays.asList( 86 PlaybackState.STATE_FAST_FORWARDING, 87 PlaybackState.STATE_REWINDING, 88 PlaybackState.STATE_SKIPPING_TO_PREVIOUS, 89 PlaybackState.STATE_SKIPPING_TO_NEXT); 90 /** 91 * These are states that usually indicate the user took an action if they 92 * were entered from a non-priority state. 93 */ 94 private static final List<Integer> TRANSITION_PRIORITY_STATES = Arrays.asList( 95 PlaybackState.STATE_BUFFERING, 96 PlaybackState.STATE_CONNECTING, 97 PlaybackState.STATE_PLAYING); 98 99 private static final AudioAttributes DEFAULT_ATTRIBUTES = 100 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(); 101 getVolumeStream(@ullable AudioAttributes attr)102 private static int getVolumeStream(@Nullable AudioAttributes attr) { 103 if (attr == null) { 104 return DEFAULT_ATTRIBUTES.getVolumeControlStream(); 105 } 106 final int stream = attr.getVolumeControlStream(); 107 if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) { 108 return DEFAULT_ATTRIBUTES.getVolumeControlStream(); 109 } 110 return stream; 111 } 112 113 private final MessageHandler mHandler; 114 115 private final int mOwnerPid; 116 private final int mOwnerUid; 117 private final int mUserId; 118 private final String mPackageName; 119 private final String mTag; 120 private final Bundle mSessionInfo; 121 private final ControllerStub mController; 122 private final MediaSession.Token mSessionToken; 123 private final SessionStub mSession; 124 private final SessionCb mSessionCb; 125 private final MediaSessionService mService; 126 private final Context mContext; 127 private final boolean mVolumeAdjustmentForRemoteGroupSessions; 128 129 private final Object mLock = new Object(); 130 private final CopyOnWriteArrayList<ISessionControllerCallbackHolder> 131 mControllerCallbackHolders = new CopyOnWriteArrayList<>(); 132 133 private long mFlags; 134 private MediaButtonReceiverHolder mMediaButtonReceiverHolder; 135 private PendingIntent mLaunchIntent; 136 137 // TransportPerformer fields 138 private Bundle mExtras; 139 // Note: Avoid unparceling the bundle inside MediaMetadata since unparceling in system process 140 // may result in throwing an exception. 141 private MediaMetadata mMetadata; 142 private PlaybackState mPlaybackState; 143 private List<QueueItem> mQueue; 144 private CharSequence mQueueTitle; 145 private int mRatingType; 146 // End TransportPerformer fields 147 148 // Volume handling fields 149 private AudioAttributes mAudioAttrs; 150 private AudioManager mAudioManager; 151 private int mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; 152 private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; 153 private int mMaxVolume = 0; 154 private int mCurrentVolume = 0; 155 private int mOptimisticVolume = -1; 156 private String mVolumeControlId; 157 // End volume handling fields 158 159 private boolean mIsActive = false; 160 private boolean mDestroyed = false; 161 162 private long mDuration = -1; 163 private String mMetadataDescription; 164 165 private int mPolicies; 166 MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo, MediaSessionService service, Looper handlerLooper, int policies)167 public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, 168 ISessionCallback cb, String tag, Bundle sessionInfo, 169 MediaSessionService service, Looper handlerLooper, int policies) 170 throws RemoteException { 171 mOwnerPid = ownerPid; 172 mOwnerUid = ownerUid; 173 mUserId = userId; 174 mPackageName = ownerPackageName; 175 mTag = tag; 176 mSessionInfo = sessionInfo; 177 mController = new ControllerStub(); 178 mSessionToken = new MediaSession.Token(ownerUid, mController); 179 mSession = new SessionStub(); 180 mSessionCb = new SessionCb(cb); 181 mService = service; 182 mContext = mService.getContext(); 183 mHandler = new MessageHandler(handlerLooper); 184 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 185 mAudioAttrs = DEFAULT_ATTRIBUTES; 186 mPolicies = policies; 187 mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean( 188 com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions); 189 190 // May throw RemoteException if the session app is killed. 191 mSessionCb.mCb.asBinder().linkToDeath(this, 0); 192 } 193 194 /** 195 * Get the session binder for the {@link MediaSession}. 196 * 197 * @return The session binder apps talk to. 198 */ getSessionBinder()199 public ISession getSessionBinder() { 200 return mSession; 201 } 202 203 /** 204 * Get the session token for creating {@link MediaController}. 205 * 206 * @return The session token. 207 */ getSessionToken()208 public MediaSession.Token getSessionToken() { 209 return mSessionToken; 210 } 211 212 /** 213 * Get the info for this session. 214 * 215 * @return Info that identifies this session. 216 */ 217 @Override getPackageName()218 public String getPackageName() { 219 return mPackageName; 220 } 221 222 /** 223 * Get the intent the app set for their media button receiver. 224 * 225 * @return The pending intent set by the app or null. 226 */ getMediaButtonReceiver()227 public MediaButtonReceiverHolder getMediaButtonReceiver() { 228 return mMediaButtonReceiverHolder; 229 } 230 231 /** 232 * Get the UID this session was created for. 233 * 234 * @return The UID for this session. 235 */ 236 @Override getUid()237 public int getUid() { 238 return mOwnerUid; 239 } 240 241 /** 242 * Get the user id this session was created for. 243 * 244 * @return The user id for this session. 245 */ 246 @Override getUserId()247 public int getUserId() { 248 return mUserId; 249 } 250 251 /** 252 * Check if this session has system priorty and should receive media buttons 253 * before any other sessions. 254 * 255 * @return True if this is a system priority session, false otherwise 256 */ 257 @Override isSystemPriority()258 public boolean isSystemPriority() { 259 return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0; 260 } 261 262 /** 263 * Send a volume adjustment to the session owner. Direction must be one of 264 * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, 265 * {@link AudioManager#ADJUST_SAME}. 266 * 267 * @param packageName The package that made the original volume request. 268 * @param opPackageName The op package that made the original volume request. 269 * @param pid The pid that made the original volume request. 270 * @param uid The uid that made the original volume request. 271 * @param asSystemService {@code true} if the event sent to the session as if it was come from 272 * the system service instead of the app process. This helps sessions to distinguish 273 * between the key injection by the app and key events from the hardware devices. 274 * Should be used only when the volume key events aren't handled by foreground 275 * activity. {@code false} otherwise to tell session about the real caller. 276 * @param direction The direction to adjust volume in. 277 * @param flags Any of the flags from {@link AudioManager}. 278 * @param useSuggested True to use adjustSuggestedStreamVolumeForUid instead of 279 * adjustStreamVolumeForUid 280 */ adjustVolume(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, int direction, int flags, boolean useSuggested)281 public void adjustVolume(String packageName, String opPackageName, int pid, int uid, 282 boolean asSystemService, int direction, int flags, boolean useSuggested) { 283 int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND; 284 if (checkPlaybackActiveState(true) || isSystemPriority()) { 285 flags &= ~AudioManager.FLAG_PLAY_SOUND; 286 } 287 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { 288 // Adjust the volume with a handler not to be blocked by other system service. 289 int stream = getVolumeStream(mAudioAttrs); 290 postAdjustLocalVolume(stream, direction, flags, opPackageName, pid, uid, 291 asSystemService, useSuggested, previousFlagPlaySound); 292 } else { 293 if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) { 294 if (DEBUG) { 295 Log.d(TAG, "Session does not support volume adjustment"); 296 } 297 } else if (direction == AudioManager.ADJUST_TOGGLE_MUTE 298 || direction == AudioManager.ADJUST_MUTE 299 || direction == AudioManager.ADJUST_UNMUTE) { 300 Log.w(TAG, "Muting remote playback is not supported"); 301 } else { 302 if (DEBUG) { 303 Log.w(TAG, "adjusting volume, pkg=" + packageName + ", asSystemService=" 304 + asSystemService + ", dir=" + direction); 305 } 306 mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction); 307 308 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 309 mOptimisticVolume = volumeBefore + direction; 310 mOptimisticVolume = Math.max(0, Math.min(mOptimisticVolume, mMaxVolume)); 311 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 312 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 313 if (volumeBefore != mOptimisticVolume) { 314 pushVolumeUpdate(); 315 } 316 317 if (DEBUG) { 318 Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is " 319 + mMaxVolume); 320 } 321 } 322 // Always notify, even if the volume hasn't changed. This is important to ensure that 323 // System UI receives an event if a hardware volume key is pressed but the session that 324 // handles it does not allow volume adjustment. Without such an event, System UI would 325 // not show volume controls to the user. 326 mService.notifyRemoteVolumeChanged(flags, this); 327 } 328 } 329 setVolumeTo(String packageName, String opPackageName, int pid, int uid, int value, int flags)330 private void setVolumeTo(String packageName, String opPackageName, int pid, int uid, int value, 331 int flags) { 332 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { 333 int stream = getVolumeStream(mAudioAttrs); 334 final int volumeValue = value; 335 mHandler.post(new Runnable() { 336 @Override 337 public void run() { 338 try { 339 mAudioManager.setStreamVolumeForUid(stream, volumeValue, flags, 340 opPackageName, uid, pid, 341 mContext.getApplicationInfo().targetSdkVersion); 342 } catch (IllegalArgumentException | SecurityException e) { 343 Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue 344 + ", flags=" + flags, e); 345 } 346 } 347 }); 348 } else { 349 if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) { 350 if (DEBUG) { 351 Log.d(TAG, "Session does not support setting volume"); 352 } 353 } else { 354 value = Math.max(0, Math.min(value, mMaxVolume)); 355 mSessionCb.setVolumeTo(packageName, pid, uid, value); 356 357 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 358 mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume)); 359 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 360 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 361 if (volumeBefore != mOptimisticVolume) { 362 pushVolumeUpdate(); 363 } 364 365 if (DEBUG) { 366 Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is " 367 + mMaxVolume); 368 } 369 } 370 // Always notify, even if the volume hasn't changed. 371 mService.notifyRemoteVolumeChanged(flags, this); 372 } 373 } 374 375 /** 376 * Check if this session has been set to active by the app. 377 * <p> 378 * It's not used to prioritize sessions for dispatching media keys since API 26, but still used 379 * to filter session list in MediaSessionManager#getActiveSessions(). 380 * 381 * @return True if the session is active, false otherwise. 382 */ 383 @Override isActive()384 public boolean isActive() { 385 return mIsActive && !mDestroyed; 386 } 387 388 /** 389 * Check if the session's playback active state matches with the expectation. This always return 390 * {@code false} if the playback state is {@code null}, where we cannot know the actual playback 391 * state associated with the session. 392 * 393 * @param expected True if playback is expected to be active. false otherwise. 394 * @return True if the session's playback matches with the expectation. false otherwise. 395 */ 396 @Override checkPlaybackActiveState(boolean expected)397 public boolean checkPlaybackActiveState(boolean expected) { 398 if (mPlaybackState == null) { 399 return false; 400 } 401 return mPlaybackState.isActive() == expected; 402 } 403 404 /** 405 * Get whether the playback is local. 406 * 407 * @return {@code true} if the playback is local. 408 */ 409 @Override isPlaybackTypeLocal()410 public boolean isPlaybackTypeLocal() { 411 return mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; 412 } 413 414 @Override binderDied()415 public void binderDied() { 416 mService.onSessionDied(this); 417 } 418 419 /** 420 * Finish cleaning up this session, including disconnecting if connected and 421 * removing the death observer from the callback binder. 422 */ 423 @Override close()424 public void close() { 425 synchronized (mLock) { 426 if (mDestroyed) { 427 return; 428 } 429 mSessionCb.mCb.asBinder().unlinkToDeath(this, 0); 430 mDestroyed = true; 431 mPlaybackState = null; 432 mHandler.post(MessageHandler.MSG_DESTROYED); 433 } 434 } 435 436 @Override isClosed()437 public boolean isClosed() { 438 synchronized (mLock) { 439 return mDestroyed; 440 } 441 } 442 443 /** 444 * Sends media button. 445 * 446 * @param packageName caller package name 447 * @param pid caller pid 448 * @param uid caller uid 449 * @param asSystemService {@code true} if the event sent to the session as if it was come from 450 * the system service instead of the app process. 451 * @param ke key events 452 * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed. 453 * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is 454 * needed. 455 * @return {@code true} if the attempt to send media button was successfully. 456 * {@code false} otherwise. 457 */ sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent ke, int sequenceId, ResultReceiver cb)458 public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, 459 KeyEvent ke, int sequenceId, ResultReceiver cb) { 460 return mSessionCb.sendMediaButton(packageName, pid, uid, asSystemService, ke, sequenceId, 461 cb); 462 } 463 464 @Override canHandleVolumeKey()465 public boolean canHandleVolumeKey() { 466 if (isPlaybackTypeLocal() || mVolumeAdjustmentForRemoteGroupSessions) { 467 return true; 468 } 469 MediaRouter2Manager mRouter2Manager = MediaRouter2Manager.getInstance(mContext); 470 List<RoutingSessionInfo> sessions = 471 mRouter2Manager.getRoutingSessions(mPackageName); 472 boolean foundNonSystemSession = false; 473 boolean isGroup = false; 474 for (RoutingSessionInfo session : sessions) { 475 if (!session.isSystemSession()) { 476 foundNonSystemSession = true; 477 int selectedRouteCount = session.getSelectedRoutes().size(); 478 if (selectedRouteCount > 1) { 479 isGroup = true; 480 break; 481 } 482 } 483 } 484 if (!foundNonSystemSession) { 485 Log.d(TAG, "No routing session for " + mPackageName); 486 return false; 487 } 488 return !isGroup; 489 } 490 491 @Override getSessionPolicies()492 public int getSessionPolicies() { 493 synchronized (mLock) { 494 return mPolicies; 495 } 496 } 497 498 @Override setSessionPolicies(int policies)499 public void setSessionPolicies(int policies) { 500 synchronized (mLock) { 501 mPolicies = policies; 502 } 503 } 504 505 @Override dump(PrintWriter pw, String prefix)506 public void dump(PrintWriter pw, String prefix) { 507 pw.println(prefix + mTag + " " + this); 508 509 final String indent = prefix + " "; 510 pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid 511 + ", userId=" + mUserId); 512 pw.println(indent + "package=" + mPackageName); 513 pw.println(indent + "launchIntent=" + mLaunchIntent); 514 pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiverHolder); 515 pw.println(indent + "active=" + mIsActive); 516 pw.println(indent + "flags=" + mFlags); 517 pw.println(indent + "rating type=" + mRatingType); 518 pw.println(indent + "controllers: " + mControllerCallbackHolders.size()); 519 pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString())); 520 pw.println(indent + "audioAttrs=" + mAudioAttrs); 521 pw.println(indent + "volumeType=" + mVolumeType + ", controlType=" + mVolumeControlType 522 + ", max=" + mMaxVolume + ", current=" + mCurrentVolume); 523 pw.println(indent + "metadata: " + mMetadataDescription); 524 pw.println(indent + "queueTitle=" + mQueueTitle + ", size=" 525 + (mQueue == null ? 0 : mQueue.size())); 526 } 527 528 @Override toString()529 public String toString() { 530 return mPackageName + "/" + mTag + " (userId=" + mUserId + ")"; 531 } 532 postAdjustLocalVolume(final int stream, final int direction, final int flags, final String callingOpPackageName, final int callingPid, final int callingUid, final boolean asSystemService, final boolean useSuggested, final int previousFlagPlaySound)533 private void postAdjustLocalVolume(final int stream, final int direction, final int flags, 534 final String callingOpPackageName, final int callingPid, final int callingUid, 535 final boolean asSystemService, final boolean useSuggested, 536 final int previousFlagPlaySound) { 537 if (DEBUG) { 538 Log.w(TAG, "adjusting local volume, stream=" + stream + ", dir=" + direction 539 + ", asSystemService=" + asSystemService + ", useSuggested=" + useSuggested); 540 } 541 // Must use opPackageName for adjusting volumes with UID. 542 final String opPackageName; 543 final int uid; 544 final int pid; 545 if (asSystemService) { 546 opPackageName = mContext.getOpPackageName(); 547 uid = Process.SYSTEM_UID; 548 pid = Process.myPid(); 549 } else { 550 opPackageName = callingOpPackageName; 551 uid = callingUid; 552 pid = callingPid; 553 } 554 mHandler.post(new Runnable() { 555 @Override 556 public void run() { 557 try { 558 if (useSuggested) { 559 if (AudioSystem.isStreamActive(stream, 0)) { 560 mAudioManager.adjustSuggestedStreamVolumeForUid(stream, 561 direction, flags, opPackageName, uid, pid, 562 mContext.getApplicationInfo().targetSdkVersion); 563 } else { 564 mAudioManager.adjustSuggestedStreamVolumeForUid( 565 AudioManager.USE_DEFAULT_STREAM_TYPE, direction, 566 flags | previousFlagPlaySound, opPackageName, uid, pid, 567 mContext.getApplicationInfo().targetSdkVersion); 568 } 569 } else { 570 mAudioManager.adjustStreamVolumeForUid(stream, direction, flags, 571 opPackageName, uid, pid, 572 mContext.getApplicationInfo().targetSdkVersion); 573 } 574 } catch (IllegalArgumentException | SecurityException e) { 575 Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream=" 576 + stream + ", flags=" + flags + ", opPackageName=" + opPackageName 577 + ", uid=" + uid + ", useSuggested=" + useSuggested 578 + ", previousFlagPlaySound=" + previousFlagPlaySound, e); 579 } 580 } 581 }); 582 } 583 logCallbackException( String msg, ISessionControllerCallbackHolder holder, Exception e)584 private void logCallbackException( 585 String msg, ISessionControllerCallbackHolder holder, Exception e) { 586 Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName 587 + ", exception=" + e); 588 } 589 pushPlaybackStateUpdate()590 private void pushPlaybackStateUpdate() { 591 PlaybackState playbackState; 592 synchronized (mLock) { 593 if (mDestroyed) { 594 return; 595 } 596 playbackState = mPlaybackState; 597 } 598 Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; 599 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 600 try { 601 holder.mCallback.onPlaybackStateChanged(playbackState); 602 } catch (DeadObjectException e) { 603 if (deadCallbackHolders == null) { 604 deadCallbackHolders = new ArrayList<>(); 605 } 606 deadCallbackHolders.add(holder); 607 logCallbackException("Removing dead callback in pushPlaybackStateUpdate", holder, 608 e); 609 } catch (RemoteException e) { 610 logCallbackException("unexpected exception in pushPlaybackStateUpdate", holder, e); 611 } 612 } 613 if (deadCallbackHolders != null) { 614 mControllerCallbackHolders.removeAll(deadCallbackHolders); 615 } 616 } 617 pushMetadataUpdate()618 private void pushMetadataUpdate() { 619 MediaMetadata metadata; 620 synchronized (mLock) { 621 if (mDestroyed) { 622 return; 623 } 624 metadata = mMetadata; 625 } 626 Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; 627 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 628 try { 629 holder.mCallback.onMetadataChanged(metadata); 630 } catch (DeadObjectException e) { 631 if (deadCallbackHolders == null) { 632 deadCallbackHolders = new ArrayList<>(); 633 } 634 deadCallbackHolders.add(holder); 635 logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e); 636 } catch (RemoteException e) { 637 logCallbackException("unexpected exception in pushMetadataUpdate", holder, e); 638 } 639 } 640 if (deadCallbackHolders != null) { 641 mControllerCallbackHolders.removeAll(deadCallbackHolders); 642 } 643 } 644 pushQueueUpdate()645 private void pushQueueUpdate() { 646 ParceledListSlice<QueueItem> parcelableQueue; 647 synchronized (mLock) { 648 if (mDestroyed) { 649 return; 650 } 651 if (mQueue == null) { 652 parcelableQueue = null; 653 } else { 654 parcelableQueue = new ParceledListSlice<>(mQueue); 655 // Limit the size of initial Parcel to prevent binder buffer overflow 656 // as onQueueChanged is an async binder call. 657 parcelableQueue.setInlineCountLimit(1); 658 } 659 } 660 Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; 661 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 662 try { 663 holder.mCallback.onQueueChanged(parcelableQueue); 664 } catch (DeadObjectException e) { 665 if (deadCallbackHolders == null) { 666 deadCallbackHolders = new ArrayList<>(); 667 } 668 deadCallbackHolders.add(holder); 669 logCallbackException("Removing dead callback in pushQueueUpdate", holder, e); 670 } catch (RemoteException e) { 671 logCallbackException("unexpected exception in pushQueueUpdate", holder, e); 672 } 673 } 674 if (deadCallbackHolders != null) { 675 mControllerCallbackHolders.removeAll(deadCallbackHolders); 676 } 677 } 678 pushQueueTitleUpdate()679 private void pushQueueTitleUpdate() { 680 CharSequence queueTitle; 681 synchronized (mLock) { 682 if (mDestroyed) { 683 return; 684 } 685 queueTitle = mQueueTitle; 686 } 687 Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; 688 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 689 try { 690 holder.mCallback.onQueueTitleChanged(queueTitle); 691 } catch (DeadObjectException e) { 692 if (deadCallbackHolders == null) { 693 deadCallbackHolders = new ArrayList<>(); 694 } 695 deadCallbackHolders.add(holder); 696 logCallbackException("Removing dead callback in pushQueueTitleUpdate", holder, e); 697 } catch (RemoteException e) { 698 logCallbackException("unexpected exception in pushQueueTitleUpdate", holder, e); 699 } 700 } 701 if (deadCallbackHolders != null) { 702 mControllerCallbackHolders.removeAll(deadCallbackHolders); 703 } 704 } 705 pushExtrasUpdate()706 private void pushExtrasUpdate() { 707 Bundle extras; 708 synchronized (mLock) { 709 if (mDestroyed) { 710 return; 711 } 712 extras = mExtras; 713 } 714 Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; 715 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 716 try { 717 holder.mCallback.onExtrasChanged(extras); 718 } catch (DeadObjectException e) { 719 if (deadCallbackHolders == null) { 720 deadCallbackHolders = new ArrayList<>(); 721 } 722 deadCallbackHolders.add(holder); 723 logCallbackException("Removing dead callback in pushExtrasUpdate", holder, e); 724 } catch (RemoteException e) { 725 logCallbackException("unexpected exception in pushExtrasUpdate", holder, e); 726 } 727 } 728 if (deadCallbackHolders != null) { 729 mControllerCallbackHolders.removeAll(deadCallbackHolders); 730 } 731 } 732 pushVolumeUpdate()733 private void pushVolumeUpdate() { 734 PlaybackInfo info; 735 synchronized (mLock) { 736 if (mDestroyed) { 737 return; 738 } 739 info = getVolumeAttributes(); 740 } 741 Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; 742 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 743 try { 744 holder.mCallback.onVolumeInfoChanged(info); 745 } catch (DeadObjectException e) { 746 if (deadCallbackHolders == null) { 747 deadCallbackHolders = new ArrayList<>(); 748 } 749 deadCallbackHolders.add(holder); 750 logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e); 751 } catch (RemoteException e) { 752 logCallbackException("unexpected exception in pushVolumeUpdate", holder, e); 753 } 754 } 755 if (deadCallbackHolders != null) { 756 mControllerCallbackHolders.removeAll(deadCallbackHolders); 757 } 758 } 759 pushEvent(String event, Bundle data)760 private void pushEvent(String event, Bundle data) { 761 synchronized (mLock) { 762 if (mDestroyed) { 763 return; 764 } 765 } 766 Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; 767 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 768 try { 769 holder.mCallback.onEvent(event, data); 770 } catch (DeadObjectException e) { 771 if (deadCallbackHolders == null) { 772 deadCallbackHolders = new ArrayList<>(); 773 } 774 deadCallbackHolders.add(holder); 775 logCallbackException("Removing dead callback in pushEvent", holder, e); 776 } catch (RemoteException e) { 777 logCallbackException("unexpected exception in pushEvent", holder, e); 778 } 779 } 780 if (deadCallbackHolders != null) { 781 mControllerCallbackHolders.removeAll(deadCallbackHolders); 782 } 783 } 784 pushSessionDestroyed()785 private void pushSessionDestroyed() { 786 synchronized (mLock) { 787 // This is the only method that may be (and can only be) called 788 // after the session is destroyed. 789 if (!mDestroyed) { 790 return; 791 } 792 } 793 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 794 try { 795 holder.mCallback.onSessionDestroyed(); 796 } catch (DeadObjectException e) { 797 logCallbackException("Removing dead callback in pushSessionDestroyed", holder, e); 798 } catch (RemoteException e) { 799 logCallbackException("unexpected exception in pushSessionDestroyed", holder, e); 800 } 801 } 802 // After notifying clear all listeners 803 mControllerCallbackHolders.clear(); 804 } 805 getStateWithUpdatedPosition()806 private PlaybackState getStateWithUpdatedPosition() { 807 PlaybackState state; 808 long duration; 809 synchronized (mLock) { 810 if (mDestroyed) { 811 return null; 812 } 813 state = mPlaybackState; 814 duration = mDuration; 815 } 816 PlaybackState result = null; 817 if (state != null) { 818 if (state.getState() == PlaybackState.STATE_PLAYING 819 || state.getState() == PlaybackState.STATE_FAST_FORWARDING 820 || state.getState() == PlaybackState.STATE_REWINDING) { 821 long updateTime = state.getLastPositionUpdateTime(); 822 long currentTime = SystemClock.elapsedRealtime(); 823 if (updateTime > 0) { 824 long position = (long) (state.getPlaybackSpeed() 825 * (currentTime - updateTime)) + state.getPosition(); 826 if (duration >= 0 && position > duration) { 827 position = duration; 828 } else if (position < 0) { 829 position = 0; 830 } 831 PlaybackState.Builder builder = new PlaybackState.Builder(state); 832 builder.setState(state.getState(), position, state.getPlaybackSpeed(), 833 currentTime); 834 result = builder.build(); 835 } 836 } 837 } 838 return result == null ? state : result; 839 } 840 getControllerHolderIndexForCb(ISessionControllerCallback cb)841 private int getControllerHolderIndexForCb(ISessionControllerCallback cb) { 842 IBinder binder = cb.asBinder(); 843 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 844 if (binder.equals(mControllerCallbackHolders.get(i).mCallback.asBinder())) { 845 return i; 846 } 847 } 848 return -1; 849 } 850 getVolumeAttributes()851 private PlaybackInfo getVolumeAttributes() { 852 int volumeType; 853 AudioAttributes attributes; 854 synchronized (mLock) { 855 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 856 int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume; 857 return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current, 858 mAudioAttrs, mVolumeControlId); 859 } 860 volumeType = mVolumeType; 861 attributes = mAudioAttrs; 862 } 863 int stream = getVolumeStream(attributes); 864 int max = mAudioManager.getStreamMaxVolume(stream); 865 int current = mAudioManager.getStreamVolume(stream); 866 return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, 867 current, attributes, null); 868 } 869 870 private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { 871 @Override 872 public void run() { 873 boolean needUpdate = (mOptimisticVolume != mCurrentVolume); 874 mOptimisticVolume = -1; 875 if (needUpdate) { 876 pushVolumeUpdate(); 877 } 878 } 879 }; 880 881 private final class SessionStub extends ISession.Stub { 882 @Override destroySession()883 public void destroySession() throws RemoteException { 884 final long token = Binder.clearCallingIdentity(); 885 try { 886 mService.onSessionDied(MediaSessionRecord.this); 887 } finally { 888 Binder.restoreCallingIdentity(token); 889 } 890 } 891 892 @Override sendEvent(String event, Bundle data)893 public void sendEvent(String event, Bundle data) throws RemoteException { 894 mHandler.post(MessageHandler.MSG_SEND_EVENT, event, 895 data == null ? null : new Bundle(data)); 896 } 897 898 @Override getController()899 public ISessionController getController() throws RemoteException { 900 return mController; 901 } 902 903 @Override setActive(boolean active)904 public void setActive(boolean active) throws RemoteException { 905 mIsActive = active; 906 final long token = Binder.clearCallingIdentity(); 907 try { 908 mService.onSessionActiveStateChanged(MediaSessionRecord.this); 909 } finally { 910 Binder.restoreCallingIdentity(token); 911 } 912 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 913 } 914 915 @Override setFlags(int flags)916 public void setFlags(int flags) throws RemoteException { 917 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 918 int pid = Binder.getCallingPid(); 919 int uid = Binder.getCallingUid(); 920 mService.enforcePhoneStatePermission(pid, uid); 921 } 922 mFlags = flags; 923 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 924 final long token = Binder.clearCallingIdentity(); 925 try { 926 mService.setGlobalPrioritySession(MediaSessionRecord.this); 927 } finally { 928 Binder.restoreCallingIdentity(token); 929 } 930 } 931 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 932 } 933 934 @Override setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)935 public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName) 936 throws RemoteException { 937 final long token = Binder.clearCallingIdentity(); 938 try { 939 if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) 940 != 0) { 941 return; 942 } 943 mMediaButtonReceiverHolder = 944 MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName); 945 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); 946 } finally { 947 Binder.restoreCallingIdentity(token); 948 } 949 } 950 951 @Override setMediaButtonBroadcastReceiver(ComponentName receiver)952 public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException { 953 final long token = Binder.clearCallingIdentity(); 954 try { 955 if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) 956 != 0) { 957 return; 958 } 959 mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver); 960 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); 961 } finally { 962 Binder.restoreCallingIdentity(token); 963 } 964 } 965 966 @Override setLaunchPendingIntent(PendingIntent pi)967 public void setLaunchPendingIntent(PendingIntent pi) throws RemoteException { 968 mLaunchIntent = pi; 969 } 970 971 @Override setMetadata(MediaMetadata metadata, long duration, String metadataDescription)972 public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) 973 throws RemoteException { 974 synchronized (mLock) { 975 MediaMetadata temp = metadata == null ? null : new MediaMetadata.Builder(metadata) 976 .build(); 977 // This is to guarantee that the underlying bundle is unparceled 978 // before we set it to prevent concurrent reads from throwing an 979 // exception 980 if (temp != null) { 981 temp.size(); 982 } 983 mMetadata = temp; 984 mDuration = duration; 985 mMetadataDescription = metadataDescription; 986 } 987 mHandler.post(MessageHandler.MSG_UPDATE_METADATA); 988 } 989 990 @Override setPlaybackState(PlaybackState state)991 public void setPlaybackState(PlaybackState state) throws RemoteException { 992 int oldState = mPlaybackState == null 993 ? PlaybackState.STATE_NONE : mPlaybackState.getState(); 994 int newState = state == null 995 ? PlaybackState.STATE_NONE : state.getState(); 996 boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState) 997 || (!TRANSITION_PRIORITY_STATES.contains(oldState) 998 && TRANSITION_PRIORITY_STATES.contains(newState)); 999 synchronized (mLock) { 1000 mPlaybackState = state; 1001 } 1002 final long token = Binder.clearCallingIdentity(); 1003 try { 1004 mService.onSessionPlaybackStateChanged( 1005 MediaSessionRecord.this, shouldUpdatePriority); 1006 } finally { 1007 Binder.restoreCallingIdentity(token); 1008 } 1009 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); 1010 } 1011 1012 @Override resetQueue()1013 public void resetQueue() throws RemoteException { 1014 synchronized (mLock) { 1015 mQueue = null; 1016 } 1017 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); 1018 } 1019 1020 @Override getBinderForSetQueue()1021 public IBinder getBinderForSetQueue() throws RemoteException { 1022 return new ParcelableListBinder<QueueItem>((list) -> { 1023 synchronized (mLock) { 1024 mQueue = list; 1025 } 1026 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); 1027 }); 1028 } 1029 1030 @Override setQueueTitle(CharSequence title)1031 public void setQueueTitle(CharSequence title) throws RemoteException { 1032 mQueueTitle = title; 1033 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE); 1034 } 1035 1036 @Override setExtras(Bundle extras)1037 public void setExtras(Bundle extras) throws RemoteException { 1038 synchronized (mLock) { 1039 mExtras = extras == null ? null : new Bundle(extras); 1040 } 1041 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS); 1042 } 1043 1044 @Override setRatingType(int type)1045 public void setRatingType(int type) throws RemoteException { 1046 mRatingType = type; 1047 } 1048 1049 @Override setCurrentVolume(int volume)1050 public void setCurrentVolume(int volume) throws RemoteException { 1051 mCurrentVolume = volume; 1052 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 1053 } 1054 1055 @Override setPlaybackToLocal(AudioAttributes attributes)1056 public void setPlaybackToLocal(AudioAttributes attributes) throws RemoteException { 1057 boolean typeChanged; 1058 synchronized (mLock) { 1059 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE; 1060 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; 1061 mVolumeControlId = null; 1062 if (attributes != null) { 1063 mAudioAttrs = attributes; 1064 } else { 1065 Log.e(TAG, "Received null audio attributes, using existing attributes"); 1066 } 1067 } 1068 if (typeChanged) { 1069 final long token = Binder.clearCallingIdentity(); 1070 try { 1071 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 1072 } finally { 1073 Binder.restoreCallingIdentity(token); 1074 } 1075 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 1076 } 1077 } 1078 1079 @Override setPlaybackToRemote(int control, int max, String controlId)1080 public void setPlaybackToRemote(int control, int max, String controlId) 1081 throws RemoteException { 1082 boolean typeChanged; 1083 synchronized (mLock) { 1084 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; 1085 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE; 1086 mVolumeControlType = control; 1087 mMaxVolume = max; 1088 mVolumeControlId = controlId; 1089 } 1090 if (typeChanged) { 1091 final long token = Binder.clearCallingIdentity(); 1092 try { 1093 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 1094 } finally { 1095 Binder.restoreCallingIdentity(token); 1096 } 1097 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 1098 } 1099 } 1100 } 1101 1102 class SessionCb { 1103 private final ISessionCallback mCb; 1104 1105 SessionCb(ISessionCallback cb) { 1106 mCb = cb; 1107 } 1108 1109 public boolean sendMediaButton(String packageName, int pid, int uid, 1110 boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { 1111 try { 1112 if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { 1113 final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction()) 1114 + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode()); 1115 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1116 pid, uid, packageName, reason); 1117 } 1118 if (asSystemService) { 1119 mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), 1120 Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb); 1121 } else { 1122 mCb.onMediaButton(packageName, pid, uid, 1123 createMediaButtonIntent(keyEvent), sequenceId, cb); 1124 } 1125 return true; 1126 } catch (RemoteException e) { 1127 Log.e(TAG, "Remote failure in sendMediaRequest.", e); 1128 } 1129 return false; 1130 } 1131 1132 public boolean sendMediaButton(String packageName, int pid, int uid, 1133 boolean asSystemService, KeyEvent keyEvent) { 1134 try { 1135 if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { 1136 final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction()) 1137 + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode()); 1138 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1139 pid, uid, packageName, reason); 1140 } 1141 if (asSystemService) { 1142 mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), 1143 Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null); 1144 } else { 1145 mCb.onMediaButtonFromController(packageName, pid, uid, 1146 createMediaButtonIntent(keyEvent)); 1147 } 1148 return true; 1149 } catch (RemoteException e) { 1150 Log.e(TAG, "Remote failure in sendMediaRequest.", e); 1151 } 1152 return false; 1153 } 1154 1155 public void sendCommand(String packageName, int pid, int uid, String command, Bundle args, 1156 ResultReceiver cb) { 1157 try { 1158 mCb.onCommand(packageName, pid, uid, command, args, cb); 1159 } catch (RemoteException e) { 1160 Log.e(TAG, "Remote failure in sendCommand.", e); 1161 } 1162 } 1163 1164 public void sendCustomAction(String packageName, int pid, int uid, String action, 1165 Bundle args) { 1166 try { 1167 mCb.onCustomAction(packageName, pid, uid, action, args); 1168 } catch (RemoteException e) { 1169 Log.e(TAG, "Remote failure in sendCustomAction.", e); 1170 } 1171 } 1172 1173 public void prepare(String packageName, int pid, int uid) { 1174 try { 1175 mCb.onPrepare(packageName, pid, uid); 1176 } catch (RemoteException e) { 1177 Log.e(TAG, "Remote failure in prepare.", e); 1178 } 1179 } 1180 1181 public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId, 1182 Bundle extras) { 1183 try { 1184 mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras); 1185 } catch (RemoteException e) { 1186 Log.e(TAG, "Remote failure in prepareFromMediaId.", e); 1187 } 1188 } 1189 1190 public void prepareFromSearch(String packageName, int pid, int uid, String query, 1191 Bundle extras) { 1192 try { 1193 mCb.onPrepareFromSearch(packageName, pid, uid, query, extras); 1194 } catch (RemoteException e) { 1195 Log.e(TAG, "Remote failure in prepareFromSearch.", e); 1196 } 1197 } 1198 1199 public void prepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { 1200 try { 1201 mCb.onPrepareFromUri(packageName, pid, uid, uri, extras); 1202 } catch (RemoteException e) { 1203 Log.e(TAG, "Remote failure in prepareFromUri.", e); 1204 } 1205 } 1206 1207 public void play(String packageName, int pid, int uid) { 1208 try { 1209 mCb.onPlay(packageName, pid, uid); 1210 } catch (RemoteException e) { 1211 Log.e(TAG, "Remote failure in play.", e); 1212 } 1213 } 1214 1215 public void playFromMediaId(String packageName, int pid, int uid, String mediaId, 1216 Bundle extras) { 1217 try { 1218 mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras); 1219 } catch (RemoteException e) { 1220 Log.e(TAG, "Remote failure in playFromMediaId.", e); 1221 } 1222 } 1223 1224 public void playFromSearch(String packageName, int pid, int uid, String query, 1225 Bundle extras) { 1226 try { 1227 mCb.onPlayFromSearch(packageName, pid, uid, query, extras); 1228 } catch (RemoteException e) { 1229 Log.e(TAG, "Remote failure in playFromSearch.", e); 1230 } 1231 } 1232 1233 public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { 1234 try { 1235 mCb.onPlayFromUri(packageName, pid, uid, uri, extras); 1236 } catch (RemoteException e) { 1237 Log.e(TAG, "Remote failure in playFromUri.", e); 1238 } 1239 } 1240 1241 public void skipToTrack(String packageName, int pid, int uid, long id) { 1242 try { 1243 mCb.onSkipToTrack(packageName, pid, uid, id); 1244 } catch (RemoteException e) { 1245 Log.e(TAG, "Remote failure in skipToTrack", e); 1246 } 1247 } 1248 1249 public void pause(String packageName, int pid, int uid) { 1250 try { 1251 mCb.onPause(packageName, pid, uid); 1252 } catch (RemoteException e) { 1253 Log.e(TAG, "Remote failure in pause.", e); 1254 } 1255 } 1256 1257 public void stop(String packageName, int pid, int uid) { 1258 try { 1259 mCb.onStop(packageName, pid, uid); 1260 } catch (RemoteException e) { 1261 Log.e(TAG, "Remote failure in stop.", e); 1262 } 1263 } 1264 1265 public void next(String packageName, int pid, int uid) { 1266 try { 1267 mCb.onNext(packageName, pid, uid); 1268 } catch (RemoteException e) { 1269 Log.e(TAG, "Remote failure in next.", e); 1270 } 1271 } 1272 1273 public void previous(String packageName, int pid, int uid) { 1274 try { 1275 mCb.onPrevious(packageName, pid, uid); 1276 } catch (RemoteException e) { 1277 Log.e(TAG, "Remote failure in previous.", e); 1278 } 1279 } 1280 1281 public void fastForward(String packageName, int pid, int uid) { 1282 try { 1283 mCb.onFastForward(packageName, pid, uid); 1284 } catch (RemoteException e) { 1285 Log.e(TAG, "Remote failure in fastForward.", e); 1286 } 1287 } 1288 1289 public void rewind(String packageName, int pid, int uid) { 1290 try { 1291 mCb.onRewind(packageName, pid, uid); 1292 } catch (RemoteException e) { 1293 Log.e(TAG, "Remote failure in rewind.", e); 1294 } 1295 } 1296 1297 public void seekTo(String packageName, int pid, int uid, long pos) { 1298 try { 1299 mCb.onSeekTo(packageName, pid, uid, pos); 1300 } catch (RemoteException e) { 1301 Log.e(TAG, "Remote failure in seekTo.", e); 1302 } 1303 } 1304 1305 public void rate(String packageName, int pid, int uid, Rating rating) { 1306 try { 1307 mCb.onRate(packageName, pid, uid, rating); 1308 } catch (RemoteException e) { 1309 Log.e(TAG, "Remote failure in rate.", e); 1310 } 1311 } 1312 1313 public void setPlaybackSpeed(String packageName, int pid, int uid, float speed) { 1314 try { 1315 mCb.onSetPlaybackSpeed(packageName, pid, uid, speed); 1316 } catch (RemoteException e) { 1317 Log.e(TAG, "Remote failure in setPlaybackSpeed.", e); 1318 } 1319 } 1320 1321 public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService, 1322 int direction) { 1323 try { 1324 if (asSystemService) { 1325 mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(), 1326 Process.SYSTEM_UID, direction); 1327 } else { 1328 mCb.onAdjustVolume(packageName, pid, uid, direction); 1329 } 1330 } catch (RemoteException e) { 1331 Log.e(TAG, "Remote failure in adjustVolume.", e); 1332 } 1333 } 1334 1335 public void setVolumeTo(String packageName, int pid, int uid, int value) { 1336 try { 1337 mCb.onSetVolumeTo(packageName, pid, uid, value); 1338 } catch (RemoteException e) { 1339 Log.e(TAG, "Remote failure in setVolumeTo.", e); 1340 } 1341 } 1342 1343 private Intent createMediaButtonIntent(KeyEvent keyEvent) { 1344 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 1345 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 1346 return mediaButtonIntent; 1347 } 1348 } 1349 1350 class ControllerStub extends ISessionController.Stub { 1351 @Override 1352 public void sendCommand(String packageName, String command, Bundle args, 1353 ResultReceiver cb) { 1354 mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1355 command, args, cb); 1356 } 1357 1358 @Override 1359 public boolean sendMediaButton(String packageName, KeyEvent keyEvent) { 1360 return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(), 1361 Binder.getCallingUid(), false, keyEvent); 1362 } 1363 1364 @Override 1365 public void registerCallback(String packageName, ISessionControllerCallback cb) { 1366 synchronized (mLock) { 1367 // If this session is already destroyed tell the caller and 1368 // don't add them. 1369 if (mDestroyed) { 1370 try { 1371 cb.onSessionDestroyed(); 1372 } catch (Exception e) { 1373 // ignored 1374 } 1375 return; 1376 } 1377 if (getControllerHolderIndexForCb(cb) < 0) { 1378 mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb, 1379 packageName, Binder.getCallingUid())); 1380 if (DEBUG) { 1381 Log.d(TAG, "registering controller callback " + cb + " from controller" 1382 + packageName); 1383 } 1384 } 1385 } 1386 } 1387 1388 @Override 1389 public void unregisterCallback(ISessionControllerCallback cb) { 1390 synchronized (mLock) { 1391 int index = getControllerHolderIndexForCb(cb); 1392 if (index != -1) { 1393 mControllerCallbackHolders.remove(index); 1394 } 1395 if (DEBUG) { 1396 Log.d(TAG, "unregistering callback " + cb.asBinder()); 1397 } 1398 } 1399 } 1400 1401 @Override 1402 public String getPackageName() { 1403 return mPackageName; 1404 } 1405 1406 @Override 1407 public String getTag() { 1408 return mTag; 1409 } 1410 1411 @Override 1412 public Bundle getSessionInfo() { 1413 return mSessionInfo; 1414 } 1415 1416 @Override 1417 public PendingIntent getLaunchPendingIntent() { 1418 return mLaunchIntent; 1419 } 1420 1421 @Override 1422 public long getFlags() { 1423 return mFlags; 1424 } 1425 1426 @Override 1427 public PlaybackInfo getVolumeAttributes() { 1428 return MediaSessionRecord.this.getVolumeAttributes(); 1429 } 1430 1431 @Override 1432 public void adjustVolume(String packageName, String opPackageName, int direction, 1433 int flags) { 1434 int pid = Binder.getCallingPid(); 1435 int uid = Binder.getCallingUid(); 1436 final long token = Binder.clearCallingIdentity(); 1437 try { 1438 MediaSessionRecord.this.adjustVolume(packageName, opPackageName, pid, uid, 1439 false, direction, flags, false /* useSuggested */); 1440 } finally { 1441 Binder.restoreCallingIdentity(token); 1442 } 1443 } 1444 1445 @Override 1446 public void setVolumeTo(String packageName, String opPackageName, int value, int flags) { 1447 int pid = Binder.getCallingPid(); 1448 int uid = Binder.getCallingUid(); 1449 final long token = Binder.clearCallingIdentity(); 1450 try { 1451 MediaSessionRecord.this.setVolumeTo(packageName, opPackageName, pid, uid, value, 1452 flags); 1453 } finally { 1454 Binder.restoreCallingIdentity(token); 1455 } 1456 } 1457 1458 @Override 1459 public void prepare(String packageName) { 1460 mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1461 } 1462 1463 @Override 1464 public void prepareFromMediaId(String packageName, String mediaId, Bundle extras) { 1465 mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(), 1466 Binder.getCallingUid(), mediaId, extras); 1467 } 1468 1469 @Override 1470 public void prepareFromSearch(String packageName, String query, Bundle extras) { 1471 mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(), 1472 Binder.getCallingUid(), query, extras); 1473 } 1474 1475 @Override 1476 public void prepareFromUri(String packageName, Uri uri, Bundle extras) { 1477 mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1478 uri, extras); 1479 } 1480 1481 @Override 1482 public void play(String packageName) { 1483 mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1484 } 1485 1486 @Override 1487 public void playFromMediaId(String packageName, String mediaId, Bundle extras) { 1488 mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1489 mediaId, extras); 1490 } 1491 1492 @Override 1493 public void playFromSearch(String packageName, String query, Bundle extras) { 1494 mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1495 query, extras); 1496 } 1497 1498 @Override 1499 public void playFromUri(String packageName, Uri uri, Bundle extras) { 1500 mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1501 uri, extras); 1502 } 1503 1504 @Override 1505 public void skipToQueueItem(String packageName, long id) { 1506 mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(), id); 1507 } 1508 1509 @Override 1510 public void pause(String packageName) { 1511 mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1512 } 1513 1514 @Override 1515 public void stop(String packageName) { 1516 mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1517 } 1518 1519 @Override 1520 public void next(String packageName) { 1521 mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1522 } 1523 1524 @Override 1525 public void previous(String packageName) { 1526 mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1527 } 1528 1529 @Override 1530 public void fastForward(String packageName) { 1531 mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1532 } 1533 1534 @Override 1535 public void rewind(String packageName) { 1536 mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1537 } 1538 1539 @Override 1540 public void seekTo(String packageName, long pos) { 1541 mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), pos); 1542 } 1543 1544 @Override 1545 public void rate(String packageName, Rating rating) { 1546 mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), rating); 1547 } 1548 1549 @Override 1550 public void setPlaybackSpeed(String packageName, 1551 float speed) { 1552 mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1553 speed); 1554 } 1555 1556 @Override 1557 public void sendCustomAction(String packageName, String action, Bundle args) { 1558 mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1559 action, args); 1560 } 1561 1562 @Override 1563 public MediaMetadata getMetadata() { 1564 synchronized (mLock) { 1565 return mMetadata; 1566 } 1567 } 1568 1569 @Override 1570 public PlaybackState getPlaybackState() { 1571 return getStateWithUpdatedPosition(); 1572 } 1573 1574 @Override 1575 public ParceledListSlice getQueue() { 1576 synchronized (mLock) { 1577 return mQueue == null ? null : new ParceledListSlice<>(mQueue); 1578 } 1579 } 1580 1581 @Override 1582 public CharSequence getQueueTitle() { 1583 return mQueueTitle; 1584 } 1585 1586 @Override 1587 public Bundle getExtras() { 1588 synchronized (mLock) { 1589 return mExtras; 1590 } 1591 } 1592 1593 @Override 1594 public int getRatingType() { 1595 return mRatingType; 1596 } 1597 } 1598 1599 private class ISessionControllerCallbackHolder { 1600 private final ISessionControllerCallback mCallback; 1601 private final String mPackageName; 1602 private final int mUid; 1603 1604 ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName, 1605 int uid) { 1606 mCallback = callback; 1607 mPackageName = packageName; 1608 mUid = uid; 1609 } 1610 } 1611 1612 private class MessageHandler extends Handler { 1613 private static final int MSG_UPDATE_METADATA = 1; 1614 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 1615 private static final int MSG_UPDATE_QUEUE = 3; 1616 private static final int MSG_UPDATE_QUEUE_TITLE = 4; 1617 private static final int MSG_UPDATE_EXTRAS = 5; 1618 private static final int MSG_SEND_EVENT = 6; 1619 private static final int MSG_UPDATE_SESSION_STATE = 7; 1620 private static final int MSG_UPDATE_VOLUME = 8; 1621 private static final int MSG_DESTROYED = 9; 1622 1623 public MessageHandler(Looper looper) { 1624 super(looper); 1625 } 1626 @Override 1627 public void handleMessage(Message msg) { 1628 switch (msg.what) { 1629 case MSG_UPDATE_METADATA: 1630 pushMetadataUpdate(); 1631 break; 1632 case MSG_UPDATE_PLAYBACK_STATE: 1633 pushPlaybackStateUpdate(); 1634 break; 1635 case MSG_UPDATE_QUEUE: 1636 pushQueueUpdate(); 1637 break; 1638 case MSG_UPDATE_QUEUE_TITLE: 1639 pushQueueTitleUpdate(); 1640 break; 1641 case MSG_UPDATE_EXTRAS: 1642 pushExtrasUpdate(); 1643 break; 1644 case MSG_SEND_EVENT: 1645 pushEvent((String) msg.obj, msg.getData()); 1646 break; 1647 case MSG_UPDATE_SESSION_STATE: 1648 // TODO add session state 1649 break; 1650 case MSG_UPDATE_VOLUME: 1651 pushVolumeUpdate(); 1652 break; 1653 case MSG_DESTROYED: 1654 pushSessionDestroyed(); 1655 } 1656 } 1657 1658 public void post(int what) { 1659 post(what, null); 1660 } 1661 1662 public void post(int what, Object obj) { 1663 obtainMessage(what, obj).sendToTarget(); 1664 } 1665 1666 public void post(int what, Object obj, Bundle data) { 1667 Message msg = obtainMessage(what, obj); 1668 msg.setData(data); 1669 msg.sendToTarget(); 1670 } 1671 } 1672 } 1673