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