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