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