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