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