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