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