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 17 package com.android.server.media; 18 19 import android.Manifest; 20 import android.app.Activity; 21 import android.app.ActivityManager; 22 import android.app.KeyguardManager; 23 import android.app.PendingIntent; 24 import android.app.PendingIntent.CanceledException; 25 import android.content.ActivityNotFoundException; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.PackageManager; 32 import android.database.ContentObserver; 33 import android.media.AudioManager; 34 import android.media.AudioManagerInternal; 35 import android.media.AudioSystem; 36 import android.media.IAudioService; 37 import android.media.IRemoteVolumeController; 38 import android.media.session.IActiveSessionsListener; 39 import android.media.session.ISession; 40 import android.media.session.ISessionCallback; 41 import android.media.session.ISessionManager; 42 import android.media.session.MediaSession; 43 import android.net.Uri; 44 import android.os.Binder; 45 import android.os.Bundle; 46 import android.os.Handler; 47 import android.os.IBinder; 48 import android.os.Message; 49 import android.os.PowerManager; 50 import android.os.RemoteException; 51 import android.os.ResultReceiver; 52 import android.os.ServiceManager; 53 import android.os.UserHandle; 54 import android.provider.Settings; 55 import android.speech.RecognizerIntent; 56 import android.text.TextUtils; 57 import android.util.Log; 58 import android.util.Slog; 59 import android.util.SparseArray; 60 import android.view.KeyEvent; 61 62 import com.android.server.LocalServices; 63 import com.android.server.SystemService; 64 import com.android.server.Watchdog; 65 import com.android.server.Watchdog.Monitor; 66 67 import java.io.FileDescriptor; 68 import java.io.PrintWriter; 69 import java.util.ArrayList; 70 import java.util.List; 71 72 /** 73 * System implementation of MediaSessionManager 74 */ 75 public class MediaSessionService extends SystemService implements Monitor { 76 private static final String TAG = "MediaSessionService"; 77 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 78 79 private static final int WAKELOCK_TIMEOUT = 5000; 80 81 /* package */final IBinder mICallback = new Binder(); 82 83 private final SessionManagerImpl mSessionManagerImpl; 84 private final MediaSessionStack mPriorityStack; 85 86 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>(); 87 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>(); 88 private final ArrayList<SessionsListenerRecord> mSessionsListeners 89 = new ArrayList<SessionsListenerRecord>(); 90 private final Object mLock = new Object(); 91 private final MessageHandler mHandler = new MessageHandler(); 92 private final PowerManager.WakeLock mMediaEventWakeLock; 93 94 private KeyguardManager mKeyguardManager; 95 private IAudioService mAudioService; 96 private AudioManagerInternal mAudioManagerInternal; 97 private ContentResolver mContentResolver; 98 private SettingsObserver mSettingsObserver; 99 100 private int mCurrentUserId = -1; 101 102 // Used to notify system UI when remote volume was changed. TODO find a 103 // better way to handle this. 104 private IRemoteVolumeController mRvc; 105 106 public MediaSessionService(Context context) { 107 super(context); 108 mSessionManagerImpl = new SessionManagerImpl(); 109 mPriorityStack = new MediaSessionStack(); 110 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 111 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); 112 } 113 114 @Override 115 public void onStart() { 116 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); 117 Watchdog.getInstance().addMonitor(this); 118 mKeyguardManager = 119 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); 120 mAudioService = getAudioService(); 121 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); 122 mContentResolver = getContext().getContentResolver(); 123 mSettingsObserver = new SettingsObserver(); 124 mSettingsObserver.observe(); 125 126 updateUser(); 127 } 128 129 private IAudioService getAudioService() { 130 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 131 return IAudioService.Stub.asInterface(b); 132 } 133 134 public void updateSession(MediaSessionRecord record) { 135 synchronized (mLock) { 136 if (!mAllSessions.contains(record)) { 137 Log.d(TAG, "Unknown session updated. Ignoring."); 138 return; 139 } 140 mPriorityStack.onSessionStateChange(record); 141 } 142 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); 143 } 144 145 /** 146 * Tells the system UI that volume has changed on a remote session. 147 */ 148 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { 149 if (mRvc == null) { 150 return; 151 } 152 try { 153 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); 154 } catch (Exception e) { 155 Log.wtf(TAG, "Error sending volume change to system UI.", e); 156 } 157 } 158 159 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { 160 boolean updateSessions = false; 161 synchronized (mLock) { 162 if (!mAllSessions.contains(record)) { 163 Log.d(TAG, "Unknown session changed playback state. Ignoring."); 164 return; 165 } 166 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState); 167 } 168 if (updateSessions) { 169 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); 170 } 171 } 172 173 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { 174 synchronized (mLock) { 175 if (!mAllSessions.contains(record)) { 176 Log.d(TAG, "Unknown session changed playback type. Ignoring."); 177 return; 178 } 179 pushRemoteVolumeUpdateLocked(record.getUserId()); 180 } 181 } 182 183 @Override 184 public void onStartUser(int userHandle) { 185 updateUser(); 186 } 187 188 @Override 189 public void onSwitchUser(int userHandle) { 190 updateUser(); 191 } 192 193 @Override 194 public void onStopUser(int userHandle) { 195 synchronized (mLock) { 196 UserRecord user = mUserRecords.get(userHandle); 197 if (user != null) { 198 destroyUserLocked(user); 199 } 200 } 201 } 202 203 @Override 204 public void monitor() { 205 synchronized (mLock) { 206 // Check for deadlock 207 } 208 } 209 210 protected void enforcePhoneStatePermission(int pid, int uid) { 211 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) 212 != PackageManager.PERMISSION_GRANTED) { 213 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); 214 } 215 } 216 217 void sessionDied(MediaSessionRecord session) { 218 synchronized (mLock) { 219 destroySessionLocked(session); 220 } 221 } 222 223 void destroySession(MediaSessionRecord session) { 224 synchronized (mLock) { 225 destroySessionLocked(session); 226 } 227 } 228 229 private void updateUser() { 230 synchronized (mLock) { 231 int userId = ActivityManager.getCurrentUser(); 232 if (mCurrentUserId != userId) { 233 final int oldUserId = mCurrentUserId; 234 mCurrentUserId = userId; // do this first 235 236 UserRecord oldUser = mUserRecords.get(oldUserId); 237 if (oldUser != null) { 238 oldUser.stopLocked(); 239 } 240 241 UserRecord newUser = getOrCreateUser(userId); 242 newUser.startLocked(); 243 } 244 } 245 } 246 247 private void updateActiveSessionListeners() { 248 synchronized (mLock) { 249 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 250 SessionsListenerRecord listener = mSessionsListeners.get(i); 251 try { 252 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid, 253 listener.mUserId); 254 } catch (SecurityException e) { 255 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName 256 + " is no longer authorized. Disconnecting."); 257 mSessionsListeners.remove(i); 258 try { 259 listener.mListener 260 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); 261 } catch (Exception e1) { 262 // ignore 263 } 264 } 265 } 266 } 267 } 268 269 /** 270 * Stop the user and unbind from everything. 271 * 272 * @param user The user to dispose of 273 */ 274 private void destroyUserLocked(UserRecord user) { 275 user.stopLocked(); 276 user.destroyLocked(); 277 mUserRecords.remove(user.mUserId); 278 } 279 280 /* 281 * When a session is removed several things need to happen. 282 * 1. We need to remove it from the relevant user. 283 * 2. We need to remove it from the priority stack. 284 * 3. We need to remove it from all sessions. 285 * 4. If this is the system priority session we need to clear it. 286 * 5. We need to unlink to death from the cb binder 287 * 6. We need to tell the session to do any final cleanup (onDestroy) 288 */ 289 private void destroySessionLocked(MediaSessionRecord session) { 290 int userId = session.getUserId(); 291 UserRecord user = mUserRecords.get(userId); 292 if (user != null) { 293 user.removeSessionLocked(session); 294 } 295 296 mPriorityStack.removeSession(session); 297 mAllSessions.remove(session); 298 299 try { 300 session.getCallback().asBinder().unlinkToDeath(session, 0); 301 } catch (Exception e) { 302 // ignore exceptions while destroying a session. 303 } 304 session.onDestroy(); 305 306 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0); 307 } 308 309 private void enforcePackageName(String packageName, int uid) { 310 if (TextUtils.isEmpty(packageName)) { 311 throw new IllegalArgumentException("packageName may not be empty"); 312 } 313 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 314 final int packageCount = packages.length; 315 for (int i = 0; i < packageCount; i++) { 316 if (packageName.equals(packages[i])) { 317 return; 318 } 319 } 320 throw new IllegalArgumentException("packageName is not owned by the calling process"); 321 } 322 323 /** 324 * Checks a caller's authorization to register an IRemoteControlDisplay. 325 * Authorization is granted if one of the following is true: 326 * <ul> 327 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL 328 * permission</li> 329 * <li>the caller's listener is one of the enabled notification listeners 330 * for the caller's user</li> 331 * </ul> 332 */ 333 private void enforceMediaPermissions(ComponentName compName, int pid, int uid, 334 int resolvedUserId) { 335 if (isCurrentVolumeController(uid)) return; 336 if (getContext() 337 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) 338 != PackageManager.PERMISSION_GRANTED 339 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), 340 resolvedUserId)) { 341 throw new SecurityException("Missing permission to control media."); 342 } 343 } 344 345 private boolean isCurrentVolumeController(int uid) { 346 if (mAudioManagerInternal != null) { 347 final int vcuid = mAudioManagerInternal.getVolumeControllerUid(); 348 if (vcuid > 0 && uid == vcuid) { 349 return true; 350 } 351 } 352 return false; 353 } 354 355 private void enforceSystemUiPermission(String action, int pid, int uid) { 356 if (isCurrentVolumeController(uid)) return; 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 void pushSessionsChanged(int userId) { 468 synchronized (mLock) { 469 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId); 470 int size = records.size(); 471 if (size > 0 && records.get(0).isPlaybackActive(false)) { 472 rememberMediaButtonReceiverLocked(records.get(0)); 473 } 474 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); 475 for (int i = 0; i < size; i++) { 476 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); 477 } 478 pushRemoteVolumeUpdateLocked(userId); 479 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 480 SessionsListenerRecord record = mSessionsListeners.get(i); 481 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { 482 try { 483 record.mListener.onActiveSessionsChanged(tokens); 484 } catch (RemoteException e) { 485 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", 486 e); 487 mSessionsListeners.remove(i); 488 } 489 } 490 } 491 } 492 } 493 494 private void pushRemoteVolumeUpdateLocked(int userId) { 495 if (mRvc != null) { 496 try { 497 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId); 498 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder()); 499 } catch (RemoteException e) { 500 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); 501 } 502 } 503 } 504 505 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { 506 PendingIntent receiver = record.getMediaButtonReceiver(); 507 UserRecord user = mUserRecords.get(record.getUserId()); 508 if (receiver != null && user != null) { 509 user.mLastMediaButtonReceiver = receiver; 510 ComponentName component = receiver.getIntent().getComponent(); 511 if (component != null && record.getPackageName().equals(component.getPackageName())) { 512 Settings.Secure.putStringForUser(mContentResolver, 513 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(), 514 record.getUserId()); 515 } 516 } 517 } 518 519 /** 520 * Information about a particular user. The contents of this object is 521 * guarded by mLock. 522 */ 523 final class UserRecord { 524 private final int mUserId; 525 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); 526 private final Context mContext; 527 private PendingIntent mLastMediaButtonReceiver; 528 private ComponentName mRestoredMediaButtonReceiver; 529 530 public UserRecord(Context context, int userId) { 531 mContext = context; 532 mUserId = userId; 533 restoreMediaButtonReceiver(); 534 } 535 536 public void startLocked() { 537 } 538 539 public void stopLocked() { 540 // nothing for now 541 } 542 543 public void destroyLocked() { 544 for (int i = mSessions.size() - 1; i >= 0; i--) { 545 MediaSessionRecord session = mSessions.get(i); 546 MediaSessionService.this.destroySessionLocked(session); 547 } 548 } 549 550 public ArrayList<MediaSessionRecord> getSessionsLocked() { 551 return mSessions; 552 } 553 554 public void addSessionLocked(MediaSessionRecord session) { 555 mSessions.add(session); 556 } 557 558 public void removeSessionLocked(MediaSessionRecord session) { 559 mSessions.remove(session); 560 } 561 562 public void dumpLocked(PrintWriter pw, String prefix) { 563 pw.println(prefix + "Record for user " + mUserId); 564 String indent = prefix + " "; 565 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver); 566 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver); 567 int size = mSessions.size(); 568 pw.println(indent + size + " Sessions:"); 569 for (int i = 0; i < size; i++) { 570 // Just print the short version, the full session dump will 571 // already be in the list of all sessions. 572 pw.println(indent + mSessions.get(i).toString()); 573 } 574 } 575 576 private void restoreMediaButtonReceiver() { 577 String receiverName = Settings.Secure.getStringForUser(mContentResolver, 578 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT); 579 if (!TextUtils.isEmpty(receiverName)) { 580 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); 581 if (eventReceiver == null) { 582 // an invalid name was persisted 583 return; 584 } 585 mRestoredMediaButtonReceiver = eventReceiver; 586 } 587 } 588 } 589 590 final class SessionsListenerRecord implements IBinder.DeathRecipient { 591 private final IActiveSessionsListener mListener; 592 private final ComponentName mComponentName; 593 private final int mUserId; 594 private final int mPid; 595 private final int mUid; 596 597 public SessionsListenerRecord(IActiveSessionsListener listener, 598 ComponentName componentName, 599 int userId, int pid, int uid) { 600 mListener = listener; 601 mComponentName = componentName; 602 mUserId = userId; 603 mPid = pid; 604 mUid = uid; 605 } 606 607 @Override 608 public void binderDied() { 609 synchronized (mLock) { 610 mSessionsListeners.remove(this); 611 } 612 } 613 } 614 615 final class SettingsObserver extends ContentObserver { 616 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( 617 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 618 619 private SettingsObserver() { 620 super(null); 621 } 622 623 private void observe() { 624 mContentResolver.registerContentObserver(mSecureSettingsUri, 625 false, this, UserHandle.USER_ALL); 626 } 627 628 @Override 629 public void onChange(boolean selfChange, Uri uri) { 630 updateActiveSessionListeners(); 631 } 632 } 633 634 class SessionManagerImpl extends ISessionManager.Stub { 635 private static final String EXTRA_WAKELOCK_ACQUIRED = 636 "android.media.AudioService.WAKELOCK_ACQUIRED"; 637 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number 638 639 private boolean mVoiceButtonDown = false; 640 private boolean mVoiceButtonHandled = false; 641 642 @Override 643 public ISession createSession(String packageName, ISessionCallback cb, String tag, 644 int userId) throws RemoteException { 645 final int pid = Binder.getCallingPid(); 646 final int uid = Binder.getCallingUid(); 647 final long token = Binder.clearCallingIdentity(); 648 try { 649 enforcePackageName(packageName, uid); 650 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 651 false /* allowAll */, true /* requireFull */, "createSession", packageName); 652 if (cb == null) { 653 throw new IllegalArgumentException("Controller callback cannot be null"); 654 } 655 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) 656 .getSessionBinder(); 657 } finally { 658 Binder.restoreCallingIdentity(token); 659 } 660 } 661 662 @Override 663 public List<IBinder> getSessions(ComponentName componentName, int userId) { 664 final int pid = Binder.getCallingPid(); 665 final int uid = Binder.getCallingUid(); 666 final long token = Binder.clearCallingIdentity(); 667 668 try { 669 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 670 ArrayList<IBinder> binders = new ArrayList<IBinder>(); 671 synchronized (mLock) { 672 ArrayList<MediaSessionRecord> records = mPriorityStack 673 .getActiveSessions(resolvedUserId); 674 int size = records.size(); 675 for (int i = 0; i < size; i++) { 676 binders.add(records.get(i).getControllerBinder().asBinder()); 677 } 678 } 679 return binders; 680 } finally { 681 Binder.restoreCallingIdentity(token); 682 } 683 } 684 685 @Override 686 public void addSessionsListener(IActiveSessionsListener listener, 687 ComponentName componentName, int userId) throws RemoteException { 688 final int pid = Binder.getCallingPid(); 689 final int uid = Binder.getCallingUid(); 690 final long token = Binder.clearCallingIdentity(); 691 692 try { 693 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 694 synchronized (mLock) { 695 int index = findIndexOfSessionsListenerLocked(listener); 696 if (index != -1) { 697 Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); 698 return; 699 } 700 SessionsListenerRecord record = new SessionsListenerRecord(listener, 701 componentName, resolvedUserId, pid, uid); 702 try { 703 listener.asBinder().linkToDeath(record, 0); 704 } catch (RemoteException e) { 705 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); 706 return; 707 } 708 mSessionsListeners.add(record); 709 } 710 } finally { 711 Binder.restoreCallingIdentity(token); 712 } 713 } 714 715 @Override 716 public void removeSessionsListener(IActiveSessionsListener listener) 717 throws RemoteException { 718 synchronized (mLock) { 719 int index = findIndexOfSessionsListenerLocked(listener); 720 if (index != -1) { 721 SessionsListenerRecord record = mSessionsListeners.remove(index); 722 try { 723 record.mListener.asBinder().unlinkToDeath(record, 0); 724 } catch (Exception e) { 725 // ignore exceptions, the record is being removed 726 } 727 } 728 } 729 } 730 731 /** 732 * Handles the dispatching of the media button events to one of the 733 * registered listeners, or if there was none, broadcast an 734 * ACTION_MEDIA_BUTTON intent to the rest of the system. 735 * 736 * @param keyEvent a non-null KeyEvent whose key code is one of the 737 * supported media buttons 738 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held 739 * while this key event is dispatched. 740 */ 741 @Override 742 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 743 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) { 744 Log.w(TAG, "Attempted to dispatch null or non-media key event."); 745 return; 746 } 747 748 final int pid = Binder.getCallingPid(); 749 final int uid = Binder.getCallingUid(); 750 final long token = Binder.clearCallingIdentity(); 751 try { 752 if (DEBUG) { 753 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event=" 754 + keyEvent); 755 } 756 if (!isUserSetupComplete()) { 757 // Global media key handling can have the side-effect of starting new 758 // activities which is undesirable while setup is in progress. 759 Slog.i(TAG, "Not dispatching media key event because user " 760 + "setup is in progress."); 761 return; 762 } 763 764 synchronized (mLock) { 765 // If we don't have a media button receiver to fall back on 766 // include non-playing sessions for dispatching 767 UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser()); 768 boolean useNotPlayingSessions = (ur == null) || 769 (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 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, 893 flags, packageName, TAG); 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.CURRENT, 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 private boolean isUserSetupComplete() { 1039 return Settings.Secure.getIntForUser(getContext().getContentResolver(), 1040 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; 1041 } 1042 1043 // we only handle public stream types, which are 0-5 1044 private boolean isValidLocalStreamType(int streamType) { 1045 return streamType >= AudioManager.STREAM_VOICE_CALL 1046 && streamType <= AudioManager.STREAM_NOTIFICATION; 1047 } 1048 1049 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); 1050 1051 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable, 1052 PendingIntent.OnFinished { 1053 private final Handler mHandler; 1054 private int mRefCount = 0; 1055 private int mLastTimeoutId = 0; 1056 1057 public KeyEventWakeLockReceiver(Handler handler) { 1058 super(handler); 1059 mHandler = handler; 1060 } 1061 1062 public void onTimeout() { 1063 synchronized (mLock) { 1064 if (mRefCount == 0) { 1065 // We've already released it, so just return 1066 return; 1067 } 1068 mLastTimeoutId++; 1069 mRefCount = 0; 1070 releaseWakeLockLocked(); 1071 } 1072 } 1073 1074 public void aquireWakeLockLocked() { 1075 if (mRefCount == 0) { 1076 mMediaEventWakeLock.acquire(); 1077 } 1078 mRefCount++; 1079 mHandler.removeCallbacks(this); 1080 mHandler.postDelayed(this, WAKELOCK_TIMEOUT); 1081 1082 } 1083 1084 @Override 1085 public void run() { 1086 onTimeout(); 1087 } 1088 1089 @Override 1090 protected void onReceiveResult(int resultCode, Bundle resultData) { 1091 if (resultCode < mLastTimeoutId) { 1092 // Ignore results from calls that were before the last 1093 // timeout, just in case. 1094 return; 1095 } else { 1096 synchronized (mLock) { 1097 if (mRefCount > 0) { 1098 mRefCount--; 1099 if (mRefCount == 0) { 1100 releaseWakeLockLocked(); 1101 } 1102 } 1103 } 1104 } 1105 } 1106 1107 private void releaseWakeLockLocked() { 1108 mMediaEventWakeLock.release(); 1109 mHandler.removeCallbacks(this); 1110 } 1111 1112 @Override 1113 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, 1114 String resultData, Bundle resultExtras) { 1115 onReceiveResult(resultCode, null); 1116 } 1117 }; 1118 1119 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { 1120 @Override 1121 public void onReceive(Context context, Intent intent) { 1122 if (intent == null) { 1123 return; 1124 } 1125 Bundle extras = intent.getExtras(); 1126 if (extras == null) { 1127 return; 1128 } 1129 synchronized (mLock) { 1130 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED) 1131 && mMediaEventWakeLock.isHeld()) { 1132 mMediaEventWakeLock.release(); 1133 } 1134 } 1135 } 1136 }; 1137 } 1138 1139 final class MessageHandler extends Handler { 1140 private static final int MSG_SESSIONS_CHANGED = 1; 1141 1142 @Override 1143 public void handleMessage(Message msg) { 1144 switch (msg.what) { 1145 case MSG_SESSIONS_CHANGED: 1146 pushSessionsChanged(msg.arg1); 1147 break; 1148 } 1149 } 1150 1151 public void post(int what, int arg1, int arg2) { 1152 obtainMessage(what, arg1, arg2).sendToTarget(); 1153 } 1154 } 1155 } 1156