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