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