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