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