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