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