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