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