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