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