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