• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MediaSessionService(Context context)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
onStart()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 
getAudioService()134     private IAudioService getAudioService() {
135         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
136         return IAudioService.Stub.asInterface(b);
137     }
138 
updateSession(MediaSessionRecord record)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      */
notifyRemoteVolumeChanged(int flags, MediaSessionRecord session)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 
onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState)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 
onSessionPlaybackTypeChanged(MediaSessionRecord record)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
onStartUser(int userId)189     public void onStartUser(int userId) {
190         if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
191         updateUser();
192     }
193 
194     @Override
onSwitchUser(int userId)195     public void onSwitchUser(int userId) {
196         if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
197         updateUser();
198     }
199 
200     @Override
onStopUser(int userId)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
monitor()213     public void monitor() {
214         synchronized (mLock) {
215             // Check for deadlock
216         }
217     }
218 
enforcePhoneStatePermission(int pid, int uid)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 
sessionDied(MediaSessionRecord session)226     void sessionDied(MediaSessionRecord session) {
227         synchronized (mLock) {
228             destroySessionLocked(session);
229         }
230     }
231 
destroySession(MediaSessionRecord session)232     void destroySession(MediaSessionRecord session) {
233         synchronized (mLock) {
234             destroySessionLocked(session);
235         }
236     }
237 
updateUser()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 
updateActiveSessionListeners()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      */
destroyUserLocked(UserRecord user)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      */
destroySessionLocked(MediaSessionRecord session)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 
enforcePackageName(String packageName, int uid)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      */
enforceMediaPermissions(ComponentName compName, int pid, int uid, int resolvedUserId)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 
isCurrentVolumeController(int uid)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 
enforceSystemUiPermission(String action, int pid, int uid)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      */
isEnabledNotificationListener(ComponentName compName, int userId, int forUserId)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 
createSessionInternal(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag)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      */
createSessionLocked(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag)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 
findIndexOfSessionsListenerLocked(IActiveSessionsListener listener)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 
pushSessionsChanged(int userId)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 
pushRemoteVolumeUpdateLocked(int userId)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 
rememberMediaButtonReceiverLocked(MediaSessionRecord record)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 
UserRecord(Context context, int userId)542         public UserRecord(Context context, int userId) {
543             mContext = context;
544             mUserId = userId;
545             restoreMediaButtonReceiver();
546         }
547 
destroyLocked()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 
getSessionsLocked()555         public ArrayList<MediaSessionRecord> getSessionsLocked() {
556             return mSessions;
557         }
558 
addSessionLocked(MediaSessionRecord session)559         public void addSessionLocked(MediaSessionRecord session) {
560             mSessions.add(session);
561         }
562 
removeSessionLocked(MediaSessionRecord session)563         public void removeSessionLocked(MediaSessionRecord session) {
564             mSessions.remove(session);
565         }
566 
dumpLocked(PrintWriter pw, String prefix)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 
restoreMediaButtonReceiver()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 
SessionsListenerRecord(IActiveSessionsListener listener, ComponentName componentName, int userId, int pid, int uid)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
binderDied()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 
SettingsObserver()624         private SettingsObserver() {
625             super(null);
626         }
627 
observe()628         private void observe() {
629             mContentResolver.registerContentObserver(mSecureSettingsUri,
630                     false, this, UserHandle.USER_ALL);
631         }
632 
633         @Override
onChange(boolean selfChange, Uri uri)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
createSession(String packageName, ISessionCallback cb, String tag, int userId)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
getSessions(ComponentName componentName, int userId)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
addSessionsListener(IActiveSessionsListener listener, ComponentName componentName, int userId)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
removeSessionsListener(IActiveSessionsListener listener)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
dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock)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                 if (isGlobalPriorityActive() && uid != Process.SYSTEM_UID) {
769                     // Prevent dispatching key event through reflection while the global priority
770                     // session is active.
771                     Slog.i(TAG, "Only the system can dispatch media key event "
772                             + "to the global priority session.");
773                     return;
774                 }
775 
776                 synchronized (mLock) {
777                     // If we don't have a media button receiver to fall back on
778                     // include non-playing sessions for dispatching
779                     boolean useNotPlayingSessions = true;
780                     for (int userId : mCurrentUserIdList) {
781                         UserRecord ur = mUserRecords.get(userId);
782                         if (ur.mLastMediaButtonReceiver != null
783                                 || ur.mRestoredMediaButtonReceiver != null) {
784                             useNotPlayingSessions = false;
785                             break;
786                         }
787                     }
788 
789                     if (DEBUG) {
790                         Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
791                                 + useNotPlayingSessions);
792                     }
793                     MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
794                             mCurrentUserIdList, useNotPlayingSessions);
795                     if (isVoiceKey(keyEvent.getKeyCode())) {
796                         handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
797                     } else {
798                         dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
799                     }
800                 }
801             } finally {
802                 Binder.restoreCallingIdentity(token);
803             }
804         }
805 
806         @Override
dispatchAdjustVolume(int suggestedStream, int delta, int flags)807         public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
808             final long token = Binder.clearCallingIdentity();
809             try {
810                 synchronized (mLock) {
811                     MediaSessionRecord session = mPriorityStack
812                             .getDefaultVolumeSession(mCurrentUserIdList);
813                     dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
814                 }
815             } finally {
816                 Binder.restoreCallingIdentity(token);
817             }
818         }
819 
820         @Override
setRemoteVolumeController(IRemoteVolumeController rvc)821         public void setRemoteVolumeController(IRemoteVolumeController rvc) {
822             final int pid = Binder.getCallingPid();
823             final int uid = Binder.getCallingUid();
824             final long token = Binder.clearCallingIdentity();
825             try {
826                 enforceSystemUiPermission("listen for volume changes", pid, uid);
827                 mRvc = rvc;
828             } finally {
829                 Binder.restoreCallingIdentity(token);
830             }
831         }
832 
833         @Override
isGlobalPriorityActive()834         public boolean isGlobalPriorityActive() {
835             return mPriorityStack.isGlobalPriorityActive();
836         }
837 
838         @Override
dump(FileDescriptor fd, final PrintWriter pw, String[] args)839         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
840             if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
841                     != PackageManager.PERMISSION_GRANTED) {
842                 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
843                         + Binder.getCallingPid()
844                         + ", uid=" + Binder.getCallingUid());
845                 return;
846             }
847 
848             pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
849             pw.println();
850 
851             synchronized (mLock) {
852                 pw.println(mSessionsListeners.size() + " sessions listeners.");
853                 int count = mAllSessions.size();
854                 pw.println(count + " Sessions:");
855                 for (int i = 0; i < count; i++) {
856                     mAllSessions.get(i).dump(pw, "");
857                     pw.println();
858                 }
859                 mPriorityStack.dump(pw, "");
860 
861                 pw.println("User Records:");
862                 count = mUserRecords.size();
863                 for (int i = 0; i < count; i++) {
864                     UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
865                     user.dumpLocked(pw, "");
866                 }
867             }
868         }
869 
verifySessionsRequest(ComponentName componentName, int userId, final int pid, final int uid)870         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
871                 final int uid) {
872             String packageName = null;
873             if (componentName != null) {
874                 // If they gave us a component name verify they own the
875                 // package
876                 packageName = componentName.getPackageName();
877                 enforcePackageName(packageName, uid);
878             }
879             // Check that they can make calls on behalf of the user and
880             // get the final user id
881             int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
882                     true /* allowAll */, true /* requireFull */, "getSessions", packageName);
883             // Check if they have the permissions or their component is
884             // enabled for the user they're calling from.
885             enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
886             return resolvedUserId;
887         }
888 
dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags, MediaSessionRecord session)889         private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
890                 MediaSessionRecord session) {
891             if (DEBUG) {
892                 String description = session == null ? null : session.toString();
893                 Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
894                         + flags + ", suggestedStream=" + suggestedStream);
895 
896             }
897             boolean preferSuggestedStream = false;
898             if (isValidLocalStreamType(suggestedStream)
899                     && AudioSystem.isStreamActive(suggestedStream, 0)) {
900                 preferSuggestedStream = true;
901             }
902             if (session == null || preferSuggestedStream) {
903                 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
904                         && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
905                     if (DEBUG) {
906                         Log.d(TAG, "No active session to adjust, skipping media only volume event");
907                     }
908                     return;
909                 }
910                 try {
911                     String packageName = getContext().getOpPackageName();
912                     mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
913                             flags, packageName, TAG);
914                 } catch (RemoteException e) {
915                     Log.e(TAG, "Error adjusting default volume.", e);
916                 }
917             } else {
918                 session.adjustVolume(direction, flags, getContext().getPackageName(),
919                         Process.SYSTEM_UID, true);
920             }
921         }
922 
handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, MediaSessionRecord session)923         private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
924                 MediaSessionRecord session) {
925             if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
926                 // If the phone app has priority just give it the event
927                 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
928                 return;
929             }
930             int action = keyEvent.getAction();
931             boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
932             if (action == KeyEvent.ACTION_DOWN) {
933                 if (keyEvent.getRepeatCount() == 0) {
934                     mVoiceButtonDown = true;
935                     mVoiceButtonHandled = false;
936                 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
937                     mVoiceButtonHandled = true;
938                     startVoiceInput(needWakeLock);
939                 }
940             } else if (action == KeyEvent.ACTION_UP) {
941                 if (mVoiceButtonDown) {
942                     mVoiceButtonDown = false;
943                     if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
944                         // Resend the down then send this event through
945                         KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
946                         dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
947                         dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
948                     }
949                 }
950             }
951         }
952 
dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, MediaSessionRecord session)953         private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
954                 MediaSessionRecord session) {
955             if (session != null) {
956                 if (DEBUG) {
957                     Log.d(TAG, "Sending media key to " + session.toString());
958                 }
959                 if (needWakeLock) {
960                     mKeyEventReceiver.aquireWakeLockLocked();
961                 }
962                 // If we don't need a wakelock use -1 as the id so we
963                 // won't release it later
964                 session.sendMediaButton(keyEvent,
965                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
966                         mKeyEventReceiver, Process.SYSTEM_UID,
967                         getContext().getPackageName());
968             } else {
969                 // Launch the last PendingIntent we had with priority
970                 for (int userId : mCurrentUserIdList) {
971                     UserRecord user = mUserRecords.get(userId);
972                     if (user.mLastMediaButtonReceiver == null
973                             && user.mRestoredMediaButtonReceiver == null) {
974                         continue;
975                     }
976                     if (DEBUG) {
977                         Log.d(TAG, "Sending media key to last known PendingIntent "
978                                 + user.mLastMediaButtonReceiver + " or restored Intent "
979                                 + user.mRestoredMediaButtonReceiver);
980                     }
981                     if (needWakeLock) {
982                         mKeyEventReceiver.aquireWakeLockLocked();
983                     }
984                     Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
985                     mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
986                     mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
987                     try {
988                         if (user.mLastMediaButtonReceiver != null) {
989                             user.mLastMediaButtonReceiver.send(getContext(),
990                                     needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
991                                     mediaButtonIntent, mKeyEventReceiver, mHandler);
992                         } else {
993                             mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
994                             getContext().sendBroadcastAsUser(mediaButtonIntent,
995                                     UserHandle.of(userId));
996                         }
997                     } catch (CanceledException e) {
998                         Log.i(TAG, "Error sending key event to media button receiver "
999                                 + user.mLastMediaButtonReceiver, e);
1000                     }
1001                     return;
1002                 }
1003                 if (DEBUG) {
1004                     Log.d(TAG, "Sending media key ordered broadcast");
1005                 }
1006                 if (needWakeLock) {
1007                     mMediaEventWakeLock.acquire();
1008                 }
1009                 // Fallback to legacy behavior
1010                 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1011                 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1012                 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1013                 if (needWakeLock) {
1014                     keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1015                 }
1016                 // Send broadcast only to the full user.
1017                 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1018                         null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
1019             }
1020         }
1021 
startVoiceInput(boolean needWakeLock)1022         private void startVoiceInput(boolean needWakeLock) {
1023             Intent voiceIntent = null;
1024             // select which type of search to launch:
1025             // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1026             // - device locked or screen off: action is
1027             // ACTION_VOICE_SEARCH_HANDS_FREE
1028             // with EXTRA_SECURE set to true if the device is securely locked
1029             PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1030             boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1031             if (!isLocked && pm.isScreenOn()) {
1032                 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1033                 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1034             } else {
1035                 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1036                 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1037                         isLocked && mKeyguardManager.isKeyguardSecure());
1038                 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1039             }
1040             // start the search activity
1041             if (needWakeLock) {
1042                 mMediaEventWakeLock.acquire();
1043             }
1044             try {
1045                 if (voiceIntent != null) {
1046                     voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1047                             | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1048                     if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
1049                     getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1050                 }
1051             } catch (ActivityNotFoundException e) {
1052                 Log.w(TAG, "No activity for search: " + e);
1053             } finally {
1054                 if (needWakeLock) {
1055                     mMediaEventWakeLock.release();
1056                 }
1057             }
1058         }
1059 
isVoiceKey(int keyCode)1060         private boolean isVoiceKey(int keyCode) {
1061             return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1062         }
1063 
isUserSetupComplete()1064         private boolean isUserSetupComplete() {
1065             return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1066                     Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1067         }
1068 
1069         // we only handle public stream types, which are 0-5
isValidLocalStreamType(int streamType)1070         private boolean isValidLocalStreamType(int streamType) {
1071             return streamType >= AudioManager.STREAM_VOICE_CALL
1072                     && streamType <= AudioManager.STREAM_NOTIFICATION;
1073         }
1074 
1075         private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1076 
1077         class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1078                 PendingIntent.OnFinished {
1079             private final Handler mHandler;
1080             private int mRefCount = 0;
1081             private int mLastTimeoutId = 0;
1082 
KeyEventWakeLockReceiver(Handler handler)1083             public KeyEventWakeLockReceiver(Handler handler) {
1084                 super(handler);
1085                 mHandler = handler;
1086             }
1087 
onTimeout()1088             public void onTimeout() {
1089                 synchronized (mLock) {
1090                     if (mRefCount == 0) {
1091                         // We've already released it, so just return
1092                         return;
1093                     }
1094                     mLastTimeoutId++;
1095                     mRefCount = 0;
1096                     releaseWakeLockLocked();
1097                 }
1098             }
1099 
aquireWakeLockLocked()1100             public void aquireWakeLockLocked() {
1101                 if (mRefCount == 0) {
1102                     mMediaEventWakeLock.acquire();
1103                 }
1104                 mRefCount++;
1105                 mHandler.removeCallbacks(this);
1106                 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1107 
1108             }
1109 
1110             @Override
run()1111             public void run() {
1112                 onTimeout();
1113             }
1114 
1115             @Override
onReceiveResult(int resultCode, Bundle resultData)1116             protected void onReceiveResult(int resultCode, Bundle resultData) {
1117                 if (resultCode < mLastTimeoutId) {
1118                     // Ignore results from calls that were before the last
1119                     // timeout, just in case.
1120                     return;
1121                 } else {
1122                     synchronized (mLock) {
1123                         if (mRefCount > 0) {
1124                             mRefCount--;
1125                             if (mRefCount == 0) {
1126                                 releaseWakeLockLocked();
1127                             }
1128                         }
1129                     }
1130                 }
1131             }
1132 
releaseWakeLockLocked()1133             private void releaseWakeLockLocked() {
1134                 mMediaEventWakeLock.release();
1135                 mHandler.removeCallbacks(this);
1136             }
1137 
1138             @Override
onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, String resultData, Bundle resultExtras)1139             public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1140                     String resultData, Bundle resultExtras) {
1141                 onReceiveResult(resultCode, null);
1142             }
1143         };
1144 
1145         BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1146             @Override
1147             public void onReceive(Context context, Intent intent) {
1148                 if (intent == null) {
1149                     return;
1150                 }
1151                 Bundle extras = intent.getExtras();
1152                 if (extras == null) {
1153                     return;
1154                 }
1155                 synchronized (mLock) {
1156                     if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1157                             && mMediaEventWakeLock.isHeld()) {
1158                         mMediaEventWakeLock.release();
1159                     }
1160                 }
1161             }
1162         };
1163     }
1164 
1165     final class MessageHandler extends Handler {
1166         private static final int MSG_SESSIONS_CHANGED = 1;
1167 
1168         @Override
handleMessage(Message msg)1169         public void handleMessage(Message msg) {
1170             switch (msg.what) {
1171                 case MSG_SESSIONS_CHANGED:
1172                     pushSessionsChanged(msg.arg1);
1173                     break;
1174             }
1175         }
1176 
post(int what, int arg1, int arg2)1177         public void post(int what, int arg1, int arg2) {
1178             obtainMessage(what, arg1, arg2).sendToTarget();
1179         }
1180     }
1181 }
1182