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