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