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