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