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