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