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