MediaSessionService.java revision 1f7a580f22f8a3406ad1896d234163ead1a97088
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.Slog;
59import android.util.SparseArray;
60import android.view.KeyEvent;
61
62import com.android.server.LocalServices;
63import com.android.server.SystemService;
64import com.android.server.Watchdog;
65import com.android.server.Watchdog.Monitor;
66
67import java.io.FileDescriptor;
68import java.io.PrintWriter;
69import java.util.ArrayList;
70import java.util.List;
71
72/**
73 * System implementation of MediaSessionManager
74 */
75public class MediaSessionService extends SystemService implements Monitor {
76    private static final String TAG = "MediaSessionService";
77    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
78
79    private static final int WAKELOCK_TIMEOUT = 5000;
80
81    /* package */final IBinder mICallback = new Binder();
82
83    private final SessionManagerImpl mSessionManagerImpl;
84    private final MediaSessionStack mPriorityStack;
85
86    private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
87    private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
88    private final ArrayList<SessionsListenerRecord> mSessionsListeners
89            = new ArrayList<SessionsListenerRecord>();
90    private final Object mLock = new Object();
91    private final MessageHandler mHandler = new MessageHandler();
92    private final PowerManager.WakeLock mMediaEventWakeLock;
93
94    private KeyguardManager mKeyguardManager;
95    private IAudioService mAudioService;
96    private AudioManagerInternal mAudioManagerInternal;
97    private ContentResolver mContentResolver;
98    private SettingsObserver mSettingsObserver;
99
100    private int mCurrentUserId = -1;
101
102    // Used to notify system UI when remote volume was changed. TODO find a
103    // better way to handle this.
104    private IRemoteVolumeController mRvc;
105
106    public MediaSessionService(Context context) {
107        super(context);
108        mSessionManagerImpl = new SessionManagerImpl();
109        mPriorityStack = new MediaSessionStack();
110        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
111        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
112    }
113
114    @Override
115    public void onStart() {
116        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
117        Watchdog.getInstance().addMonitor(this);
118        mKeyguardManager =
119                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
120        mAudioService = getAudioService();
121        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
122        mContentResolver = getContext().getContentResolver();
123        mSettingsObserver = new SettingsObserver();
124        mSettingsObserver.observe();
125
126        updateUser();
127    }
128
129    private IAudioService getAudioService() {
130        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
131        return IAudioService.Stub.asInterface(b);
132    }
133
134    public void updateSession(MediaSessionRecord record) {
135        synchronized (mLock) {
136            if (!mAllSessions.contains(record)) {
137                Log.d(TAG, "Unknown session updated. Ignoring.");
138                return;
139            }
140            mPriorityStack.onSessionStateChange(record);
141        }
142        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
143    }
144
145    /**
146     * Tells the system UI that volume has changed on a remote session.
147     */
148    public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
149        if (mRvc == null) {
150            return;
151        }
152        try {
153            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
154        } catch (Exception e) {
155            Log.wtf(TAG, "Error sending volume change to system UI.", e);
156        }
157    }
158
159    public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
160        boolean updateSessions = false;
161        synchronized (mLock) {
162            if (!mAllSessions.contains(record)) {
163                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
164                return;
165            }
166            updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
167        }
168        if (updateSessions) {
169            mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
170        }
171    }
172
173    public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
174        synchronized (mLock) {
175            if (!mAllSessions.contains(record)) {
176                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
177                return;
178            }
179            pushRemoteVolumeUpdateLocked(record.getUserId());
180        }
181    }
182
183    @Override
184    public void onStartUser(int userHandle) {
185        updateUser();
186    }
187
188    @Override
189    public void onSwitchUser(int userHandle) {
190        updateUser();
191    }
192
193    @Override
194    public void onStopUser(int userHandle) {
195        synchronized (mLock) {
196            UserRecord user = mUserRecords.get(userHandle);
197            if (user != null) {
198                destroyUserLocked(user);
199            }
200        }
201    }
202
203    @Override
204    public void monitor() {
205        synchronized (mLock) {
206            // Check for deadlock
207        }
208    }
209
210    protected void enforcePhoneStatePermission(int pid, int uid) {
211        if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
212                != PackageManager.PERMISSION_GRANTED) {
213            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
214        }
215    }
216
217    void sessionDied(MediaSessionRecord session) {
218        synchronized (mLock) {
219            destroySessionLocked(session);
220        }
221    }
222
223    void destroySession(MediaSessionRecord session) {
224        synchronized (mLock) {
225            destroySessionLocked(session);
226        }
227    }
228
229    private void updateUser() {
230        synchronized (mLock) {
231            int userId = ActivityManager.getCurrentUser();
232            if (mCurrentUserId != userId) {
233                final int oldUserId = mCurrentUserId;
234                mCurrentUserId = userId; // do this first
235
236                UserRecord oldUser = mUserRecords.get(oldUserId);
237                if (oldUser != null) {
238                    oldUser.stopLocked();
239                }
240
241                UserRecord newUser = getOrCreateUser(userId);
242                newUser.startLocked();
243            }
244        }
245    }
246
247    private void updateActiveSessionListeners() {
248        synchronized (mLock) {
249            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
250                SessionsListenerRecord listener = mSessionsListeners.get(i);
251                try {
252                    enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
253                            listener.mUserId);
254                } catch (SecurityException e) {
255                    Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
256                            + " is no longer authorized. Disconnecting.");
257                    mSessionsListeners.remove(i);
258                    try {
259                        listener.mListener
260                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
261                    } catch (Exception e1) {
262                        // ignore
263                    }
264                }
265            }
266        }
267    }
268
269    /**
270     * Stop the user and unbind from everything.
271     *
272     * @param user The user to dispose of
273     */
274    private void destroyUserLocked(UserRecord user) {
275        user.stopLocked();
276        user.destroyLocked();
277        mUserRecords.remove(user.mUserId);
278    }
279
280    /*
281     * When a session is removed several things need to happen.
282     * 1. We need to remove it from the relevant user.
283     * 2. We need to remove it from the priority stack.
284     * 3. We need to remove it from all sessions.
285     * 4. If this is the system priority session we need to clear it.
286     * 5. We need to unlink to death from the cb binder
287     * 6. We need to tell the session to do any final cleanup (onDestroy)
288     */
289    private void destroySessionLocked(MediaSessionRecord session) {
290        int userId = session.getUserId();
291        UserRecord user = mUserRecords.get(userId);
292        if (user != null) {
293            user.removeSessionLocked(session);
294        }
295
296        mPriorityStack.removeSession(session);
297        mAllSessions.remove(session);
298
299        try {
300            session.getCallback().asBinder().unlinkToDeath(session, 0);
301        } catch (Exception e) {
302            // ignore exceptions while destroying a session.
303        }
304        session.onDestroy();
305
306        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
307    }
308
309    private void enforcePackageName(String packageName, int uid) {
310        if (TextUtils.isEmpty(packageName)) {
311            throw new IllegalArgumentException("packageName may not be empty");
312        }
313        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
314        final int packageCount = packages.length;
315        for (int i = 0; i < packageCount; i++) {
316            if (packageName.equals(packages[i])) {
317                return;
318            }
319        }
320        throw new IllegalArgumentException("packageName is not owned by the calling process");
321    }
322
323    /**
324     * Checks a caller's authorization to register an IRemoteControlDisplay.
325     * Authorization is granted if one of the following is true:
326     * <ul>
327     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
328     * permission</li>
329     * <li>the caller's listener is one of the enabled notification listeners
330     * for the caller's user</li>
331     * </ul>
332     */
333    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
334            int resolvedUserId) {
335        if (isCurrentVolumeController(uid)) return;
336        if (getContext()
337                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
338                    != PackageManager.PERMISSION_GRANTED
339                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
340                        resolvedUserId)) {
341            throw new SecurityException("Missing permission to control media.");
342        }
343    }
344
345    private boolean isCurrentVolumeController(int uid) {
346        if (mAudioManagerInternal != null) {
347            final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
348            if (vcuid > 0 && uid == vcuid) {
349                return true;
350            }
351        }
352        return false;
353    }
354
355    private void enforceSystemUiPermission(String action, int pid, int uid) {
356        if (isCurrentVolumeController(uid)) return;
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 void pushSessionsChanged(int userId) {
468        synchronized (mLock) {
469            List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
470            int size = records.size();
471            if (size > 0 && records.get(0).isPlaybackActive(false)) {
472                rememberMediaButtonReceiverLocked(records.get(0));
473            }
474            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
475            for (int i = 0; i < size; i++) {
476                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
477            }
478            pushRemoteVolumeUpdateLocked(userId);
479            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
480                SessionsListenerRecord record = mSessionsListeners.get(i);
481                if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
482                    try {
483                        record.mListener.onActiveSessionsChanged(tokens);
484                    } catch (RemoteException e) {
485                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
486                                e);
487                        mSessionsListeners.remove(i);
488                    }
489                }
490            }
491        }
492    }
493
494    private void pushRemoteVolumeUpdateLocked(int userId) {
495        if (mRvc != null) {
496            try {
497                MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
498                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
499            } catch (RemoteException e) {
500                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
501            }
502        }
503    }
504
505    private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
506        PendingIntent receiver = record.getMediaButtonReceiver();
507        UserRecord user = mUserRecords.get(record.getUserId());
508        if (receiver != null && user != null) {
509            user.mLastMediaButtonReceiver = receiver;
510            ComponentName component = receiver.getIntent().getComponent();
511            if (component != null && record.getPackageName().equals(component.getPackageName())) {
512                Settings.Secure.putStringForUser(mContentResolver,
513                        Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
514                        record.getUserId());
515            }
516        }
517    }
518
519    /**
520     * Information about a particular user. The contents of this object is
521     * guarded by mLock.
522     */
523    final class UserRecord {
524        private final int mUserId;
525        private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
526        private final Context mContext;
527        private PendingIntent mLastMediaButtonReceiver;
528        private ComponentName mRestoredMediaButtonReceiver;
529
530        public UserRecord(Context context, int userId) {
531            mContext = context;
532            mUserId = userId;
533            restoreMediaButtonReceiver();
534        }
535
536        public void startLocked() {
537        }
538
539        public void stopLocked() {
540            // nothing for now
541        }
542
543        public void destroyLocked() {
544            for (int i = mSessions.size() - 1; i >= 0; i--) {
545                MediaSessionRecord session = mSessions.get(i);
546                MediaSessionService.this.destroySessionLocked(session);
547            }
548        }
549
550        public ArrayList<MediaSessionRecord> getSessionsLocked() {
551            return mSessions;
552        }
553
554        public void addSessionLocked(MediaSessionRecord session) {
555            mSessions.add(session);
556        }
557
558        public void removeSessionLocked(MediaSessionRecord session) {
559            mSessions.remove(session);
560        }
561
562        public void dumpLocked(PrintWriter pw, String prefix) {
563            pw.println(prefix + "Record for user " + mUserId);
564            String indent = prefix + "  ";
565            pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
566            pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
567            int size = mSessions.size();
568            pw.println(indent + size + " Sessions:");
569            for (int i = 0; i < size; i++) {
570                // Just print the short version, the full session dump will
571                // already be in the list of all sessions.
572                pw.println(indent + mSessions.get(i).toString());
573            }
574        }
575
576        private void restoreMediaButtonReceiver() {
577            String receiverName = Settings.Secure.getStringForUser(mContentResolver,
578                    Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
579            if (!TextUtils.isEmpty(receiverName)) {
580                ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
581                if (eventReceiver == null) {
582                    // an invalid name was persisted
583                    return;
584                }
585                mRestoredMediaButtonReceiver = eventReceiver;
586            }
587        }
588    }
589
590    final class SessionsListenerRecord implements IBinder.DeathRecipient {
591        private final IActiveSessionsListener mListener;
592        private final ComponentName mComponentName;
593        private final int mUserId;
594        private final int mPid;
595        private final int mUid;
596
597        public SessionsListenerRecord(IActiveSessionsListener listener,
598                ComponentName componentName,
599                int userId, int pid, int uid) {
600            mListener = listener;
601            mComponentName = componentName;
602            mUserId = userId;
603            mPid = pid;
604            mUid = uid;
605        }
606
607        @Override
608        public void binderDied() {
609            synchronized (mLock) {
610                mSessionsListeners.remove(this);
611            }
612        }
613    }
614
615    final class SettingsObserver extends ContentObserver {
616        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
617                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
618
619        private SettingsObserver() {
620            super(null);
621        }
622
623        private void observe() {
624            mContentResolver.registerContentObserver(mSecureSettingsUri,
625                    false, this, UserHandle.USER_ALL);
626        }
627
628        @Override
629        public void onChange(boolean selfChange, Uri uri) {
630            updateActiveSessionListeners();
631        }
632    }
633
634    class SessionManagerImpl extends ISessionManager.Stub {
635        private static final String EXTRA_WAKELOCK_ACQUIRED =
636                "android.media.AudioService.WAKELOCK_ACQUIRED";
637        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
638
639        private boolean mVoiceButtonDown = false;
640        private boolean mVoiceButtonHandled = false;
641
642        @Override
643        public ISession createSession(String packageName, ISessionCallback cb, String tag,
644                int userId) throws RemoteException {
645            final int pid = Binder.getCallingPid();
646            final int uid = Binder.getCallingUid();
647            final long token = Binder.clearCallingIdentity();
648            try {
649                enforcePackageName(packageName, uid);
650                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
651                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
652                if (cb == null) {
653                    throw new IllegalArgumentException("Controller callback cannot be null");
654                }
655                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
656                        .getSessionBinder();
657            } finally {
658                Binder.restoreCallingIdentity(token);
659            }
660        }
661
662        @Override
663        public List<IBinder> getSessions(ComponentName componentName, int userId) {
664            final int pid = Binder.getCallingPid();
665            final int uid = Binder.getCallingUid();
666            final long token = Binder.clearCallingIdentity();
667
668            try {
669                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
670                ArrayList<IBinder> binders = new ArrayList<IBinder>();
671                synchronized (mLock) {
672                    ArrayList<MediaSessionRecord> records = mPriorityStack
673                            .getActiveSessions(resolvedUserId);
674                    int size = records.size();
675                    for (int i = 0; i < size; i++) {
676                        binders.add(records.get(i).getControllerBinder().asBinder());
677                    }
678                }
679                return binders;
680            } finally {
681                Binder.restoreCallingIdentity(token);
682            }
683        }
684
685        @Override
686        public void addSessionsListener(IActiveSessionsListener listener,
687                ComponentName componentName, int userId) throws RemoteException {
688            final int pid = Binder.getCallingPid();
689            final int uid = Binder.getCallingUid();
690            final long token = Binder.clearCallingIdentity();
691
692            try {
693                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
694                synchronized (mLock) {
695                    int index = findIndexOfSessionsListenerLocked(listener);
696                    if (index != -1) {
697                        Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
698                        return;
699                    }
700                    SessionsListenerRecord record = new SessionsListenerRecord(listener,
701                            componentName, resolvedUserId, pid, uid);
702                    try {
703                        listener.asBinder().linkToDeath(record, 0);
704                    } catch (RemoteException e) {
705                        Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
706                        return;
707                    }
708                    mSessionsListeners.add(record);
709                }
710            } finally {
711                Binder.restoreCallingIdentity(token);
712            }
713        }
714
715        @Override
716        public void removeSessionsListener(IActiveSessionsListener listener)
717                throws RemoteException {
718            synchronized (mLock) {
719                int index = findIndexOfSessionsListenerLocked(listener);
720                if (index != -1) {
721                    SessionsListenerRecord record = mSessionsListeners.remove(index);
722                    try {
723                        record.mListener.asBinder().unlinkToDeath(record, 0);
724                    } catch (Exception e) {
725                        // ignore exceptions, the record is being removed
726                    }
727                }
728            }
729        }
730
731        /**
732         * Handles the dispatching of the media button events to one of the
733         * registered listeners, or if there was none, broadcast an
734         * ACTION_MEDIA_BUTTON intent to the rest of the system.
735         *
736         * @param keyEvent a non-null KeyEvent whose key code is one of the
737         *            supported media buttons
738         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
739         *            while this key event is dispatched.
740         */
741        @Override
742        public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
743            if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
744                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
745                return;
746            }
747
748            final int pid = Binder.getCallingPid();
749            final int uid = Binder.getCallingUid();
750            final long token = Binder.clearCallingIdentity();
751            try {
752                if (DEBUG) {
753                    Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
754                            + keyEvent);
755                }
756                if (!isUserSetupComplete()) {
757                    // Global media key handling can have the side-effect of starting new
758                    // activities which is undesirable while setup is in progress.
759                    Slog.i(TAG, "Not dispatching media key event because user "
760                            + "setup is in progress.");
761                    return;
762                }
763
764                synchronized (mLock) {
765                    // If we don't have a media button receiver to fall back on
766                    // include non-playing sessions for dispatching
767                    UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
768                    boolean useNotPlayingSessions = (ur == null) ||
769                            (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                    mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
893                            flags, packageName, TAG);
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.CURRENT,
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        private boolean isUserSetupComplete() {
1039            return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1040                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1041        }
1042
1043        // we only handle public stream types, which are 0-5
1044        private boolean isValidLocalStreamType(int streamType) {
1045            return streamType >= AudioManager.STREAM_VOICE_CALL
1046                    && streamType <= AudioManager.STREAM_NOTIFICATION;
1047        }
1048
1049        private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1050
1051        class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1052                PendingIntent.OnFinished {
1053            private final Handler mHandler;
1054            private int mRefCount = 0;
1055            private int mLastTimeoutId = 0;
1056
1057            public KeyEventWakeLockReceiver(Handler handler) {
1058                super(handler);
1059                mHandler = handler;
1060            }
1061
1062            public void onTimeout() {
1063                synchronized (mLock) {
1064                    if (mRefCount == 0) {
1065                        // We've already released it, so just return
1066                        return;
1067                    }
1068                    mLastTimeoutId++;
1069                    mRefCount = 0;
1070                    releaseWakeLockLocked();
1071                }
1072            }
1073
1074            public void aquireWakeLockLocked() {
1075                if (mRefCount == 0) {
1076                    mMediaEventWakeLock.acquire();
1077                }
1078                mRefCount++;
1079                mHandler.removeCallbacks(this);
1080                mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1081
1082            }
1083
1084            @Override
1085            public void run() {
1086                onTimeout();
1087            }
1088
1089            @Override
1090            protected void onReceiveResult(int resultCode, Bundle resultData) {
1091                if (resultCode < mLastTimeoutId) {
1092                    // Ignore results from calls that were before the last
1093                    // timeout, just in case.
1094                    return;
1095                } else {
1096                    synchronized (mLock) {
1097                        if (mRefCount > 0) {
1098                            mRefCount--;
1099                            if (mRefCount == 0) {
1100                                releaseWakeLockLocked();
1101                            }
1102                        }
1103                    }
1104                }
1105            }
1106
1107            private void releaseWakeLockLocked() {
1108                mMediaEventWakeLock.release();
1109                mHandler.removeCallbacks(this);
1110            }
1111
1112            @Override
1113            public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1114                    String resultData, Bundle resultExtras) {
1115                onReceiveResult(resultCode, null);
1116            }
1117        };
1118
1119        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1120            @Override
1121            public void onReceive(Context context, Intent intent) {
1122                if (intent == null) {
1123                    return;
1124                }
1125                Bundle extras = intent.getExtras();
1126                if (extras == null) {
1127                    return;
1128                }
1129                synchronized (mLock) {
1130                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1131                            && mMediaEventWakeLock.isHeld()) {
1132                        mMediaEventWakeLock.release();
1133                    }
1134                }
1135            }
1136        };
1137    }
1138
1139    final class MessageHandler extends Handler {
1140        private static final int MSG_SESSIONS_CHANGED = 1;
1141
1142        @Override
1143        public void handleMessage(Message msg) {
1144            switch (msg.what) {
1145                case MSG_SESSIONS_CHANGED:
1146                    pushSessionsChanged(msg.arg1);
1147                    break;
1148            }
1149        }
1150
1151        public void post(int what, int arg1, int arg2) {
1152            obtainMessage(what, arg1, arg2).sendToTarget();
1153        }
1154    }
1155}
1156