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 static android.media.VolumeProvider.VOLUME_CONTROL_ABSOLUTE; 20 import static android.media.VolumeProvider.VOLUME_CONTROL_FIXED; 21 import static android.media.VolumeProvider.VOLUME_CONTROL_RELATIVE; 22 import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL; 23 import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE; 24 25 import android.Manifest; 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.RequiresPermission; 30 import android.app.ActivityManager; 31 import android.app.ActivityManagerInternal; 32 import android.app.Notification; 33 import android.app.PendingIntent; 34 import android.app.compat.CompatChanges; 35 import android.compat.annotation.ChangeId; 36 import android.compat.annotation.EnabledSince; 37 import android.content.ComponentName; 38 import android.content.ContentProvider; 39 import android.content.ContentResolver; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.pm.PackageManager; 43 import android.content.pm.ParceledListSlice; 44 import android.content.pm.ResolveInfo; 45 import android.media.AudioAttributes; 46 import android.media.AudioManager; 47 import android.media.AudioSystem; 48 import android.media.MediaMetadata; 49 import android.media.Rating; 50 import android.media.VolumeProvider; 51 import android.media.session.ISession; 52 import android.media.session.ISessionCallback; 53 import android.media.session.ISessionController; 54 import android.media.session.ISessionControllerCallback; 55 import android.media.session.MediaController; 56 import android.media.session.MediaController.PlaybackInfo; 57 import android.media.session.MediaSession; 58 import android.media.session.MediaSession.QueueItem; 59 import android.media.session.ParcelableListBinder; 60 import android.media.session.PlaybackState; 61 import android.net.Uri; 62 import android.os.Binder; 63 import android.os.Build; 64 import android.os.Bundle; 65 import android.os.Handler; 66 import android.os.IBinder; 67 import android.os.Looper; 68 import android.os.Message; 69 import android.os.Process; 70 import android.os.RemoteException; 71 import android.os.ResultReceiver; 72 import android.os.SystemClock; 73 import android.os.UserHandle; 74 import android.text.TextUtils; 75 import android.util.EventLog; 76 import android.util.Log; 77 import android.util.Slog; 78 import android.view.KeyEvent; 79 80 import com.android.internal.annotations.GuardedBy; 81 import com.android.media.flags.Flags; 82 import com.android.server.LocalServices; 83 import com.android.server.uri.UriGrantsManagerInternal; 84 85 import java.io.PrintWriter; 86 import java.lang.annotation.Retention; 87 import java.lang.annotation.RetentionPolicy; 88 import java.util.ArrayList; 89 import java.util.Arrays; 90 import java.util.List; 91 import java.util.NoSuchElementException; 92 import java.util.Objects; 93 import java.util.concurrent.CopyOnWriteArrayList; 94 95 /** 96 * This is the system implementation of a Session. Apps will interact with the 97 * MediaSession wrapper class instead. 98 */ 99 // TODO(jaewan): Do not call service method directly -- introduce listener instead. 100 public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinder.DeathRecipient { 101 102 /** 103 * {@link android.media.session.MediaSession#setMediaButtonBroadcastReceiver( 104 * android.content.ComponentName)} throws an {@link 105 * java.lang.IllegalArgumentException} if the provided {@link android.content.ComponentName} 106 * does not resolve to a valid {@link android.content.BroadcastReceiver broadcast receiver} 107 * for apps targeting Android U and above. For apps targeting Android T and below, the request 108 * will be ignored. 109 */ 110 @ChangeId 111 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 112 static final long THROW_FOR_INVALID_BROADCAST_RECEIVER = 270049379L; 113 114 /** 115 * {@link android.media.session.MediaSession#setMediaButtonReceiver(android.app.PendingIntent)} 116 * throws an {@link java.lang.IllegalArgumentException} if the provided 117 * {@link android.app.PendingIntent} targets an {@link android.app.Activity activity} for 118 * apps targeting Android V and above. For apps targeting Android U and below, the request will 119 * be ignored. 120 */ 121 @ChangeId 122 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) 123 static final long THROW_FOR_ACTIVITY_MEDIA_BUTTON_RECEIVER = 272737196L; 124 125 private static final String TAG = "MediaSessionRecord"; 126 private static final String[] ART_URIS = new String[] { 127 MediaMetadata.METADATA_KEY_ALBUM_ART_URI, 128 MediaMetadata.METADATA_KEY_ART_URI, 129 MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI}; 130 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 131 132 /** 133 * The amount of time we'll send an assumed volume after the last volume 134 * command before reverting to the last reported volume. 135 */ 136 private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000; 137 138 /** 139 * These are states that usually indicate the user took an action and should 140 * bump priority regardless of the old state. 141 */ 142 private static final List<Integer> ALWAYS_PRIORITY_STATES = Arrays.asList( 143 PlaybackState.STATE_FAST_FORWARDING, 144 PlaybackState.STATE_REWINDING, 145 PlaybackState.STATE_SKIPPING_TO_PREVIOUS, 146 PlaybackState.STATE_SKIPPING_TO_NEXT); 147 /** 148 * These are states that usually indicate the user took an action if they 149 * were entered from a non-priority state. 150 */ 151 private static final List<Integer> TRANSITION_PRIORITY_STATES = Arrays.asList( 152 PlaybackState.STATE_BUFFERING, 153 PlaybackState.STATE_CONNECTING, 154 PlaybackState.STATE_PLAYING); 155 156 private static final AudioAttributes DEFAULT_ATTRIBUTES = 157 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(); 158 getVolumeStream(@ullable AudioAttributes attr)159 private static int getVolumeStream(@Nullable AudioAttributes attr) { 160 if (attr == null) { 161 return DEFAULT_ATTRIBUTES.getVolumeControlStream(); 162 } 163 final int stream = attr.getVolumeControlStream(); 164 if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) { 165 return DEFAULT_ATTRIBUTES.getVolumeControlStream(); 166 } 167 return stream; 168 } 169 170 private final MessageHandler mHandler; 171 172 private final int mOwnerPid; 173 private final int mOwnerUid; 174 private final int mUserId; 175 private final String mPackageName; 176 private final String mTag; 177 private final Bundle mSessionInfo; 178 private final ControllerStub mController; 179 private final MediaSession.Token mSessionToken; 180 private final SessionStub mSession; 181 private final SessionCb mSessionCb; 182 private final MediaSessionService mService; 183 private final UriGrantsManagerInternal mUgmInternal; 184 private final Context mContext; 185 186 private final Object mLock = new Object(); 187 // This field is partially guarded by mLock. Writes and non-atomic iterations (for example: 188 // index-based-iterations) must be guarded by mLock. But it is safe to acquire an iterator 189 // without acquiring mLock. 190 private final CopyOnWriteArrayList<ISessionControllerCallbackHolder> 191 mControllerCallbackHolders = new CopyOnWriteArrayList<>(); 192 193 private long mFlags; 194 private MediaButtonReceiverHolder mMediaButtonReceiverHolder; 195 private PendingIntent mLaunchIntent; 196 197 // TransportPerformer fields 198 private Bundle mExtras; 199 // Note: Avoid unparceling the bundle inside MediaMetadata since unparceling in system process 200 // may result in throwing an exception. 201 private MediaMetadata mMetadata; 202 private PlaybackState mPlaybackState; 203 private List<QueueItem> mQueue; 204 private CharSequence mQueueTitle; 205 private int mRatingType; 206 // End TransportPerformer fields 207 208 // Volume handling fields 209 private AudioAttributes mAudioAttrs; 210 private AudioManager mAudioManager; 211 private int mVolumeType = PLAYBACK_TYPE_LOCAL; 212 private int mVolumeControlType = VOLUME_CONTROL_ABSOLUTE; 213 private int mMaxVolume = 0; 214 private int mCurrentVolume = 0; 215 private int mOptimisticVolume = -1; 216 private String mVolumeControlId; 217 // End volume handling fields 218 219 private boolean mIsActive = false; 220 private boolean mDestroyed = false; 221 222 private long mDuration = -1; 223 private String mMetadataDescription; 224 225 private int mPolicies; 226 227 private final Runnable mUserEngagementTimeoutExpirationRunnable = 228 () -> { 229 synchronized (mLock) { 230 updateUserEngagedStateIfNeededLocked( 231 /* isTimeoutExpired= */ true, 232 /* isGlobalPrioritySessionActive= */ false); 233 } 234 }; 235 236 @GuardedBy("mLock") 237 private @UserEngagementState int mUserEngagementState = USER_ENGAGEMENT_UNSET; 238 239 @IntDef({ 240 USER_ENGAGEMENT_UNSET, 241 USER_PERMANENTLY_ENGAGED, 242 USER_TEMPORARILY_ENGAGED, 243 USER_DISENGAGED 244 }) 245 @Retention(RetentionPolicy.SOURCE) 246 private @interface UserEngagementState {} 247 248 /** 249 * Indicates that the {@link UserEngagementState} is not yet set. 250 * 251 * @see #updateUserEngagedStateIfNeededLocked(boolean) 252 */ 253 private static final int USER_ENGAGEMENT_UNSET = -1; 254 255 /** 256 * Indicates that the session is {@linkplain MediaSession#isActive() active} and in one of the 257 * {@linkplain PlaybackState#isActive() active states}. 258 * 259 * @see #updateUserEngagedStateIfNeededLocked(boolean) 260 */ 261 private static final int USER_PERMANENTLY_ENGAGED = 0; 262 263 /** 264 * Indicates that the session is {@linkplain MediaSession#isActive() active} and has recently 265 * switched to one of the {@linkplain PlaybackState#isActive() inactive states}. 266 * 267 * @see #updateUserEngagedStateIfNeededLocked(boolean) 268 */ 269 private static final int USER_TEMPORARILY_ENGAGED = 1; 270 271 /** 272 * Indicates that the session is either not {@linkplain MediaSession#isActive() active} or in 273 * one of the {@linkplain PlaybackState#isActive() inactive states}. 274 * 275 * @see #updateUserEngagedStateIfNeededLocked(boolean) 276 */ 277 private static final int USER_DISENGAGED = 2; 278 MediaSessionRecord( int ownerPid, int ownerUid, int userId, String ownerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo, MediaSessionService service, Looper handlerLooper, int policies)279 public MediaSessionRecord( 280 int ownerPid, 281 int ownerUid, 282 int userId, 283 String ownerPackageName, 284 ISessionCallback cb, 285 String tag, 286 Bundle sessionInfo, 287 MediaSessionService service, 288 Looper handlerLooper, 289 int policies) 290 throws RemoteException { 291 mOwnerPid = ownerPid; 292 mOwnerUid = ownerUid; 293 mUserId = userId; 294 mPackageName = ownerPackageName; 295 mTag = tag; 296 mSessionInfo = sessionInfo; 297 mController = new ControllerStub(); 298 mSessionToken = new MediaSession.Token(ownerUid, mController); 299 mSession = new SessionStub(); 300 mSessionCb = new SessionCb(cb); 301 mService = service; 302 mContext = mService.getContext(); 303 mHandler = new MessageHandler(handlerLooper); 304 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 305 mAudioAttrs = DEFAULT_ATTRIBUTES; 306 mPolicies = policies; 307 mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); 308 309 // May throw RemoteException if the session app is killed. 310 mSessionCb.mCb.asBinder().linkToDeath(this, 0); 311 } 312 313 /** 314 * Get the session binder for the {@link MediaSession}. 315 * 316 * @return The session binder apps talk to. 317 */ getSessionBinder()318 public ISession getSessionBinder() { 319 return mSession; 320 } 321 322 /** 323 * Get the session token for creating {@link MediaController}. 324 * 325 * @return The session token. 326 */ getSessionToken()327 public MediaSession.Token getSessionToken() { 328 return mSessionToken; 329 } 330 331 /** 332 * Get the info for this session. 333 * 334 * @return Info that identifies this session. 335 */ 336 @Override getPackageName()337 public String getPackageName() { 338 return mPackageName; 339 } 340 341 /** 342 * Get the intent the app set for their media button receiver. 343 * 344 * @return The pending intent set by the app or null. 345 */ getMediaButtonReceiver()346 public MediaButtonReceiverHolder getMediaButtonReceiver() { 347 return mMediaButtonReceiverHolder; 348 } 349 350 /** 351 * Get the UID this session was created for. 352 * 353 * @return The UID for this session. 354 */ 355 @Override getUid()356 public int getUid() { 357 return mOwnerUid; 358 } 359 360 /** 361 * Get the user id this session was created for. 362 * 363 * @return The user id for this session. 364 */ 365 @Override getUserId()366 public int getUserId() { 367 return mUserId; 368 } 369 370 @Override hasLinkedNotificationSupport()371 public boolean hasLinkedNotificationSupport() { 372 return true; 373 } 374 375 /** 376 * Check if this session has system priorty and should receive media buttons 377 * before any other sessions. 378 * 379 * @return True if this is a system priority session, false otherwise 380 */ 381 @Override isSystemPriority()382 public boolean isSystemPriority() { 383 return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0; 384 } 385 386 /** 387 * Send a volume adjustment to the session owner. Direction must be one of 388 * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, 389 * {@link AudioManager#ADJUST_SAME}. 390 * 391 * @param packageName The package that made the original volume request. 392 * @param opPackageName The op package that made the original volume request. 393 * @param pid The pid that made the original volume request. 394 * @param uid The uid that made the original volume request. 395 * @param asSystemService {@code true} if the event sent to the session as if it was come from 396 * the system service instead of the app process. This helps sessions to distinguish 397 * between the key injection by the app and key events from the hardware devices. 398 * Should be used only when the volume key events aren't handled by foreground 399 * activity. {@code false} otherwise to tell session about the real caller. 400 * @param direction The direction to adjust volume in. 401 * @param flags Any of the flags from {@link AudioManager}. 402 * @param useSuggested True to use adjustSuggestedStreamVolumeForUid instead of 403 * adjustStreamVolumeForUid 404 */ adjustVolume(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, int direction, int flags, boolean useSuggested)405 public void adjustVolume(String packageName, String opPackageName, int pid, int uid, 406 boolean asSystemService, int direction, int flags, boolean useSuggested) { 407 int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND; 408 if (checkPlaybackActiveState(true) || isSystemPriority()) { 409 flags &= ~AudioManager.FLAG_PLAY_SOUND; 410 } 411 if (mVolumeType == PLAYBACK_TYPE_LOCAL) { 412 // Adjust the volume with a handler not to be blocked by other system service. 413 int stream = getVolumeStream(mAudioAttrs); 414 postAdjustLocalVolume(stream, direction, flags, opPackageName, pid, uid, 415 asSystemService, useSuggested, previousFlagPlaySound); 416 } else { 417 if (mVolumeControlType == VOLUME_CONTROL_FIXED) { 418 if (DEBUG) { 419 Slog.d(TAG, "Session does not support volume adjustment"); 420 } 421 } else if (direction == AudioManager.ADJUST_TOGGLE_MUTE 422 || direction == AudioManager.ADJUST_MUTE 423 || direction == AudioManager.ADJUST_UNMUTE) { 424 Slog.w(TAG, "Muting remote playback is not supported"); 425 } else { 426 if (DEBUG) { 427 Slog.w( 428 TAG, 429 "adjusting volume, pkg=" + packageName 430 + ", asSystemService=" + asSystemService 431 + ", dir=" + direction); 432 } 433 mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction); 434 435 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 436 mOptimisticVolume = volumeBefore + direction; 437 mOptimisticVolume = Math.max(0, Math.min(mOptimisticVolume, mMaxVolume)); 438 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 439 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 440 if (volumeBefore != mOptimisticVolume) { 441 pushVolumeUpdate(); 442 } 443 444 if (DEBUG) { 445 Slog.d( 446 TAG, 447 "Adjusted optimistic volume to " + mOptimisticVolume 448 + " max is " + mMaxVolume); 449 } 450 } 451 // Always notify, even if the volume hasn't changed. This is important to ensure that 452 // System UI receives an event if a hardware volume key is pressed but the session that 453 // handles it does not allow volume adjustment. Without such an event, System UI would 454 // not show volume controls to the user. 455 mService.notifyRemoteVolumeChanged(flags, this); 456 } 457 } 458 setVolumeTo(String packageName, String opPackageName, int pid, int uid, int value, int flags)459 private void setVolumeTo(String packageName, String opPackageName, int pid, int uid, int value, 460 int flags) { 461 if (mVolumeType == PLAYBACK_TYPE_LOCAL) { 462 int stream = getVolumeStream(mAudioAttrs); 463 final int volumeValue = value; 464 mHandler.post( 465 () -> 466 setStreamVolumeForUid( 467 opPackageName, pid, uid, flags, stream, volumeValue)); 468 } else { 469 if (mVolumeControlType != VOLUME_CONTROL_ABSOLUTE) { 470 if (DEBUG) { 471 Slog.d(TAG, "Session does not support setting volume"); 472 } 473 } else { 474 value = Math.max(0, Math.min(value, mMaxVolume)); 475 mSessionCb.setVolumeTo(packageName, pid, uid, value); 476 477 int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); 478 mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume)); 479 mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); 480 mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); 481 if (volumeBefore != mOptimisticVolume) { 482 pushVolumeUpdate(); 483 } 484 485 if (DEBUG) { 486 Slog.d( 487 TAG, 488 "Set optimistic volume to " + mOptimisticVolume 489 + " max is " + mMaxVolume); 490 } 491 } 492 // Always notify, even if the volume hasn't changed. 493 mService.notifyRemoteVolumeChanged(flags, this); 494 } 495 } 496 setStreamVolumeForUid( String opPackageName, int pid, int uid, int flags, int stream, int volumeValue)497 private void setStreamVolumeForUid( 498 String opPackageName, int pid, int uid, int flags, int stream, int volumeValue) { 499 try { 500 mAudioManager.setStreamVolumeForUid( 501 stream, 502 volumeValue, 503 flags, 504 opPackageName, 505 uid, 506 pid, 507 mContext.getApplicationInfo().targetSdkVersion); 508 } catch (IllegalArgumentException | SecurityException e) { 509 Slog.e( 510 TAG, 511 "Cannot set volume: stream=" + stream 512 + ", value=" + volumeValue 513 + ", flags=" + flags, 514 e); 515 } 516 } 517 518 /** 519 * Check if this session has been set to active by the app. 520 * <p> 521 * It's not used to prioritize sessions for dispatching media keys since API 26, but still used 522 * to filter session list in MediaSessionManager#getActiveSessions(). 523 * 524 * @return True if the session is active, false otherwise. 525 */ 526 @Override isActive()527 public boolean isActive() { 528 return mIsActive && !mDestroyed; 529 } 530 531 /** 532 * Check if the session's playback active state matches with the expectation. This always return 533 * {@code false} if the playback state is {@code null}, where we cannot know the actual playback 534 * state associated with the session. 535 * 536 * @param expected True if playback is expected to be active. false otherwise. 537 * @return True if the session's playback matches with the expectation. false otherwise. 538 */ 539 @Override checkPlaybackActiveState(boolean expected)540 public boolean checkPlaybackActiveState(boolean expected) { 541 if (mPlaybackState == null) { 542 return false; 543 } 544 return mPlaybackState.isActive() == expected; 545 } 546 547 /** 548 * Get whether the playback is local. 549 * 550 * @return {@code true} if the playback is local. 551 */ 552 @Override isPlaybackTypeLocal()553 public boolean isPlaybackTypeLocal() { 554 return mVolumeType == PLAYBACK_TYPE_LOCAL; 555 } 556 557 @Override binderDied()558 public void binderDied() { 559 mService.onSessionDied(this); 560 } 561 562 /** 563 * Finish cleaning up this session, including disconnecting if connected and 564 * removing the death observer from the callback binder. 565 */ 566 @Override close()567 public void close() { 568 // Log the session's active state 569 // to measure usage of foreground service resources 570 int callingUid = Binder.getCallingUid(); 571 int callingPid = Binder.getCallingPid(); 572 LocalServices.getService(ActivityManagerInternal.class) 573 .logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK, 574 callingUid, callingPid); 575 synchronized (mLock) { 576 if (mDestroyed) { 577 return; 578 } 579 mSessionCb.mCb.asBinder().unlinkToDeath(this, 0); 580 mDestroyed = true; 581 mPlaybackState = null; 582 updateUserEngagedStateIfNeededLocked( 583 /* isTimeoutExpired= */ true, /* isGlobalPrioritySessionActive= */ false); 584 mHandler.post(MessageHandler.MSG_DESTROYED); 585 } 586 } 587 588 @Override isClosed()589 public boolean isClosed() { 590 synchronized (mLock) { 591 return mDestroyed; 592 } 593 } 594 595 @Override expireTempEngaged()596 public void expireTempEngaged() { 597 mHandler.post(mUserEngagementTimeoutExpirationRunnable); 598 } 599 600 @Override onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive)601 public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) { 602 mHandler.post( 603 () -> { 604 synchronized (mLock) { 605 if (isGlobalPrioritySessionActive) { 606 mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable); 607 } else { 608 if (mUserEngagementState == USER_TEMPORARILY_ENGAGED) { 609 mHandler.postDelayed( 610 mUserEngagementTimeoutExpirationRunnable, 611 MediaSessionDeviceConfig 612 .getMediaSessionTempUserEngagedDurationMs()); 613 } 614 } 615 } 616 }); 617 } 618 619 /** 620 * Sends media button. 621 * 622 * @param packageName caller package name 623 * @param pid caller pid 624 * @param uid caller uid 625 * @param asSystemService {@code true} if the event sent to the session as if it was come from 626 * the system service instead of the app process. 627 * @param ke key events 628 * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed. 629 * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is 630 * needed. 631 * @return {@code true} if the attempt to send media button was successfully. 632 * {@code false} otherwise. 633 */ sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent ke, int sequenceId, ResultReceiver cb)634 public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, 635 KeyEvent ke, int sequenceId, ResultReceiver cb) { 636 return mSessionCb.sendMediaButton(packageName, pid, uid, asSystemService, ke, sequenceId, 637 cb); 638 } 639 640 @Override canHandleVolumeKey()641 public boolean canHandleVolumeKey() { 642 if (isPlaybackTypeLocal()) { 643 if (DEBUG) { 644 Slog.d(TAG, "Local MediaSessionRecord can handle volume key"); 645 } 646 return true; 647 } 648 if (mVolumeControlType == VOLUME_CONTROL_FIXED) { 649 if (DEBUG) { 650 Slog.d( 651 TAG, 652 "Local MediaSessionRecord with FIXED volume control can't handle volume" 653 + " key"); 654 } 655 return false; 656 } 657 return true; 658 } 659 660 @Override isLinkedToNotification(Notification notification)661 boolean isLinkedToNotification(Notification notification) { 662 return notification.isMediaNotification() 663 && Objects.equals( 664 notification.extras.getParcelable( 665 Notification.EXTRA_MEDIA_SESSION, MediaSession.Token.class), 666 mSessionToken); 667 } 668 669 @Override getSessionPolicies()670 public int getSessionPolicies() { 671 synchronized (mLock) { 672 return mPolicies; 673 } 674 } 675 676 @Override setSessionPolicies(int policies)677 public void setSessionPolicies(int policies) { 678 synchronized (mLock) { 679 mPolicies = policies; 680 } 681 } 682 683 @Override dump(PrintWriter pw, String prefix)684 public void dump(PrintWriter pw, String prefix) { 685 pw.println(prefix + mTag + " " + this); 686 687 final String indent = prefix + " "; 688 pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid 689 + ", userId=" + mUserId); 690 pw.println(indent + "package=" + mPackageName); 691 pw.println(indent + "launchIntent=" + mLaunchIntent); 692 pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiverHolder); 693 pw.println(indent + "active=" + mIsActive); 694 pw.println(indent + "flags=" + mFlags); 695 pw.println(indent + "rating type=" + mRatingType); 696 pw.println(indent + "controllers: " + mControllerCallbackHolders.size()); 697 pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString())); 698 pw.println(indent + "audioAttrs=" + mAudioAttrs); 699 pw.append(indent) 700 .append("volumeType=") 701 .append(toVolumeTypeString(mVolumeType)) 702 .append(", controlType=") 703 .append(toVolumeControlTypeString(mVolumeControlType)) 704 .append(", max=") 705 .append(Integer.toString(mMaxVolume)) 706 .append(", current=") 707 .append(Integer.toString(mCurrentVolume)) 708 .append(", volumeControlId=") 709 .append(mVolumeControlId) 710 .println(); 711 pw.println(indent + "metadata: " + mMetadataDescription); 712 pw.println(indent + "queueTitle=" + mQueueTitle + ", size=" 713 + (mQueue == null ? 0 : mQueue.size())); 714 } 715 toVolumeControlTypeString( @olumeProvider.ControlType int volumeControlType)716 private static String toVolumeControlTypeString( 717 @VolumeProvider.ControlType int volumeControlType) { 718 return switch (volumeControlType) { 719 case VOLUME_CONTROL_FIXED -> "FIXED"; 720 case VOLUME_CONTROL_RELATIVE -> "RELATIVE"; 721 case VOLUME_CONTROL_ABSOLUTE -> "ABSOLUTE"; 722 default -> TextUtils.formatSimple("unknown(%d)", volumeControlType); 723 }; 724 } 725 toVolumeTypeString(@laybackInfo.PlaybackType int volumeType)726 private static String toVolumeTypeString(@PlaybackInfo.PlaybackType int volumeType) { 727 return switch (volumeType) { 728 case PLAYBACK_TYPE_LOCAL -> "LOCAL"; 729 case PLAYBACK_TYPE_REMOTE -> "REMOTE"; 730 default -> TextUtils.formatSimple("unknown(%d)", volumeType); 731 }; 732 } 733 734 @Override 735 public String toString() { 736 return mPackageName + "/" + mTag + "/" + getUniqueId() + " (userId=" + mUserId + ")"; 737 } 738 739 private void postAdjustLocalVolume(final int stream, final int direction, final int flags, 740 final String callingOpPackageName, final int callingPid, final int callingUid, 741 final boolean asSystemService, final boolean useSuggested, 742 final int previousFlagPlaySound) { 743 if (DEBUG) { 744 Slog.w( 745 TAG, 746 "adjusting local volume, stream=" + stream + ", dir=" + direction 747 + ", asSystemService=" + asSystemService 748 + ", useSuggested=" + useSuggested); 749 } 750 // Must use opPackageName for adjusting volumes with UID. 751 final String opPackageName; 752 final int uid; 753 final int pid; 754 if (asSystemService) { 755 opPackageName = mContext.getOpPackageName(); 756 uid = Process.SYSTEM_UID; 757 pid = Process.myPid(); 758 } else { 759 opPackageName = callingOpPackageName; 760 uid = callingUid; 761 pid = callingPid; 762 } 763 mHandler.post( 764 () -> 765 adjustSuggestedStreamVolumeForUid( 766 stream, 767 direction, 768 flags, 769 useSuggested, 770 previousFlagPlaySound, 771 opPackageName, 772 uid, 773 pid)); 774 } 775 776 private void adjustSuggestedStreamVolumeForUid( 777 int stream, 778 int direction, 779 int flags, 780 boolean useSuggested, 781 int previousFlagPlaySound, 782 String opPackageName, 783 int uid, 784 int pid) { 785 try { 786 if (useSuggested) { 787 if (AudioSystem.isStreamActive(stream, 0)) { 788 mAudioManager.adjustSuggestedStreamVolumeForUid( 789 stream, 790 direction, 791 flags, 792 opPackageName, 793 uid, 794 pid, 795 mContext.getApplicationInfo().targetSdkVersion); 796 } else { 797 mAudioManager.adjustSuggestedStreamVolumeForUid( 798 AudioManager.USE_DEFAULT_STREAM_TYPE, 799 direction, 800 flags | previousFlagPlaySound, 801 opPackageName, 802 uid, 803 pid, 804 mContext.getApplicationInfo().targetSdkVersion); 805 } 806 } else { 807 mAudioManager.adjustStreamVolumeForUid( 808 stream, 809 direction, 810 flags, 811 opPackageName, 812 uid, 813 pid, 814 mContext.getApplicationInfo().targetSdkVersion); 815 } 816 } catch (IllegalArgumentException | SecurityException e) { 817 Slog.e( 818 TAG, 819 "Cannot adjust volume: direction=" + direction 820 + ", stream=" + stream 821 + ", flags=" + flags 822 + ", opPackageName=" + opPackageName 823 + ", uid=" + uid 824 + ", useSuggested=" + useSuggested 825 + ", previousFlagPlaySound=" + previousFlagPlaySound, 826 e); 827 } 828 } 829 830 private void logCallbackException( 831 String msg, ISessionControllerCallbackHolder holder, Exception e) { 832 Slog.v( 833 TAG, 834 msg + ", this=" + this + ", callback package=" + holder.mPackageName 835 + ", exception=" + e); 836 } 837 838 private void pushPlaybackStateUpdate() { 839 PlaybackState playbackState; 840 synchronized (mLock) { 841 if (mDestroyed) { 842 return; 843 } 844 playbackState = mPlaybackState; 845 } 846 performOnCallbackHolders( 847 "pushPlaybackStateUpdate", 848 holder -> holder.mCallback.onPlaybackStateChanged(playbackState)); 849 } 850 851 private void pushMetadataUpdate() { 852 MediaMetadata metadata; 853 synchronized (mLock) { 854 if (mDestroyed) { 855 return; 856 } 857 metadata = mMetadata; 858 } 859 performOnCallbackHolders( 860 "pushMetadataUpdate", holder -> holder.mCallback.onMetadataChanged(metadata)); 861 } 862 863 private void pushQueueUpdate() { 864 ArrayList<QueueItem> toSend; 865 synchronized (mLock) { 866 if (mDestroyed) { 867 return; 868 } 869 toSend = mQueue == null ? null : new ArrayList<>(mQueue); 870 } 871 performOnCallbackHolders( 872 "pushQueueUpdate", 873 holder -> { 874 ParceledListSlice<QueueItem> parcelableQueue = null; 875 if (toSend != null) { 876 parcelableQueue = new ParceledListSlice<>(toSend); 877 // Limit the size of initial Parcel to prevent binder buffer overflow 878 // as onQueueChanged is an async binder call. 879 parcelableQueue.setInlineCountLimit(1); 880 } 881 holder.mCallback.onQueueChanged(parcelableQueue); 882 }); 883 } 884 885 private void pushQueueTitleUpdate() { 886 CharSequence queueTitle; 887 synchronized (mLock) { 888 if (mDestroyed) { 889 return; 890 } 891 queueTitle = mQueueTitle; 892 } 893 performOnCallbackHolders( 894 "pushQueueTitleUpdate", holder -> holder.mCallback.onQueueTitleChanged(queueTitle)); 895 } 896 897 private void pushExtrasUpdate() { 898 Bundle extras; 899 synchronized (mLock) { 900 if (mDestroyed) { 901 return; 902 } 903 extras = mExtras; 904 } 905 performOnCallbackHolders( 906 "pushExtrasUpdate", holder -> holder.mCallback.onExtrasChanged(extras)); 907 } 908 909 private void pushVolumeUpdate() { 910 PlaybackInfo info; 911 synchronized (mLock) { 912 if (mDestroyed) { 913 return; 914 } 915 info = getVolumeAttributes(); 916 } 917 performOnCallbackHolders( 918 "pushVolumeUpdate", holder -> holder.mCallback.onVolumeInfoChanged(info)); 919 } 920 921 private void pushEvent(String event, Bundle data) { 922 synchronized (mLock) { 923 if (mDestroyed) { 924 return; 925 } 926 } 927 performOnCallbackHolders("pushEvent", holder -> holder.mCallback.onEvent(event, data)); 928 } 929 930 private void pushSessionDestroyed() { 931 synchronized (mLock) { 932 // This is the only method that may be (and can only be) called 933 // after the session is destroyed. 934 if (!mDestroyed) { 935 return; 936 } 937 } 938 performOnCallbackHolders( 939 "pushSessionDestroyed", 940 holder -> { 941 holder.mCallback.asBinder().unlinkToDeath(holder.mDeathMonitor, 0); 942 holder.mCallback.onSessionDestroyed(); 943 }); 944 // After notifying clear all listeners 945 synchronized (mLock) { 946 mControllerCallbackHolders.clear(); 947 } 948 } 949 950 private interface ControllerCallbackCall { 951 952 void performOn(ISessionControllerCallbackHolder holder) throws RemoteException; 953 } 954 955 private void performOnCallbackHolders(String operationName, ControllerCallbackCall call) { 956 ArrayList<ISessionControllerCallbackHolder> deadCallbackHolders = new ArrayList<>(); 957 for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { 958 try { 959 call.performOn(holder); 960 } catch (RemoteException | NoSuchElementException exception) { 961 deadCallbackHolders.add(holder); 962 logCallbackException( 963 "Exception while executing: " + operationName, holder, exception); 964 } 965 } 966 synchronized (mLock) { 967 mControllerCallbackHolders.removeAll(deadCallbackHolders); 968 } 969 } 970 971 private PlaybackState getStateWithUpdatedPosition() { 972 PlaybackState state; 973 long duration; 974 synchronized (mLock) { 975 if (mDestroyed) { 976 return null; 977 } 978 state = mPlaybackState; 979 duration = mDuration; 980 } 981 PlaybackState result = null; 982 if (state != null) { 983 if (state.getState() == PlaybackState.STATE_PLAYING 984 || state.getState() == PlaybackState.STATE_FAST_FORWARDING 985 || state.getState() == PlaybackState.STATE_REWINDING) { 986 long updateTime = state.getLastPositionUpdateTime(); 987 long currentTime = SystemClock.elapsedRealtime(); 988 if (updateTime > 0) { 989 long position = (long) (state.getPlaybackSpeed() 990 * (currentTime - updateTime)) + state.getPosition(); 991 if (duration >= 0 && position > duration) { 992 position = duration; 993 } else if (position < 0) { 994 position = 0; 995 } 996 PlaybackState.Builder builder = new PlaybackState.Builder(state); 997 builder.setState(state.getState(), position, state.getPlaybackSpeed(), 998 currentTime); 999 result = builder.build(); 1000 } 1001 } 1002 } 1003 return result == null ? state : result; 1004 } 1005 1006 private int getControllerHolderIndexForCb(ISessionControllerCallback cb) { 1007 IBinder binder = cb.asBinder(); 1008 for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { 1009 if (binder.equals(mControllerCallbackHolders.get(i).mCallback.asBinder())) { 1010 return i; 1011 } 1012 } 1013 return -1; 1014 } 1015 1016 @NonNull 1017 private PlaybackInfo getVolumeAttributes() { 1018 int volumeType; 1019 AudioAttributes attributes; 1020 synchronized (mLock) { 1021 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 1022 int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume; 1023 return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current, 1024 mAudioAttrs, mVolumeControlId); 1025 } 1026 volumeType = mVolumeType; 1027 attributes = mAudioAttrs; 1028 } 1029 int stream = getVolumeStream(attributes); 1030 int max = mAudioManager.getStreamMaxVolume(stream); 1031 int current = mAudioManager.getStreamVolume(stream); 1032 return new PlaybackInfo( 1033 volumeType, VOLUME_CONTROL_ABSOLUTE, max, current, attributes, null); 1034 } 1035 1036 private final Runnable mClearOptimisticVolumeRunnable = 1037 () -> { 1038 boolean needUpdate = (mOptimisticVolume != mCurrentVolume); 1039 mOptimisticVolume = -1; 1040 if (needUpdate) { 1041 pushVolumeUpdate(); 1042 } 1043 }; 1044 1045 @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) 1046 private static boolean componentNameExists( 1047 @NonNull ComponentName componentName, @NonNull Context context, int userId) { 1048 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 1049 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1050 mediaButtonIntent.setComponent(componentName); 1051 1052 UserHandle userHandle = UserHandle.of(userId); 1053 PackageManager pm = context.getPackageManager(); 1054 1055 List<ResolveInfo> resolveInfos = 1056 pm.queryBroadcastReceiversAsUser( 1057 mediaButtonIntent, PackageManager.ResolveInfoFlags.of(0), userHandle); 1058 return !resolveInfos.isEmpty(); 1059 } 1060 1061 @GuardedBy("mLock") 1062 private void updateUserEngagedStateIfNeededLocked( 1063 boolean isTimeoutExpired, boolean isGlobalPrioritySessionActive) { 1064 if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { 1065 return; 1066 } 1067 int oldUserEngagedState = mUserEngagementState; 1068 int newUserEngagedState; 1069 if (isActive() && mPlaybackState != null && mPlaybackState.isActive()) { 1070 newUserEngagedState = USER_PERMANENTLY_ENGAGED; 1071 } else if (oldUserEngagedState == USER_PERMANENTLY_ENGAGED 1072 || oldUserEngagedState == USER_ENGAGEMENT_UNSET 1073 || (oldUserEngagedState == USER_TEMPORARILY_ENGAGED && !isTimeoutExpired)) { 1074 newUserEngagedState = USER_TEMPORARILY_ENGAGED; 1075 } else { 1076 newUserEngagedState = USER_DISENGAGED; 1077 } 1078 if (oldUserEngagedState == newUserEngagedState) { 1079 return; 1080 } 1081 1082 mUserEngagementState = newUserEngagedState; 1083 if (newUserEngagedState == USER_TEMPORARILY_ENGAGED && !isGlobalPrioritySessionActive) { 1084 mHandler.postDelayed( 1085 mUserEngagementTimeoutExpirationRunnable, 1086 MediaSessionDeviceConfig.getMediaSessionTempUserEngagedDurationMs()); 1087 } else { 1088 mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable); 1089 } 1090 1091 boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED; 1092 boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED; 1093 if (oldUserEngagedState == USER_ENGAGEMENT_UNSET || wasUserEngaged != isNowUserEngaged) { 1094 mHandler.post( 1095 () -> 1096 mService.onSessionUserEngagementStateChange( 1097 /* mediaSessionRecord= */ this, 1098 /* isUserEngaged= */ isNowUserEngaged)); 1099 } 1100 } 1101 1102 private final class SessionStub extends ISession.Stub { 1103 @Override 1104 public void destroySession() throws RemoteException { 1105 final long token = Binder.clearCallingIdentity(); 1106 try { 1107 mService.onSessionDied(MediaSessionRecord.this); 1108 } finally { 1109 Binder.restoreCallingIdentity(token); 1110 } 1111 } 1112 1113 @Override 1114 public void sendEvent(String event, Bundle data) throws RemoteException { 1115 mHandler.post(MessageHandler.MSG_SEND_EVENT, event, 1116 data == null ? null : new Bundle(data)); 1117 } 1118 1119 @Override 1120 public ISessionController getController() throws RemoteException { 1121 return mController; 1122 } 1123 1124 @Override 1125 public void setActive(boolean active) throws RemoteException { 1126 // Log the session's active state 1127 // to measure usage of foreground service resources 1128 int callingUid = Binder.getCallingUid(); 1129 int callingPid = Binder.getCallingPid(); 1130 if (active) { 1131 LocalServices.getService(ActivityManagerInternal.class) 1132 .logFgsApiBegin(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK, 1133 callingUid, callingPid); 1134 } else { 1135 LocalServices.getService(ActivityManagerInternal.class) 1136 .logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK, 1137 callingUid, callingPid); 1138 } 1139 boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive(); 1140 synchronized (mLock) { 1141 mIsActive = active; 1142 updateUserEngagedStateIfNeededLocked( 1143 /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive); 1144 } 1145 long token = Binder.clearCallingIdentity(); 1146 try { 1147 mService.onSessionActiveStateChanged(MediaSessionRecord.this, mPlaybackState); 1148 } finally { 1149 Binder.restoreCallingIdentity(token); 1150 } 1151 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 1152 } 1153 1154 @Override 1155 public void setFlags(int flags) throws RemoteException { 1156 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 1157 int pid = Binder.getCallingPid(); 1158 int uid = Binder.getCallingUid(); 1159 mService.enforcePhoneStatePermission(pid, uid); 1160 } 1161 mFlags = flags; 1162 if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 1163 final long token = Binder.clearCallingIdentity(); 1164 try { 1165 mService.setGlobalPrioritySession(MediaSessionRecord.this); 1166 } finally { 1167 Binder.restoreCallingIdentity(token); 1168 } 1169 } 1170 mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); 1171 } 1172 1173 @Override 1174 public void setMediaButtonReceiver(@Nullable PendingIntent pi) throws RemoteException { 1175 final int uid = Binder.getCallingUid(); 1176 final long token = Binder.clearCallingIdentity(); 1177 try { 1178 if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) 1179 != 0) { 1180 return; 1181 } 1182 1183 if (pi != null && pi.isActivity()) { 1184 if (CompatChanges.isChangeEnabled( 1185 THROW_FOR_ACTIVITY_MEDIA_BUTTON_RECEIVER, uid)) { 1186 throw new IllegalArgumentException( 1187 "The media button receiver cannot be set to an activity."); 1188 } else { 1189 Slog.w( 1190 TAG, 1191 "Ignoring invalid media button receiver targeting an activity."); 1192 return; 1193 } 1194 } 1195 1196 mMediaButtonReceiverHolder = 1197 MediaButtonReceiverHolder.create(mUserId, pi, mPackageName); 1198 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); 1199 } finally { 1200 Binder.restoreCallingIdentity(token); 1201 } 1202 } 1203 1204 @Override 1205 @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) 1206 public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException { 1207 final int uid = Binder.getCallingUid(); 1208 final long token = Binder.clearCallingIdentity(); 1209 try { 1210 //mPackageName has been verified in MediaSessionService.enforcePackageName(). 1211 if (receiver != null && !TextUtils.equals( 1212 mPackageName, receiver.getPackageName())) { 1213 EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet logging. 1214 throw new IllegalArgumentException("receiver does not belong to " 1215 + "package name provided to MediaSessionRecord. Pkg = " + mPackageName 1216 + ", Receiver Pkg = " + receiver.getPackageName()); 1217 } 1218 if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) 1219 != 0) { 1220 return; 1221 } 1222 1223 if (!componentNameExists(receiver, mContext, mUserId)) { 1224 if (CompatChanges.isChangeEnabled(THROW_FOR_INVALID_BROADCAST_RECEIVER, uid)) { 1225 throw new IllegalArgumentException("Invalid component name: " + receiver); 1226 } else { 1227 Slog.w( 1228 TAG, 1229 "setMediaButtonBroadcastReceiver(): " 1230 + "Ignoring invalid component name=" 1231 + receiver); 1232 } 1233 return; 1234 } 1235 1236 mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver); 1237 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); 1238 } finally { 1239 Binder.restoreCallingIdentity(token); 1240 } 1241 } 1242 1243 @Override 1244 public void setLaunchPendingIntent(PendingIntent pi) throws RemoteException { 1245 mLaunchIntent = pi; 1246 } 1247 1248 @Override 1249 public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) 1250 throws RemoteException { 1251 synchronized (mLock) { 1252 mDuration = duration; 1253 mMetadataDescription = metadataDescription; 1254 mMetadata = sanitizeMediaMetadata(metadata); 1255 } 1256 mHandler.post(MessageHandler.MSG_UPDATE_METADATA); 1257 } 1258 1259 private MediaMetadata sanitizeMediaMetadata(MediaMetadata metadata) { 1260 if (metadata == null) { 1261 return null; 1262 } 1263 MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(metadata); 1264 for (String key: ART_URIS) { 1265 String uriString = metadata.getString(key); 1266 if (TextUtils.isEmpty(uriString)) { 1267 continue; 1268 } 1269 Uri uri = Uri.parse(uriString); 1270 if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { 1271 continue; 1272 } 1273 try { 1274 mUgmInternal.checkGrantUriPermission(getUid(), 1275 getPackageName(), 1276 ContentProvider.getUriWithoutUserId(uri), 1277 Intent.FLAG_GRANT_READ_URI_PERMISSION, 1278 ContentProvider.getUserIdFromUri(uri, getUserId())); 1279 } catch (SecurityException e) { 1280 metadataBuilder.putString(key, null); 1281 } 1282 } 1283 MediaMetadata sanitizedMetadata = metadataBuilder.build(); 1284 // sanitizedMetadata.size() guarantees that the underlying bundle is unparceled 1285 // before we set it to prevent concurrent reads from throwing an 1286 // exception 1287 sanitizedMetadata.size(); 1288 return sanitizedMetadata; 1289 } 1290 1291 @Override 1292 public void setPlaybackState(PlaybackState state) throws RemoteException { 1293 int oldState = mPlaybackState == null 1294 ? PlaybackState.STATE_NONE : mPlaybackState.getState(); 1295 int newState = state == null 1296 ? PlaybackState.STATE_NONE : state.getState(); 1297 boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState) 1298 || (!TRANSITION_PRIORITY_STATES.contains(oldState) 1299 && TRANSITION_PRIORITY_STATES.contains(newState)); 1300 boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive(); 1301 synchronized (mLock) { 1302 mPlaybackState = state; 1303 updateUserEngagedStateIfNeededLocked( 1304 /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive); 1305 } 1306 final long token = Binder.clearCallingIdentity(); 1307 try { 1308 mService.onSessionPlaybackStateChanged( 1309 MediaSessionRecord.this, shouldUpdatePriority, mPlaybackState); 1310 } finally { 1311 Binder.restoreCallingIdentity(token); 1312 } 1313 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); 1314 } 1315 1316 @Override 1317 public void resetQueue() throws RemoteException { 1318 synchronized (mLock) { 1319 mQueue = null; 1320 } 1321 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); 1322 } 1323 1324 @Override 1325 public IBinder getBinderForSetQueue() throws RemoteException { 1326 return new ParcelableListBinder<QueueItem>( 1327 QueueItem.class, 1328 (list) -> { 1329 synchronized (mLock) { 1330 mQueue = list; 1331 } 1332 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); 1333 }); 1334 } 1335 1336 @Override 1337 public void setQueueTitle(CharSequence title) throws RemoteException { 1338 mQueueTitle = title; 1339 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE); 1340 } 1341 1342 @Override 1343 public void setExtras(Bundle extras) throws RemoteException { 1344 synchronized (mLock) { 1345 mExtras = extras == null ? null : new Bundle(extras); 1346 } 1347 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS); 1348 } 1349 1350 @Override 1351 public void setRatingType(int type) throws RemoteException { 1352 mRatingType = type; 1353 } 1354 1355 @Override 1356 public void setCurrentVolume(int volume) throws RemoteException { 1357 mCurrentVolume = volume; 1358 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 1359 } 1360 1361 @Override 1362 public void setPlaybackToLocal(AudioAttributes attributes) throws RemoteException { 1363 boolean typeChanged; 1364 synchronized (mLock) { 1365 typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE; 1366 mVolumeType = PLAYBACK_TYPE_LOCAL; 1367 mVolumeControlId = null; 1368 if (attributes != null) { 1369 mAudioAttrs = attributes; 1370 } else { 1371 Slog.e(TAG, "Received null audio attributes, using existing attributes"); 1372 } 1373 } 1374 if (typeChanged) { 1375 final long token = Binder.clearCallingIdentity(); 1376 try { 1377 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 1378 } finally { 1379 Binder.restoreCallingIdentity(token); 1380 } 1381 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 1382 } 1383 } 1384 1385 @Override 1386 public void setPlaybackToRemote(int control, int max, String controlId) 1387 throws RemoteException { 1388 boolean typeChanged; 1389 synchronized (mLock) { 1390 typeChanged = mVolumeType == PLAYBACK_TYPE_LOCAL; 1391 mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE; 1392 mVolumeControlType = control; 1393 mMaxVolume = max; 1394 mVolumeControlId = controlId; 1395 } 1396 if (typeChanged) { 1397 final long token = Binder.clearCallingIdentity(); 1398 try { 1399 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); 1400 } finally { 1401 Binder.restoreCallingIdentity(token); 1402 } 1403 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); 1404 } 1405 } 1406 } 1407 1408 class SessionCb { 1409 private final ISessionCallback mCb; 1410 1411 SessionCb(ISessionCallback cb) { 1412 mCb = cb; 1413 } 1414 1415 public boolean sendMediaButton(String packageName, int pid, int uid, 1416 boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { 1417 try { 1418 if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { 1419 final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction()) 1420 + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode()); 1421 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1422 pid, uid, packageName, reason); 1423 } 1424 if (asSystemService) { 1425 mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), 1426 Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb); 1427 } else { 1428 mCb.onMediaButton(packageName, pid, uid, 1429 createMediaButtonIntent(keyEvent), sequenceId, cb); 1430 } 1431 return true; 1432 } catch (RemoteException e) { 1433 Slog.e(TAG, "Remote failure in sendMediaRequest.", e); 1434 } 1435 return false; 1436 } 1437 1438 public boolean sendMediaButton(String packageName, int pid, int uid, 1439 boolean asSystemService, KeyEvent keyEvent) { 1440 try { 1441 if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { 1442 final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction()) 1443 + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode()); 1444 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1445 pid, uid, packageName, reason); 1446 } 1447 if (asSystemService) { 1448 mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), 1449 Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null); 1450 } else { 1451 mCb.onMediaButtonFromController(packageName, pid, uid, 1452 createMediaButtonIntent(keyEvent)); 1453 } 1454 return true; 1455 } catch (RemoteException e) { 1456 Slog.e(TAG, "Remote failure in sendMediaRequest.", e); 1457 } 1458 return false; 1459 } 1460 1461 public void sendCommand(String packageName, int pid, int uid, String command, Bundle args, 1462 ResultReceiver cb) { 1463 try { 1464 final String reason = TAG + ":" + command; 1465 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1466 pid, uid, packageName, reason); 1467 mCb.onCommand(packageName, pid, uid, command, args, cb); 1468 } catch (RemoteException e) { 1469 Slog.e(TAG, "Remote failure in sendCommand.", e); 1470 } 1471 } 1472 1473 public void sendCustomAction(String packageName, int pid, int uid, String action, 1474 Bundle args) { 1475 try { 1476 final String reason = TAG + ":custom-" + action; 1477 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1478 pid, uid, packageName, reason); 1479 mCb.onCustomAction(packageName, pid, uid, action, args); 1480 } catch (RemoteException e) { 1481 Slog.e(TAG, "Remote failure in sendCustomAction.", e); 1482 } 1483 } 1484 1485 public void prepare(String packageName, int pid, int uid) { 1486 try { 1487 final String reason = TAG + ":prepare"; 1488 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1489 pid, uid, packageName, reason); 1490 mCb.onPrepare(packageName, pid, uid); 1491 } catch (RemoteException e) { 1492 Slog.e(TAG, "Remote failure in prepare.", e); 1493 } 1494 } 1495 1496 public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId, 1497 Bundle extras) { 1498 try { 1499 final String reason = TAG + ":prepareFromMediaId"; 1500 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1501 pid, uid, packageName, reason); 1502 mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras); 1503 } catch (RemoteException e) { 1504 Slog.e(TAG, "Remote failure in prepareFromMediaId.", e); 1505 } 1506 } 1507 1508 public void prepareFromSearch(String packageName, int pid, int uid, String query, 1509 Bundle extras) { 1510 try { 1511 final String reason = TAG + ":prepareFromSearch"; 1512 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1513 pid, uid, packageName, reason); 1514 mCb.onPrepareFromSearch(packageName, pid, uid, query, extras); 1515 } catch (RemoteException e) { 1516 Slog.e(TAG, "Remote failure in prepareFromSearch.", e); 1517 } 1518 } 1519 1520 public void prepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { 1521 try { 1522 final String reason = TAG + ":prepareFromUri"; 1523 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1524 pid, uid, packageName, reason); 1525 mCb.onPrepareFromUri(packageName, pid, uid, uri, extras); 1526 } catch (RemoteException e) { 1527 Slog.e(TAG, "Remote failure in prepareFromUri.", e); 1528 } 1529 } 1530 1531 public void play(String packageName, int pid, int uid) { 1532 try { 1533 final String reason = TAG + ":play"; 1534 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1535 pid, uid, packageName, reason); 1536 mCb.onPlay(packageName, pid, uid); 1537 } catch (RemoteException e) { 1538 Slog.e(TAG, "Remote failure in play.", e); 1539 } 1540 } 1541 1542 public void playFromMediaId(String packageName, int pid, int uid, String mediaId, 1543 Bundle extras) { 1544 try { 1545 final String reason = TAG + ":playFromMediaId"; 1546 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1547 pid, uid, packageName, reason); 1548 mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras); 1549 } catch (RemoteException e) { 1550 Slog.e(TAG, "Remote failure in playFromMediaId.", e); 1551 } 1552 } 1553 1554 public void playFromSearch(String packageName, int pid, int uid, String query, 1555 Bundle extras) { 1556 try { 1557 final String reason = TAG + ":playFromSearch"; 1558 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1559 pid, uid, packageName, reason); 1560 mCb.onPlayFromSearch(packageName, pid, uid, query, extras); 1561 } catch (RemoteException e) { 1562 Slog.e(TAG, "Remote failure in playFromSearch.", e); 1563 } 1564 } 1565 1566 public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { 1567 try { 1568 final String reason = TAG + ":playFromUri"; 1569 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1570 pid, uid, packageName, reason); 1571 mCb.onPlayFromUri(packageName, pid, uid, uri, extras); 1572 } catch (RemoteException e) { 1573 Slog.e(TAG, "Remote failure in playFromUri.", e); 1574 } 1575 } 1576 1577 public void skipToTrack(String packageName, int pid, int uid, long id) { 1578 try { 1579 final String reason = TAG + ":skipToTrack"; 1580 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1581 pid, uid, packageName, reason); 1582 mCb.onSkipToTrack(packageName, pid, uid, id); 1583 } catch (RemoteException e) { 1584 Slog.e(TAG, "Remote failure in skipToTrack", e); 1585 } 1586 } 1587 1588 public void pause(String packageName, int pid, int uid) { 1589 try { 1590 final String reason = TAG + ":pause"; 1591 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1592 pid, uid, packageName, reason); 1593 mCb.onPause(packageName, pid, uid); 1594 } catch (RemoteException e) { 1595 Slog.e(TAG, "Remote failure in pause.", e); 1596 } 1597 } 1598 1599 public void stop(String packageName, int pid, int uid) { 1600 try { 1601 final String reason = TAG + ":stop"; 1602 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1603 pid, uid, packageName, reason); 1604 mCb.onStop(packageName, pid, uid); 1605 } catch (RemoteException e) { 1606 Slog.e(TAG, "Remote failure in stop.", e); 1607 } 1608 } 1609 1610 public void next(String packageName, int pid, int uid) { 1611 try { 1612 final String reason = TAG + ":next"; 1613 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1614 pid, uid, packageName, reason); 1615 mCb.onNext(packageName, pid, uid); 1616 } catch (RemoteException e) { 1617 Slog.e(TAG, "Remote failure in next.", e); 1618 } 1619 } 1620 1621 public void previous(String packageName, int pid, int uid) { 1622 try { 1623 final String reason = TAG + ":previous"; 1624 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1625 pid, uid, packageName, reason); 1626 mCb.onPrevious(packageName, pid, uid); 1627 } catch (RemoteException e) { 1628 Slog.e(TAG, "Remote failure in previous.", e); 1629 } 1630 } 1631 1632 public void fastForward(String packageName, int pid, int uid) { 1633 try { 1634 final String reason = TAG + ":fastForward"; 1635 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1636 pid, uid, packageName, reason); 1637 mCb.onFastForward(packageName, pid, uid); 1638 } catch (RemoteException e) { 1639 Slog.e(TAG, "Remote failure in fastForward.", e); 1640 } 1641 } 1642 1643 public void rewind(String packageName, int pid, int uid) { 1644 try { 1645 final String reason = TAG + ":rewind"; 1646 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1647 pid, uid, packageName, reason); 1648 mCb.onRewind(packageName, pid, uid); 1649 } catch (RemoteException e) { 1650 Slog.e(TAG, "Remote failure in rewind.", e); 1651 } 1652 } 1653 1654 public void seekTo(String packageName, int pid, int uid, long pos) { 1655 try { 1656 final String reason = TAG + ":seekTo"; 1657 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1658 pid, uid, packageName, reason); 1659 mCb.onSeekTo(packageName, pid, uid, pos); 1660 } catch (RemoteException e) { 1661 Slog.e(TAG, "Remote failure in seekTo.", e); 1662 } 1663 } 1664 1665 public void rate(String packageName, int pid, int uid, Rating rating) { 1666 try { 1667 final String reason = TAG + ":rate"; 1668 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1669 pid, uid, packageName, reason); 1670 mCb.onRate(packageName, pid, uid, rating); 1671 } catch (RemoteException e) { 1672 Slog.e(TAG, "Remote failure in rate.", e); 1673 } 1674 } 1675 1676 public void setPlaybackSpeed(String packageName, int pid, int uid, float speed) { 1677 try { 1678 final String reason = TAG + ":setPlaybackSpeed"; 1679 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1680 pid, uid, packageName, reason); 1681 mCb.onSetPlaybackSpeed(packageName, pid, uid, speed); 1682 } catch (RemoteException e) { 1683 Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e); 1684 } 1685 } 1686 1687 public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService, 1688 int direction) { 1689 try { 1690 final String reason = TAG + ":adjustVolume"; 1691 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1692 pid, uid, packageName, reason); 1693 if (asSystemService) { 1694 mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(), 1695 Process.SYSTEM_UID, direction); 1696 } else { 1697 mCb.onAdjustVolume(packageName, pid, uid, direction); 1698 } 1699 } catch (RemoteException e) { 1700 Slog.e(TAG, "Remote failure in adjustVolume.", e); 1701 } 1702 } 1703 1704 public void setVolumeTo(String packageName, int pid, int uid, int value) { 1705 try { 1706 final String reason = TAG + ":setVolumeTo"; 1707 mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), 1708 pid, uid, packageName, reason); 1709 mCb.onSetVolumeTo(packageName, pid, uid, value); 1710 } catch (RemoteException e) { 1711 Slog.e(TAG, "Remote failure in setVolumeTo.", e); 1712 } 1713 } 1714 1715 private Intent createMediaButtonIntent(KeyEvent keyEvent) { 1716 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 1717 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 1718 return mediaButtonIntent; 1719 } 1720 } 1721 1722 class ControllerStub extends ISessionController.Stub { 1723 @Override 1724 public void sendCommand(String packageName, String command, Bundle args, 1725 ResultReceiver cb) { 1726 mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1727 command, args, cb); 1728 } 1729 1730 @Override 1731 public boolean sendMediaButton(String packageName, KeyEvent keyEvent) { 1732 return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(), 1733 Binder.getCallingUid(), false, keyEvent); 1734 } 1735 1736 @Override 1737 public void registerCallback(String packageName, ISessionControllerCallback cb) { 1738 synchronized (mLock) { 1739 // If this session is already destroyed tell the caller and 1740 // don't add them. 1741 if (mDestroyed) { 1742 try { 1743 cb.onSessionDestroyed(); 1744 } catch (Exception e) { 1745 // ignored 1746 } 1747 return; 1748 } 1749 if (getControllerHolderIndexForCb(cb) < 0) { 1750 ISessionControllerCallbackHolder holder = new ISessionControllerCallbackHolder( 1751 cb, packageName, Binder.getCallingUid(), () -> unregisterCallback(cb)); 1752 mControllerCallbackHolders.add(holder); 1753 if (DEBUG) { 1754 Slog.d( 1755 TAG, 1756 "registering controller callback " + cb 1757 + " from controller" + packageName); 1758 } 1759 // Avoid callback leaks 1760 try { 1761 // cb is not referenced outside of the MediaSessionRecord, so the death 1762 // handler won't prevent MediaSessionRecord to be garbage collected. 1763 cb.asBinder().linkToDeath(holder.mDeathMonitor, 0); 1764 } catch (RemoteException e) { 1765 unregisterCallback(cb); 1766 Slog.w(TAG, "registerCallback failed to linkToDeath", e); 1767 } 1768 } 1769 } 1770 } 1771 1772 @Override 1773 public void unregisterCallback(ISessionControllerCallback cb) { 1774 synchronized (mLock) { 1775 int index = getControllerHolderIndexForCb(cb); 1776 if (index != -1) { 1777 try { 1778 cb.asBinder().unlinkToDeath( 1779 mControllerCallbackHolders.get(index).mDeathMonitor, 0); 1780 } catch (NoSuchElementException e) { 1781 Slog.w(TAG, "error unlinking to binder death", e); 1782 } 1783 mControllerCallbackHolders.remove(index); 1784 } 1785 if (DEBUG) { 1786 Slog.d(TAG, "unregistering callback " + cb.asBinder()); 1787 } 1788 } 1789 } 1790 1791 @Override 1792 public String getPackageName() { 1793 return mPackageName; 1794 } 1795 1796 @Override 1797 public String getTag() { 1798 return mTag; 1799 } 1800 1801 @Override 1802 public Bundle getSessionInfo() { 1803 return mSessionInfo; 1804 } 1805 1806 @Override 1807 public PendingIntent getLaunchPendingIntent() { 1808 return mLaunchIntent; 1809 } 1810 1811 @Override 1812 public long getFlags() { 1813 return mFlags; 1814 } 1815 1816 @NonNull 1817 @Override 1818 public PlaybackInfo getVolumeAttributes() { 1819 return MediaSessionRecord.this.getVolumeAttributes(); 1820 } 1821 1822 @Override 1823 public void adjustVolume(String packageName, String opPackageName, int direction, 1824 int flags) { 1825 int pid = Binder.getCallingPid(); 1826 int uid = Binder.getCallingUid(); 1827 final long token = Binder.clearCallingIdentity(); 1828 try { 1829 MediaSessionRecord.this.adjustVolume(packageName, opPackageName, pid, uid, 1830 false, direction, flags, false /* useSuggested */); 1831 } finally { 1832 Binder.restoreCallingIdentity(token); 1833 } 1834 } 1835 1836 @Override 1837 public void setVolumeTo(String packageName, String opPackageName, int value, int flags) { 1838 int pid = Binder.getCallingPid(); 1839 int uid = Binder.getCallingUid(); 1840 final long token = Binder.clearCallingIdentity(); 1841 try { 1842 MediaSessionRecord.this.setVolumeTo(packageName, opPackageName, pid, uid, value, 1843 flags); 1844 } finally { 1845 Binder.restoreCallingIdentity(token); 1846 } 1847 } 1848 1849 @Override 1850 public void prepare(String packageName) { 1851 mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1852 } 1853 1854 @Override 1855 public void prepareFromMediaId(String packageName, String mediaId, Bundle extras) { 1856 mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(), 1857 Binder.getCallingUid(), mediaId, extras); 1858 } 1859 1860 @Override 1861 public void prepareFromSearch(String packageName, String query, Bundle extras) { 1862 mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(), 1863 Binder.getCallingUid(), query, extras); 1864 } 1865 1866 @Override 1867 public void prepareFromUri(String packageName, Uri uri, Bundle extras) { 1868 mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1869 uri, extras); 1870 } 1871 1872 @Override 1873 public void play(String packageName) { 1874 mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1875 } 1876 1877 @Override 1878 public void playFromMediaId(String packageName, String mediaId, Bundle extras) { 1879 mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1880 mediaId, extras); 1881 } 1882 1883 @Override 1884 public void playFromSearch(String packageName, String query, Bundle extras) { 1885 mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1886 query, extras); 1887 } 1888 1889 @Override 1890 public void playFromUri(String packageName, Uri uri, Bundle extras) { 1891 mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1892 uri, extras); 1893 } 1894 1895 @Override 1896 public void skipToQueueItem(String packageName, long id) { 1897 mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(), id); 1898 } 1899 1900 @Override 1901 public void pause(String packageName) { 1902 mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1903 } 1904 1905 @Override 1906 public void stop(String packageName) { 1907 mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1908 } 1909 1910 @Override 1911 public void next(String packageName) { 1912 mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1913 } 1914 1915 @Override 1916 public void previous(String packageName) { 1917 mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1918 } 1919 1920 @Override 1921 public void fastForward(String packageName) { 1922 mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1923 } 1924 1925 @Override 1926 public void rewind(String packageName) { 1927 mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid()); 1928 } 1929 1930 @Override 1931 public void seekTo(String packageName, long pos) { 1932 mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), pos); 1933 } 1934 1935 @Override 1936 public void rate(String packageName, Rating rating) { 1937 mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), rating); 1938 } 1939 1940 @Override 1941 public void setPlaybackSpeed(String packageName, 1942 float speed) { 1943 mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1944 speed); 1945 } 1946 1947 @Override 1948 public void sendCustomAction(String packageName, String action, Bundle args) { 1949 mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(), 1950 action, args); 1951 } 1952 1953 @Override 1954 public MediaMetadata getMetadata() { 1955 synchronized (mLock) { 1956 return mMetadata; 1957 } 1958 } 1959 1960 @Override 1961 public PlaybackState getPlaybackState() { 1962 return getStateWithUpdatedPosition(); 1963 } 1964 1965 @Override 1966 public ParceledListSlice getQueue() { 1967 synchronized (mLock) { 1968 return mQueue == null ? null : new ParceledListSlice<>(mQueue); 1969 } 1970 } 1971 1972 @Override 1973 public CharSequence getQueueTitle() { 1974 return mQueueTitle; 1975 } 1976 1977 @Override 1978 public Bundle getExtras() { 1979 synchronized (mLock) { 1980 return mExtras; 1981 } 1982 } 1983 1984 @Override 1985 public int getRatingType() { 1986 return mRatingType; 1987 } 1988 } 1989 1990 private class ISessionControllerCallbackHolder { 1991 private final ISessionControllerCallback mCallback; 1992 private final String mPackageName; 1993 private final int mUid; 1994 private final IBinder.DeathRecipient mDeathMonitor; 1995 1996 ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName, 1997 int uid, IBinder.DeathRecipient deathMonitor) { 1998 mCallback = callback; 1999 mPackageName = packageName; 2000 mUid = uid; 2001 mDeathMonitor = deathMonitor; 2002 } 2003 } 2004 2005 private class MessageHandler extends Handler { 2006 private static final int MSG_UPDATE_METADATA = 1; 2007 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 2008 private static final int MSG_UPDATE_QUEUE = 3; 2009 private static final int MSG_UPDATE_QUEUE_TITLE = 4; 2010 private static final int MSG_UPDATE_EXTRAS = 5; 2011 private static final int MSG_SEND_EVENT = 6; 2012 private static final int MSG_UPDATE_SESSION_STATE = 7; 2013 private static final int MSG_UPDATE_VOLUME = 8; 2014 private static final int MSG_DESTROYED = 9; 2015 2016 public MessageHandler(Looper looper) { 2017 super(looper); 2018 } 2019 @Override 2020 public void handleMessage(Message msg) { 2021 switch (msg.what) { 2022 case MSG_UPDATE_METADATA: 2023 pushMetadataUpdate(); 2024 break; 2025 case MSG_UPDATE_PLAYBACK_STATE: 2026 pushPlaybackStateUpdate(); 2027 break; 2028 case MSG_UPDATE_QUEUE: 2029 pushQueueUpdate(); 2030 break; 2031 case MSG_UPDATE_QUEUE_TITLE: 2032 pushQueueTitleUpdate(); 2033 break; 2034 case MSG_UPDATE_EXTRAS: 2035 pushExtrasUpdate(); 2036 break; 2037 case MSG_SEND_EVENT: 2038 pushEvent((String) msg.obj, msg.getData()); 2039 break; 2040 case MSG_UPDATE_SESSION_STATE: 2041 // TODO add session state 2042 break; 2043 case MSG_UPDATE_VOLUME: 2044 pushVolumeUpdate(); 2045 break; 2046 case MSG_DESTROYED: 2047 pushSessionDestroyed(); 2048 } 2049 } 2050 2051 public void post(int what) { 2052 post(what, null); 2053 } 2054 2055 public void post(int what, Object obj) { 2056 obtainMessage(what, obj).sendToTarget(); 2057 } 2058 2059 public void post(int what, Object obj, Bundle data) { 2060 Message msg = obtainMessage(what, obj); 2061 msg.setData(data); 2062 msg.sendToTarget(); 2063 } 2064 } 2065 } 2066