MediaSessionService.java revision 08c7116ab9cd04ad6dd3c04aa1017237e7f409ac
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.Manifest;
20import android.app.Activity;
21import android.app.ActivityManager;
22import android.app.KeyguardManager;
23import android.app.PendingIntent;
24import android.app.PendingIntent.CanceledException;
25import android.content.ActivityNotFoundException;
26import android.content.BroadcastReceiver;
27import android.content.ComponentName;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.pm.PackageManager;
32import android.database.ContentObserver;
33import android.media.AudioManager;
34import android.media.AudioManagerInternal;
35import android.media.AudioSystem;
36import android.media.IAudioService;
37import android.media.IRemoteVolumeController;
38import android.media.session.IActiveSessionsListener;
39import android.media.session.ISession;
40import android.media.session.ISessionCallback;
41import android.media.session.ISessionManager;
42import android.media.session.MediaSession;
43import android.net.Uri;
44import android.os.Binder;
45import android.os.Bundle;
46import android.os.Handler;
47import android.os.IBinder;
48import android.os.Message;
49import android.os.PowerManager;
50import android.os.RemoteException;
51import android.os.ResultReceiver;
52import android.os.ServiceManager;
53import android.os.UserHandle;
54import android.provider.Settings;
55import android.speech.RecognizerIntent;
56import android.text.TextUtils;
57import android.util.Log;
58import android.util.SparseArray;
59import android.view.KeyEvent;
60
61import com.android.server.LocalServices;
62import com.android.server.SystemService;
63import com.android.server.Watchdog;
64import com.android.server.Watchdog.Monitor;
65
66import java.io.FileDescriptor;
67import java.io.PrintWriter;
68import java.util.ArrayList;
69import java.util.List;
70
71/**
72 * System implementation of MediaSessionManager
73 */
74public class MediaSessionService extends SystemService implements Monitor {
75    private static final String TAG = "MediaSessionService";
76    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
77
78    private static final int WAKELOCK_TIMEOUT = 5000;
79
80    /* package */final IBinder mICallback = new Binder();
81
82    private final SessionManagerImpl mSessionManagerImpl;
83    private final MediaSessionStack mPriorityStack;
84
85    private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
86    private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
87    private final ArrayList<SessionsListenerRecord> mSessionsListeners
88            = new ArrayList<SessionsListenerRecord>();
89    private final Object mLock = new Object();
90    private final MessageHandler mHandler = new MessageHandler();
91    private final PowerManager.WakeLock mMediaEventWakeLock;
92    private final boolean mUseMasterVolume;
93
94    private KeyguardManager mKeyguardManager;
95    private IAudioService mAudioService;
96    private AudioManager mAudioManager;
97    private AudioManagerInternal mAudioManagerInternal;
98    private ContentResolver mContentResolver;
99    private SettingsObserver mSettingsObserver;
100
101    private int mCurrentUserId = -1;
102
103    // Used to notify system UI when remote volume was changed. TODO find a
104    // better way to handle this.
105    private IRemoteVolumeController mRvc;
106
107    public MediaSessionService(Context context) {
108        super(context);
109        mSessionManagerImpl = new SessionManagerImpl();
110        mPriorityStack = new MediaSessionStack();
111        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
112        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
113        mUseMasterVolume = context.getResources().getBoolean(
114                com.android.internal.R.bool.config_useMasterVolume);
115    }
116
117    @Override
118    public void onStart() {
119        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
120        Watchdog.getInstance().addMonitor(this);
121        mKeyguardManager =
122                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
123        mAudioService = getAudioService();
124        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
125        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
126        mContentResolver = getContext().getContentResolver();
127        mSettingsObserver = new SettingsObserver();
128        mSettingsObserver.observe();
129
130        updateUser();
131    }
132
133    private IAudioService getAudioService() {
134        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
135        return IAudioService.Stub.asInterface(b);
136    }
137
138    public void updateSession(MediaSessionRecord record) {
139        synchronized (mLock) {
140            if (!mAllSessions.contains(record)) {
141                Log.d(TAG, "Unknown session updated. Ignoring.");
142                return;
143            }
144            mPriorityStack.onSessionStateChange(record);
145        }
146        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
147    }
148
149    /**
150     * Tells the system UI that volume has changed on a remote session.
151     */
152    public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
153        if (mRvc == null) {
154            return;
155        }
156        try {
157            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
158        } catch (Exception e) {
159            Log.wtf(TAG, "Error sending volume change to system UI.", e);
160        }
161    }
162
163    public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
164        boolean updateSessions = false;
165        synchronized (mLock) {
166            if (!mAllSessions.contains(record)) {
167                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
168                return;
169            }
170            updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
171        }
172        if (updateSessions) {
173            mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
174        }
175    }
176
177    public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
178        synchronized (mLock) {
179            if (!mAllSessions.contains(record)) {
180                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
181                return;
182            }
183            pushRemoteVolumeUpdateLocked(record.getUserId());
184        }
185    }
186
187    @Override
188    public void onStartUser(int userHandle) {
189        updateUser();
190    }
191
192    @Override
193    public void onSwitchUser(int userHandle) {
194        updateUser();
195    }
196
197    @Override
198    public void onStopUser(int userHandle) {
199        synchronized (mLock) {
200            UserRecord user = mUserRecords.get(userHandle);
201            if (user != null) {
202                destroyUserLocked(user);
203            }
204        }
205    }
206
207    @Override
208    public void monitor() {
209        synchronized (mLock) {
210            // Check for deadlock
211        }
212    }
213
214    protected void enforcePhoneStatePermission(int pid, int uid) {
215        if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
216                != PackageManager.PERMISSION_GRANTED) {
217            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
218        }
219    }
220
221    void sessionDied(MediaSessionRecord session) {
222        synchronized (mLock) {
223            destroySessionLocked(session);
224        }
225    }
226
227    void destroySession(MediaSessionRecord session) {
228        synchronized (mLock) {
229            destroySessionLocked(session);
230        }
231    }
232
233    private void updateUser() {
234        synchronized (mLock) {
235            int userId = ActivityManager.getCurrentUser();
236            if (mCurrentUserId != userId) {
237                final int oldUserId = mCurrentUserId;
238                mCurrentUserId = userId; // do this first
239
240                UserRecord oldUser = mUserRecords.get(oldUserId);
241                if (oldUser != null) {
242                    oldUser.stopLocked();
243                }
244
245                UserRecord newUser = getOrCreateUser(userId);
246                newUser.startLocked();
247            }
248        }
249    }
250
251    private void updateActiveSessionListeners() {
252        synchronized (mLock) {
253            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
254                SessionsListenerRecord listener = mSessionsListeners.get(i);
255                try {
256                    enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
257                            listener.mUserId);
258                } catch (SecurityException e) {
259                    Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
260                            + " is no longer authorized. Disconnecting.");
261                    mSessionsListeners.remove(i);
262                    try {
263                        listener.mListener
264                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
265                    } catch (Exception e1) {
266                        // ignore
267                    }
268                }
269            }
270        }
271    }
272
273    /**
274     * Stop the user and unbind from everything.
275     *
276     * @param user The user to dispose of
277     */
278    private void destroyUserLocked(UserRecord user) {
279        user.stopLocked();
280        user.destroyLocked();
281        mUserRecords.remove(user.mUserId);
282    }
283
284    /*
285     * When a session is removed several things need to happen.
286     * 1. We need to remove it from the relevant user.
287     * 2. We need to remove it from the priority stack.
288     * 3. We need to remove it from all sessions.
289     * 4. If this is the system priority session we need to clear it.
290     * 5. We need to unlink to death from the cb binder
291     * 6. We need to tell the session to do any final cleanup (onDestroy)
292     */
293    private void destroySessionLocked(MediaSessionRecord session) {
294        int userId = session.getUserId();
295        UserRecord user = mUserRecords.get(userId);
296        if (user != null) {
297            user.removeSessionLocked(session);
298        }
299
300        mPriorityStack.removeSession(session);
301        mAllSessions.remove(session);
302
303        try {
304            session.getCallback().asBinder().unlinkToDeath(session, 0);
305        } catch (Exception e) {
306            // ignore exceptions while destroying a session.
307        }
308        session.onDestroy();
309
310        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
311    }
312
313    private void enforcePackageName(String packageName, int uid) {
314        if (TextUtils.isEmpty(packageName)) {
315            throw new IllegalArgumentException("packageName may not be empty");
316        }
317        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
318        final int packageCount = packages.length;
319        for (int i = 0; i < packageCount; i++) {
320            if (packageName.equals(packages[i])) {
321                return;
322            }
323        }
324        throw new IllegalArgumentException("packageName is not owned by the calling process");
325    }
326
327    /**
328     * Checks a caller's authorization to register an IRemoteControlDisplay.
329     * Authorization is granted if one of the following is true:
330     * <ul>
331     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
332     * permission</li>
333     * <li>the caller's listener is one of the enabled notification listeners
334     * for the caller's user</li>
335     * </ul>
336     */
337    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
338            int resolvedUserId) {
339        if (isCurrentVolumeController(uid)) return;
340        if (getContext()
341                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
342                    != PackageManager.PERMISSION_GRANTED
343                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
344                        resolvedUserId)) {
345            throw new SecurityException("Missing permission to control media.");
346        }
347    }
348
349    private boolean isCurrentVolumeController(int uid) {
350        if (mAudioManagerInternal != null) {
351            final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
352            if (vcuid > 0 && uid == vcuid) {
353                return true;
354            }
355        }
356        return false;
357    }
358
359    private void enforceSystemUiPermission(String action, int pid, int uid) {
360        if (isCurrentVolumeController(uid)) return;
361        if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
362                pid, uid) != PackageManager.PERMISSION_GRANTED) {
363            throw new SecurityException("Only system ui may " + action);
364        }
365    }
366
367    /**
368     * This checks if the component is an enabled notification listener for the
369     * specified user. Enabled components may only operate on behalf of the user
370     * they're running as.
371     *
372     * @param compName The component that is enabled.
373     * @param userId The user id of the caller.
374     * @param forUserId The user id they're making the request on behalf of.
375     * @return True if the component is enabled, false otherwise
376     */
377    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
378            int forUserId) {
379        if (userId != forUserId) {
380            // You may not access another user's content as an enabled listener.
381            return false;
382        }
383        if (DEBUG) {
384            Log.d(TAG, "Checking if enabled notification listener " + compName);
385        }
386        if (compName != null) {
387            final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
388                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
389                    userId);
390            if (enabledNotifListeners != null) {
391                final String[] components = enabledNotifListeners.split(":");
392                for (int i = 0; i < components.length; i++) {
393                    final ComponentName component =
394                            ComponentName.unflattenFromString(components[i]);
395                    if (component != null) {
396                        if (compName.equals(component)) {
397                            if (DEBUG) {
398                                Log.d(TAG, "ok to get sessions: " + component +
399                                        " is authorized notification listener");
400                            }
401                            return true;
402                        }
403                    }
404                }
405            }
406            if (DEBUG) {
407                Log.d(TAG, "not ok to get sessions, " + compName +
408                        " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
409            }
410        }
411        return false;
412    }
413
414    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
415            String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
416        synchronized (mLock) {
417            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
418        }
419    }
420
421    /*
422     * When a session is created the following things need to happen.
423     * 1. Its callback binder needs a link to death
424     * 2. It needs to be added to all sessions.
425     * 3. It needs to be added to the priority stack.
426     * 4. It needs to be added to the relevant user record.
427     */
428    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
429            String callerPackageName, ISessionCallback cb, String tag) {
430
431        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
432                callerPackageName, cb, tag, this, mHandler);
433        try {
434            cb.asBinder().linkToDeath(session, 0);
435        } catch (RemoteException e) {
436            throw new RuntimeException("Media Session owner died prematurely.", e);
437        }
438
439        mAllSessions.add(session);
440        mPriorityStack.addSession(session);
441
442        UserRecord user = getOrCreateUser(userId);
443        user.addSessionLocked(session);
444
445        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
446
447        if (DEBUG) {
448            Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
449        }
450        return session;
451    }
452
453    private UserRecord getOrCreateUser(int userId) {
454        UserRecord user = mUserRecords.get(userId);
455        if (user == null) {
456            user = new UserRecord(getContext(), userId);
457            mUserRecords.put(userId, user);
458        }
459        return user;
460    }
461
462    private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
463        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
464            if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
465                return i;
466            }
467        }
468        return -1;
469    }
470
471    private boolean isSessionDiscoverable(MediaSessionRecord record) {
472        // TODO probably want to check more than if it's active.
473        return record.isActive();
474    }
475
476    private void pushSessionsChanged(int userId) {
477        synchronized (mLock) {
478            List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
479            int size = records.size();
480            if (size > 0 && records.get(0).isPlaybackActive(false)) {
481                rememberMediaButtonReceiverLocked(records.get(0));
482            }
483            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
484            for (int i = 0; i < size; i++) {
485                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
486            }
487            pushRemoteVolumeUpdateLocked(userId);
488            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
489                SessionsListenerRecord record = mSessionsListeners.get(i);
490                if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
491                    try {
492                        record.mListener.onActiveSessionsChanged(tokens);
493                    } catch (RemoteException e) {
494                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
495                                e);
496                        mSessionsListeners.remove(i);
497                    }
498                }
499            }
500        }
501    }
502
503    private void pushRemoteVolumeUpdateLocked(int userId) {
504        if (mRvc != null) {
505            try {
506                MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
507                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
508            } catch (RemoteException e) {
509                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
510            }
511        }
512    }
513
514    private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
515        PendingIntent receiver = record.getMediaButtonReceiver();
516        UserRecord user = mUserRecords.get(record.getUserId());
517        if (receiver != null && user != null) {
518            user.mLastMediaButtonReceiver = receiver;
519            ComponentName component = receiver.getIntent().getComponent();
520            if (component != null && record.getPackageName().equals(component.getPackageName())) {
521                Settings.Secure.putStringForUser(mContentResolver,
522                        Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
523                        record.getUserId());
524            }
525        }
526    }
527
528    /**
529     * Information about a particular user. The contents of this object is
530     * guarded by mLock.
531     */
532    final class UserRecord {
533        private final int mUserId;
534        private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
535        private final Context mContext;
536        private PendingIntent mLastMediaButtonReceiver;
537        private ComponentName mRestoredMediaButtonReceiver;
538
539        public UserRecord(Context context, int userId) {
540            mContext = context;
541            mUserId = userId;
542            restoreMediaButtonReceiver();
543        }
544
545        public void startLocked() {
546        }
547
548        public void stopLocked() {
549            // nothing for now
550        }
551
552        public void destroyLocked() {
553            for (int i = mSessions.size() - 1; i >= 0; i--) {
554                MediaSessionRecord session = mSessions.get(i);
555                MediaSessionService.this.destroySessionLocked(session);
556            }
557        }
558
559        public ArrayList<MediaSessionRecord> getSessionsLocked() {
560            return mSessions;
561        }
562
563        public void addSessionLocked(MediaSessionRecord session) {
564            mSessions.add(session);
565        }
566
567        public void removeSessionLocked(MediaSessionRecord session) {
568            mSessions.remove(session);
569        }
570
571        public void dumpLocked(PrintWriter pw, String prefix) {
572            pw.println(prefix + "Record for user " + mUserId);
573            String indent = prefix + "  ";
574            pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
575            pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
576            int size = mSessions.size();
577            pw.println(indent + size + " Sessions:");
578            for (int i = 0; i < size; i++) {
579                // Just print the short version, the full session dump will
580                // already be in the list of all sessions.
581                pw.println(indent + mSessions.get(i).toString());
582            }
583        }
584
585        private void restoreMediaButtonReceiver() {
586            String receiverName = Settings.Secure.getStringForUser(mContentResolver,
587                    Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
588            if (!TextUtils.isEmpty(receiverName)) {
589                ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
590                if (eventReceiver == null) {
591                    // an invalid name was persisted
592                    return;
593                }
594                mRestoredMediaButtonReceiver = eventReceiver;
595            }
596        }
597    }
598
599    final class SessionsListenerRecord implements IBinder.DeathRecipient {
600        private final IActiveSessionsListener mListener;
601        private final ComponentName mComponentName;
602        private final int mUserId;
603        private final int mPid;
604        private final int mUid;
605
606        public SessionsListenerRecord(IActiveSessionsListener listener,
607                ComponentName componentName,
608                int userId, int pid, int uid) {
609            mListener = listener;
610            mComponentName = componentName;
611            mUserId = userId;
612            mPid = pid;
613            mUid = uid;
614        }
615
616        @Override
617        public void binderDied() {
618            synchronized (mLock) {
619                mSessionsListeners.remove(this);
620            }
621        }
622    }
623
624    final class SettingsObserver extends ContentObserver {
625        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
626                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
627
628        private SettingsObserver() {
629            super(null);
630        }
631
632        private void observe() {
633            mContentResolver.registerContentObserver(mSecureSettingsUri,
634                    false, this, UserHandle.USER_ALL);
635        }
636
637        @Override
638        public void onChange(boolean selfChange, Uri uri) {
639            updateActiveSessionListeners();
640        }
641    }
642
643    class SessionManagerImpl extends ISessionManager.Stub {
644        private static final String EXTRA_WAKELOCK_ACQUIRED =
645                "android.media.AudioService.WAKELOCK_ACQUIRED";
646        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
647
648        private boolean mVoiceButtonDown = false;
649        private boolean mVoiceButtonHandled = false;
650
651        @Override
652        public ISession createSession(String packageName, ISessionCallback cb, String tag,
653                int userId) throws RemoteException {
654            final int pid = Binder.getCallingPid();
655            final int uid = Binder.getCallingUid();
656            final long token = Binder.clearCallingIdentity();
657            try {
658                enforcePackageName(packageName, uid);
659                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
660                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
661                if (cb == null) {
662                    throw new IllegalArgumentException("Controller callback cannot be null");
663                }
664                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
665                        .getSessionBinder();
666            } finally {
667                Binder.restoreCallingIdentity(token);
668            }
669        }
670
671        @Override
672        public List<IBinder> getSessions(ComponentName componentName, int userId) {
673            final int pid = Binder.getCallingPid();
674            final int uid = Binder.getCallingUid();
675            final long token = Binder.clearCallingIdentity();
676
677            try {
678                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
679                ArrayList<IBinder> binders = new ArrayList<IBinder>();
680                synchronized (mLock) {
681                    ArrayList<MediaSessionRecord> records = mPriorityStack
682                            .getActiveSessions(resolvedUserId);
683                    int size = records.size();
684                    for (int i = 0; i < size; i++) {
685                        binders.add(records.get(i).getControllerBinder().asBinder());
686                    }
687                }
688                return binders;
689            } finally {
690                Binder.restoreCallingIdentity(token);
691            }
692        }
693
694        @Override
695        public void addSessionsListener(IActiveSessionsListener listener,
696                ComponentName componentName, int userId) throws RemoteException {
697            final int pid = Binder.getCallingPid();
698            final int uid = Binder.getCallingUid();
699            final long token = Binder.clearCallingIdentity();
700
701            try {
702                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
703                synchronized (mLock) {
704                    int index = findIndexOfSessionsListenerLocked(listener);
705                    if (index != -1) {
706                        Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
707                        return;
708                    }
709                    SessionsListenerRecord record = new SessionsListenerRecord(listener,
710                            componentName, resolvedUserId, pid, uid);
711                    try {
712                        listener.asBinder().linkToDeath(record, 0);
713                    } catch (RemoteException e) {
714                        Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
715                        return;
716                    }
717                    mSessionsListeners.add(record);
718                }
719            } finally {
720                Binder.restoreCallingIdentity(token);
721            }
722        }
723
724        @Override
725        public void removeSessionsListener(IActiveSessionsListener listener)
726                throws RemoteException {
727            synchronized (mLock) {
728                int index = findIndexOfSessionsListenerLocked(listener);
729                if (index != -1) {
730                    SessionsListenerRecord record = mSessionsListeners.remove(index);
731                    try {
732                        record.mListener.asBinder().unlinkToDeath(record, 0);
733                    } catch (Exception e) {
734                        // ignore exceptions, the record is being removed
735                    }
736                }
737            }
738        }
739
740        /**
741         * Handles the dispatching of the media button events to one of the
742         * registered listeners, or if there was none, broadcast an
743         * ACTION_MEDIA_BUTTON intent to the rest of the system.
744         *
745         * @param keyEvent a non-null KeyEvent whose key code is one of the
746         *            supported media buttons
747         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
748         *            while this key event is dispatched.
749         */
750        @Override
751        public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
752            if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
753                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
754                return;
755            }
756            final int pid = Binder.getCallingPid();
757            final int uid = Binder.getCallingUid();
758            final long token = Binder.clearCallingIdentity();
759            if (DEBUG) {
760                Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
761                        + keyEvent);
762            }
763
764            try {
765                synchronized (mLock) {
766                    // If we don't have a media button receiver to fall back on
767                    // include non-playing sessions for dispatching
768                    UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
769                    boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null
770                            && ur.mRestoredMediaButtonReceiver == null;
771                    MediaSessionRecord session = mPriorityStack
772                            .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
773                    if (isVoiceKey(keyEvent.getKeyCode())) {
774                        handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
775                    } else {
776                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
777                    }
778                }
779            } finally {
780                Binder.restoreCallingIdentity(token);
781            }
782        }
783
784        @Override
785        public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
786            final int pid = Binder.getCallingPid();
787            final int uid = Binder.getCallingUid();
788            final long token = Binder.clearCallingIdentity();
789            try {
790                synchronized (mLock) {
791                    MediaSessionRecord session = mPriorityStack
792                            .getDefaultVolumeSession(mCurrentUserId);
793                    dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
794                }
795            } finally {
796                Binder.restoreCallingIdentity(token);
797            }
798        }
799
800        @Override
801        public void setRemoteVolumeController(IRemoteVolumeController rvc) {
802            final int pid = Binder.getCallingPid();
803            final int uid = Binder.getCallingUid();
804            final long token = Binder.clearCallingIdentity();
805            try {
806                enforceSystemUiPermission("listen for volume changes", pid, uid);
807                mRvc = rvc;
808            } finally {
809                Binder.restoreCallingIdentity(token);
810            }
811        }
812
813        @Override
814        public boolean isGlobalPriorityActive() {
815            return mPriorityStack.isGlobalPriorityActive();
816        }
817
818        @Override
819        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
820            if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
821                    != PackageManager.PERMISSION_GRANTED) {
822                pw.println("Permission Denial: can't dump MediaSessionService from from pid="
823                        + Binder.getCallingPid()
824                        + ", uid=" + Binder.getCallingUid());
825                return;
826            }
827
828            pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
829            pw.println();
830
831            synchronized (mLock) {
832                pw.println(mSessionsListeners.size() + " sessions listeners.");
833                int count = mAllSessions.size();
834                pw.println(count + " Sessions:");
835                for (int i = 0; i < count; i++) {
836                    mAllSessions.get(i).dump(pw, "");
837                    pw.println();
838                }
839                mPriorityStack.dump(pw, "");
840
841                pw.println("User Records:");
842                count = mUserRecords.size();
843                for (int i = 0; i < count; i++) {
844                    UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
845                    user.dumpLocked(pw, "");
846                }
847            }
848        }
849
850        private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
851                final int uid) {
852            String packageName = null;
853            if (componentName != null) {
854                // If they gave us a component name verify they own the
855                // package
856                packageName = componentName.getPackageName();
857                enforcePackageName(packageName, uid);
858            }
859            // Check that they can make calls on behalf of the user and
860            // get the final user id
861            int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
862                    true /* allowAll */, true /* requireFull */, "getSessions", packageName);
863            // Check if they have the permissions or their component is
864            // enabled for the user they're calling from.
865            enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
866            return resolvedUserId;
867        }
868
869        private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
870                MediaSessionRecord session) {
871            if (DEBUG) {
872                String description = session == null ? null : session.toString();
873                Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
874                        + flags + ", suggestedStream=" + suggestedStream);
875
876            }
877            boolean preferSuggestedStream = false;
878            if (isValidLocalStreamType(suggestedStream)
879                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
880                preferSuggestedStream = true;
881            }
882            if (session == null || preferSuggestedStream) {
883                if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
884                        && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
885                    if (DEBUG) {
886                        Log.d(TAG, "No active session to adjust, skipping media only volume event");
887                    }
888                    return;
889                }
890                try {
891                    String packageName = getContext().getOpPackageName();
892                    if (mUseMasterVolume) {
893                            mAudioService.adjustMasterVolume(direction, flags, packageName);
894                    } else {
895                        mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
896                                flags, packageName);
897                    }
898                } catch (RemoteException e) {
899                    Log.e(TAG, "Error adjusting default volume.", e);
900                }
901            } else {
902                session.adjustVolume(direction, flags, getContext().getPackageName(),
903                        UserHandle.myUserId(), true);
904            }
905        }
906
907        private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
908                MediaSessionRecord session) {
909            if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
910                // If the phone app has priority just give it the event
911                dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
912                return;
913            }
914            int action = keyEvent.getAction();
915            boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
916            if (action == KeyEvent.ACTION_DOWN) {
917                if (keyEvent.getRepeatCount() == 0) {
918                    mVoiceButtonDown = true;
919                    mVoiceButtonHandled = false;
920                } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
921                    mVoiceButtonHandled = true;
922                    startVoiceInput(needWakeLock);
923                }
924            } else if (action == KeyEvent.ACTION_UP) {
925                if (mVoiceButtonDown) {
926                    mVoiceButtonDown = false;
927                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
928                        // Resend the down then send this event through
929                        KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
930                        dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
931                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
932                    }
933                }
934            }
935        }
936
937        private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
938                MediaSessionRecord session) {
939            if (session != null) {
940                if (DEBUG) {
941                    Log.d(TAG, "Sending media key to " + session.toString());
942                }
943                if (needWakeLock) {
944                    mKeyEventReceiver.aquireWakeLockLocked();
945                }
946                // If we don't need a wakelock use -1 as the id so we
947                // won't release it later
948                session.sendMediaButton(keyEvent,
949                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
950                        mKeyEventReceiver);
951            } else {
952                // Launch the last PendingIntent we had with priority
953                int userId = ActivityManager.getCurrentUser();
954                UserRecord user = mUserRecords.get(userId);
955                if (user.mLastMediaButtonReceiver != null
956                        || user.mRestoredMediaButtonReceiver != null) {
957                    if (DEBUG) {
958                        Log.d(TAG, "Sending media key to last known PendingIntent "
959                                + user.mLastMediaButtonReceiver + " or restored Intent "
960                                + user.mRestoredMediaButtonReceiver);
961                    }
962                    if (needWakeLock) {
963                        mKeyEventReceiver.aquireWakeLockLocked();
964                    }
965                    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
966                    mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
967                    try {
968                        if (user.mLastMediaButtonReceiver != null) {
969                            user.mLastMediaButtonReceiver.send(getContext(),
970                                    needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
971                                    mediaButtonIntent, mKeyEventReceiver, null);
972                        } else {
973                            mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
974                            getContext().sendBroadcastAsUser(mediaButtonIntent,
975                                    new UserHandle(userId));
976                        }
977                    } catch (CanceledException e) {
978                        Log.i(TAG, "Error sending key event to media button receiver "
979                                + user.mLastMediaButtonReceiver, e);
980                    }
981                } else {
982                    if (DEBUG) {
983                        Log.d(TAG, "Sending media key ordered broadcast");
984                    }
985                    if (needWakeLock) {
986                        mMediaEventWakeLock.acquire();
987                    }
988                    // Fallback to legacy behavior
989                    Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
990                    keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
991                    if (needWakeLock) {
992                        keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
993                                WAKELOCK_RELEASE_ON_FINISHED);
994                    }
995                    getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
996                            null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
997                }
998            }
999        }
1000
1001        private void startVoiceInput(boolean needWakeLock) {
1002            Intent voiceIntent = null;
1003            // select which type of search to launch:
1004            // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1005            // - device locked or screen off: action is
1006            // ACTION_VOICE_SEARCH_HANDS_FREE
1007            // with EXTRA_SECURE set to true if the device is securely locked
1008            PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1009            boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1010            if (!isLocked && pm.isScreenOn()) {
1011                voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1012                Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1013            } else {
1014                voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1015                voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1016                        isLocked && mKeyguardManager.isKeyguardSecure());
1017                Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1018            }
1019            // start the search activity
1020            if (needWakeLock) {
1021                mMediaEventWakeLock.acquire();
1022            }
1023            try {
1024                if (voiceIntent != null) {
1025                    voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1026                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1027                    getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1028                }
1029            } catch (ActivityNotFoundException e) {
1030                Log.w(TAG, "No activity for search: " + e);
1031            } finally {
1032                if (needWakeLock) {
1033                    mMediaEventWakeLock.release();
1034                }
1035            }
1036        }
1037
1038        private boolean isVoiceKey(int keyCode) {
1039            return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1040        }
1041
1042        // we only handle public stream types, which are 0-5
1043        private boolean isValidLocalStreamType(int streamType) {
1044            return streamType >= AudioManager.STREAM_VOICE_CALL
1045                    && streamType <= AudioManager.STREAM_NOTIFICATION;
1046        }
1047
1048        private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1049
1050        class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1051                PendingIntent.OnFinished {
1052            private final Handler mHandler;
1053            private int mRefCount = 0;
1054            private int mLastTimeoutId = 0;
1055
1056            public KeyEventWakeLockReceiver(Handler handler) {
1057                super(handler);
1058                mHandler = handler;
1059            }
1060
1061            public void onTimeout() {
1062                synchronized (mLock) {
1063                    if (mRefCount == 0) {
1064                        // We've already released it, so just return
1065                        return;
1066                    }
1067                    mLastTimeoutId++;
1068                    mRefCount = 0;
1069                    releaseWakeLockLocked();
1070                }
1071            }
1072
1073            public void aquireWakeLockLocked() {
1074                if (mRefCount == 0) {
1075                    mMediaEventWakeLock.acquire();
1076                }
1077                mRefCount++;
1078                mHandler.removeCallbacks(this);
1079                mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1080
1081            }
1082
1083            @Override
1084            public void run() {
1085                onTimeout();
1086            }
1087
1088            @Override
1089            protected void onReceiveResult(int resultCode, Bundle resultData) {
1090                if (resultCode < mLastTimeoutId) {
1091                    // Ignore results from calls that were before the last
1092                    // timeout, just in case.
1093                    return;
1094                } else {
1095                    synchronized (mLock) {
1096                        if (mRefCount > 0) {
1097                            mRefCount--;
1098                            if (mRefCount == 0) {
1099                                releaseWakeLockLocked();
1100                            }
1101                        }
1102                    }
1103                }
1104            }
1105
1106            private void releaseWakeLockLocked() {
1107                mMediaEventWakeLock.release();
1108                mHandler.removeCallbacks(this);
1109            }
1110
1111            @Override
1112            public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1113                    String resultData, Bundle resultExtras) {
1114                onReceiveResult(resultCode, null);
1115            }
1116        };
1117
1118        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1119            @Override
1120            public void onReceive(Context context, Intent intent) {
1121                if (intent == null) {
1122                    return;
1123                }
1124                Bundle extras = intent.getExtras();
1125                if (extras == null) {
1126                    return;
1127                }
1128                synchronized (mLock) {
1129                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1130                            && mMediaEventWakeLock.isHeld()) {
1131                        mMediaEventWakeLock.release();
1132                    }
1133                }
1134            }
1135        };
1136    }
1137
1138    final class MessageHandler extends Handler {
1139        private static final int MSG_SESSIONS_CHANGED = 1;
1140
1141        @Override
1142        public void handleMessage(Message msg) {
1143            switch (msg.what) {
1144                case MSG_SESSIONS_CHANGED:
1145                    pushSessionsChanged(msg.arg1);
1146                    break;
1147            }
1148        }
1149
1150        public void post(int what, int arg1, int arg2) {
1151            obtainMessage(what, arg1, arg2).sendToTarget();
1152        }
1153    }
1154}
1155