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