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