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