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