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