[go: nahoru, domu]

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