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.app.ActivityManager; 20 import android.app.INotificationManager; 21 import android.app.KeyguardManager; 22 import android.app.PendingIntent; 23 import android.app.PendingIntent.CanceledException; 24 import android.content.ActivityNotFoundException; 25 import android.content.BroadcastReceiver; 26 import android.content.ComponentName; 27 import android.content.ContentResolver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.PackageManager; 31 import android.content.pm.UserInfo; 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.ICallback; 40 import android.media.session.IOnMediaKeyListener; 41 import android.media.session.IOnVolumeKeyLongPressListener; 42 import android.media.session.ISession; 43 import android.media.session.ISessionCallback; 44 import android.media.session.ISessionManager; 45 import android.media.session.MediaSession; 46 import android.media.session.MediaSessionManager; 47 import android.net.Uri; 48 import android.os.Binder; 49 import android.os.Bundle; 50 import android.os.Handler; 51 import android.os.IBinder; 52 import android.os.Message; 53 import android.os.PowerManager; 54 import android.os.Process; 55 import android.os.RemoteException; 56 import android.os.ResultReceiver; 57 import android.os.ServiceManager; 58 import android.os.UserHandle; 59 import android.os.UserManager; 60 import android.provider.Settings; 61 import android.speech.RecognizerIntent; 62 import android.text.TextUtils; 63 import android.util.Log; 64 import android.util.Slog; 65 import android.util.SparseArray; 66 import android.util.SparseIntArray; 67 import android.view.KeyEvent; 68 import android.view.ViewConfiguration; 69 70 import com.android.internal.util.DumpUtils; 71 import com.android.server.LocalServices; 72 import com.android.server.SystemService; 73 import com.android.server.Watchdog; 74 import com.android.server.Watchdog.Monitor; 75 76 import java.io.FileDescriptor; 77 import java.io.PrintWriter; 78 import java.util.ArrayList; 79 import java.util.List; 80 81 /** 82 * System implementation of MediaSessionManager 83 */ 84 public class MediaSessionService extends SystemService implements Monitor { 85 private static final String TAG = "MediaSessionService"; 86 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 87 // Leave log for key event always. 88 private static final boolean DEBUG_KEY_EVENT = true; 89 90 private static final int WAKELOCK_TIMEOUT = 5000; 91 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; 92 93 private final SessionManagerImpl mSessionManagerImpl; 94 95 // Keeps the full user id for each user. 96 private final SparseIntArray mFullUserIds = new SparseIntArray(); 97 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>(); 98 private final ArrayList<SessionsListenerRecord> mSessionsListeners 99 = new ArrayList<SessionsListenerRecord>(); 100 private final Object mLock = new Object(); 101 private final MessageHandler mHandler = new MessageHandler(); 102 private final PowerManager.WakeLock mMediaEventWakeLock; 103 private final int mLongPressTimeout; 104 105 private KeyguardManager mKeyguardManager; 106 private IAudioService mAudioService; 107 private AudioManagerInternal mAudioManagerInternal; 108 private ContentResolver mContentResolver; 109 private SettingsObserver mSettingsObserver; 110 private INotificationManager mNotificationManager; 111 private boolean mHasFeatureLeanback; 112 113 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) 114 // It's always not null after the MediaSessionService is started. 115 private FullUserRecord mCurrentFullUserRecord; 116 private MediaSessionRecord mGlobalPrioritySession; 117 private AudioPlaybackMonitor mAudioPlaybackMonitor; 118 119 // Used to notify system UI when remote volume was changed. TODO find a 120 // better way to handle this. 121 private IRemoteVolumeController mRvc; 122 123 public MediaSessionService(Context context) { 124 super(context); 125 mSessionManagerImpl = new SessionManagerImpl(); 126 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 127 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); 128 mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); 129 mNotificationManager = INotificationManager.Stub.asInterface( 130 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 131 } 132 133 @Override 134 public void onStart() { 135 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); 136 Watchdog.getInstance().addMonitor(this); 137 mKeyguardManager = 138 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); 139 mAudioService = getAudioService(); 140 mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(getContext(), mAudioService); 141 mAudioPlaybackMonitor.registerOnAudioPlaybackStartedListener( 142 new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() { 143 @Override 144 public void onAudioPlaybackStarted(int uid) { 145 synchronized (mLock) { 146 FullUserRecord user = 147 getFullUserRecordLocked(UserHandle.getUserId(uid)); 148 if (user != null) { 149 user.mPriorityStack.updateMediaButtonSessionIfNeeded(); 150 } 151 } 152 } 153 }); 154 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); 155 mContentResolver = getContext().getContentResolver(); 156 mSettingsObserver = new SettingsObserver(); 157 mSettingsObserver.observe(); 158 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature( 159 PackageManager.FEATURE_LEANBACK); 160 161 updateUser(); 162 } 163 164 private IAudioService getAudioService() { 165 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 166 return IAudioService.Stub.asInterface(b); 167 } 168 169 private boolean isGlobalPriorityActiveLocked() { 170 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); 171 } 172 173 public void updateSession(MediaSessionRecord record) { 174 synchronized (mLock) { 175 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 176 if (user == null) { 177 Log.w(TAG, "Unknown session updated. Ignoring."); 178 return; 179 } 180 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 181 if (DEBUG_KEY_EVENT) { 182 Log.d(TAG, "Global priority session is updated, active=" + record.isActive()); 183 } 184 user.pushAddressedPlayerChangedLocked(); 185 } else { 186 if (!user.mPriorityStack.contains(record)) { 187 Log.w(TAG, "Unknown session updated. Ignoring."); 188 return; 189 } 190 user.mPriorityStack.onSessionStateChange(record); 191 } 192 mHandler.postSessionsChanged(record.getUserId()); 193 } 194 } 195 196 public void setGlobalPrioritySession(MediaSessionRecord record) { 197 synchronized (mLock) { 198 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 199 if (mGlobalPrioritySession != record) { 200 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession 201 + " to " + record); 202 mGlobalPrioritySession = record; 203 if (user != null && user.mPriorityStack.contains(record)) { 204 // Handle the global priority session separately. 205 // Otherwise, it can be the media button session regardless of the active state 206 // because it or other system components might have been the lastly played media 207 // app. 208 user.mPriorityStack.removeSession(record); 209 } 210 } 211 } 212 } 213 214 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { 215 List<MediaSessionRecord> records = new ArrayList<>(); 216 if (userId == UserHandle.USER_ALL) { 217 int size = mUserRecords.size(); 218 for (int i = 0; i < size; i++) { 219 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); 220 } 221 } else { 222 FullUserRecord user = getFullUserRecordLocked(userId); 223 if (user == null) { 224 Log.w(TAG, "getSessions failed. Unknown user " + userId); 225 return records; 226 } 227 records.addAll(user.mPriorityStack.getActiveSessions(userId)); 228 } 229 230 // Return global priority session at the first whenever it's asked. 231 if (isGlobalPriorityActiveLocked() 232 && (userId == UserHandle.USER_ALL 233 || userId == mGlobalPrioritySession.getUserId())) { 234 records.add(0, mGlobalPrioritySession); 235 } 236 return records; 237 } 238 239 /** 240 * Tells the system UI that volume has changed on an active remote session. 241 */ 242 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { 243 if (mRvc == null || !session.isActive()) { 244 return; 245 } 246 try { 247 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); 248 } catch (Exception e) { 249 Log.wtf(TAG, "Error sending volume change to system UI.", e); 250 } 251 } 252 253 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { 254 synchronized (mLock) { 255 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 256 if (user == null || !user.mPriorityStack.contains(record)) { 257 Log.d(TAG, "Unknown session changed playback state. Ignoring."); 258 return; 259 } 260 user.mPriorityStack.onPlaystateChanged(record, oldState, newState); 261 } 262 } 263 264 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { 265 synchronized (mLock) { 266 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 267 if (user == null || !user.mPriorityStack.contains(record)) { 268 Log.d(TAG, "Unknown session changed playback type. Ignoring."); 269 return; 270 } 271 pushRemoteVolumeUpdateLocked(record.getUserId()); 272 } 273 } 274 275 @Override 276 public void onStartUser(int userId) { 277 if (DEBUG) Log.d(TAG, "onStartUser: " + userId); 278 updateUser(); 279 } 280 281 @Override 282 public void onSwitchUser(int userId) { 283 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId); 284 updateUser(); 285 } 286 287 @Override 288 public void onStopUser(int userId) { 289 if (DEBUG) Log.d(TAG, "onStopUser: " + userId); 290 synchronized (mLock) { 291 FullUserRecord user = getFullUserRecordLocked(userId); 292 if (user != null) { 293 if (user.mFullUserId == userId) { 294 user.destroySessionsForUserLocked(UserHandle.USER_ALL); 295 mUserRecords.remove(userId); 296 } else { 297 user.destroySessionsForUserLocked(userId); 298 } 299 } 300 updateUser(); 301 } 302 } 303 304 @Override 305 public void monitor() { 306 synchronized (mLock) { 307 // Check for deadlock 308 } 309 } 310 311 protected void enforcePhoneStatePermission(int pid, int uid) { 312 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) 313 != PackageManager.PERMISSION_GRANTED) { 314 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); 315 } 316 } 317 318 void sessionDied(MediaSessionRecord session) { 319 synchronized (mLock) { 320 destroySessionLocked(session); 321 } 322 } 323 324 void destroySession(MediaSessionRecord session) { 325 synchronized (mLock) { 326 destroySessionLocked(session); 327 } 328 } 329 330 private void updateUser() { 331 synchronized (mLock) { 332 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); 333 mFullUserIds.clear(); 334 List<UserInfo> allUsers = manager.getUsers(); 335 if (allUsers != null) { 336 for (UserInfo userInfo : allUsers) { 337 if (userInfo.isManagedProfile()) { 338 mFullUserIds.put(userInfo.id, userInfo.profileGroupId); 339 } else { 340 mFullUserIds.put(userInfo.id, userInfo.id); 341 if (mUserRecords.get(userInfo.id) == null) { 342 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id)); 343 } 344 } 345 } 346 } 347 // Ensure that the current full user exists. 348 int currentFullUserId = ActivityManager.getCurrentUser(); 349 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId); 350 if (mCurrentFullUserRecord == null) { 351 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); 352 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); 353 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); 354 } 355 mFullUserIds.put(currentFullUserId, currentFullUserId); 356 } 357 } 358 359 private void updateActiveSessionListeners() { 360 synchronized (mLock) { 361 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 362 SessionsListenerRecord listener = mSessionsListeners.get(i); 363 try { 364 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid, 365 listener.mUserId); 366 } catch (SecurityException e) { 367 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName 368 + " is no longer authorized. Disconnecting."); 369 mSessionsListeners.remove(i); 370 try { 371 listener.mListener 372 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); 373 } catch (Exception e1) { 374 // ignore 375 } 376 } 377 } 378 } 379 } 380 381 /* 382 * When a session is removed several things need to happen. 383 * 1. We need to remove it from the relevant user. 384 * 2. We need to remove it from the priority stack. 385 * 3. We need to remove it from all sessions. 386 * 4. If this is the system priority session we need to clear it. 387 * 5. We need to unlink to death from the cb binder 388 * 6. We need to tell the session to do any final cleanup (onDestroy) 389 */ 390 private void destroySessionLocked(MediaSessionRecord session) { 391 if (DEBUG) { 392 Log.d(TAG, "Destroying " + session); 393 } 394 FullUserRecord user = getFullUserRecordLocked(session.getUserId()); 395 if (mGlobalPrioritySession == session) { 396 mGlobalPrioritySession = null; 397 if (session.isActive() && user != null) { 398 user.pushAddressedPlayerChangedLocked(); 399 } 400 } else { 401 if (user != null) { 402 user.mPriorityStack.removeSession(session); 403 } 404 } 405 406 try { 407 session.getCallback().asBinder().unlinkToDeath(session, 0); 408 } catch (Exception e) { 409 // ignore exceptions while destroying a session. 410 } 411 session.onDestroy(); 412 mHandler.postSessionsChanged(session.getUserId()); 413 } 414 415 private void enforcePackageName(String packageName, int uid) { 416 if (TextUtils.isEmpty(packageName)) { 417 throw new IllegalArgumentException("packageName may not be empty"); 418 } 419 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 420 final int packageCount = packages.length; 421 for (int i = 0; i < packageCount; i++) { 422 if (packageName.equals(packages[i])) { 423 return; 424 } 425 } 426 throw new IllegalArgumentException("packageName is not owned by the calling process"); 427 } 428 429 /** 430 * Checks a caller's authorization to register an IRemoteControlDisplay. 431 * Authorization is granted if one of the following is true: 432 * <ul> 433 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL 434 * permission</li> 435 * <li>the caller's listener is one of the enabled notification listeners 436 * for the caller's user</li> 437 * </ul> 438 */ 439 private void enforceMediaPermissions(ComponentName compName, int pid, int uid, 440 int resolvedUserId) { 441 if (isCurrentVolumeController(uid, pid)) return; 442 if (getContext() 443 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) 444 != PackageManager.PERMISSION_GRANTED 445 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), 446 resolvedUserId)) { 447 throw new SecurityException("Missing permission to control media."); 448 } 449 } 450 451 private boolean isCurrentVolumeController(int uid, int pid) { 452 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 453 pid, uid) == PackageManager.PERMISSION_GRANTED; 454 } 455 456 private void enforceSystemUiPermission(String action, int pid, int uid) { 457 if (!isCurrentVolumeController(uid, pid)) { 458 throw new SecurityException("Only system ui may " + action); 459 } 460 } 461 462 /** 463 * This checks if the component is an enabled notification listener for the 464 * specified user. Enabled components may only operate on behalf of the user 465 * they're running as. 466 * 467 * @param compName The component that is enabled. 468 * @param userId The user id of the caller. 469 * @param forUserId The user id they're making the request on behalf of. 470 * @return True if the component is enabled, false otherwise 471 */ 472 private boolean isEnabledNotificationListener(ComponentName compName, int userId, 473 int forUserId) { 474 if (userId != forUserId) { 475 // You may not access another user's content as an enabled listener. 476 return false; 477 } 478 if (DEBUG) { 479 Log.d(TAG, "Checking if enabled notification listener " + compName); 480 } 481 if (compName != null) { 482 try { 483 return mNotificationManager.isNotificationListenerAccessGrantedForUser( 484 compName, userId); 485 } catch(RemoteException e) { 486 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e); 487 } 488 } 489 return false; 490 } 491 492 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, 493 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException { 494 synchronized (mLock) { 495 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); 496 } 497 } 498 499 /* 500 * When a session is created the following things need to happen. 501 * 1. Its callback binder needs a link to death 502 * 2. It needs to be added to all sessions. 503 * 3. It needs to be added to the priority stack. 504 * 4. It needs to be added to the relevant user record. 505 */ 506 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, 507 String callerPackageName, ISessionCallback cb, String tag) { 508 FullUserRecord user = getFullUserRecordLocked(userId); 509 if (user == null) { 510 Log.wtf(TAG, "Request from invalid user: " + userId); 511 throw new RuntimeException("Session request from invalid user."); 512 } 513 514 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, 515 callerPackageName, cb, tag, this, mHandler.getLooper()); 516 try { 517 cb.asBinder().linkToDeath(session, 0); 518 } catch (RemoteException e) { 519 throw new RuntimeException("Media Session owner died prematurely.", e); 520 } 521 522 user.mPriorityStack.addSession(session); 523 mHandler.postSessionsChanged(userId); 524 525 if (DEBUG) { 526 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); 527 } 528 return session; 529 } 530 531 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { 532 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 533 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) { 534 return i; 535 } 536 } 537 return -1; 538 } 539 540 private void pushSessionsChanged(int userId) { 541 synchronized (mLock) { 542 FullUserRecord user = getFullUserRecordLocked(userId); 543 if (user == null) { 544 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId); 545 return; 546 } 547 List<MediaSessionRecord> records = getActiveSessionsLocked(userId); 548 int size = records.size(); 549 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); 550 for (int i = 0; i < size; i++) { 551 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); 552 } 553 pushRemoteVolumeUpdateLocked(userId); 554 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 555 SessionsListenerRecord record = mSessionsListeners.get(i); 556 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { 557 try { 558 record.mListener.onActiveSessionsChanged(tokens); 559 } catch (RemoteException e) { 560 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", 561 e); 562 mSessionsListeners.remove(i); 563 } 564 } 565 } 566 } 567 } 568 569 private void pushRemoteVolumeUpdateLocked(int userId) { 570 if (mRvc != null) { 571 try { 572 FullUserRecord user = getFullUserRecordLocked(userId); 573 if (user == null) { 574 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId); 575 return; 576 } 577 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); 578 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder()); 579 } catch (RemoteException e) { 580 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); 581 } 582 } 583 } 584 585 /** 586 * Called when the media button receiver for the {@param record} is changed. 587 * 588 * @param record the media session whose media button receiver is updated. 589 */ 590 public void onMediaButtonReceiverChanged(MediaSessionRecord record) { 591 synchronized (mLock) { 592 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 593 MediaSessionRecord mediaButtonSession = 594 user.mPriorityStack.getMediaButtonSession(); 595 if (record == mediaButtonSession) { 596 user.rememberMediaButtonReceiverLocked(mediaButtonSession); 597 } 598 } 599 } 600 601 private String getCallingPackageName(int uid) { 602 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 603 if (packages != null && packages.length > 0) { 604 return packages[0]; 605 } 606 return ""; 607 } 608 609 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) { 610 try { 611 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent); 612 } catch (RemoteException e) { 613 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener"); 614 } 615 } 616 617 private FullUserRecord getFullUserRecordLocked(int userId) { 618 int fullUserId = mFullUserIds.get(userId, -1); 619 if (fullUserId < 0) { 620 return null; 621 } 622 return mUserRecords.get(fullUserId); 623 } 624 625 /** 626 * Information about a full user and its corresponding managed profiles. 627 * 628 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate 629 * them when he/she presses a media/volume button. So keeping media sessions for them in one 630 * place makes more sense and increases the readability.</p> 631 * <p>The contents of this object is guarded by {@link #mLock}. 632 */ 633 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener { 634 private static final String COMPONENT_NAME_USER_ID_DELIM = ","; 635 private final int mFullUserId; 636 private final MediaSessionStack mPriorityStack; 637 private PendingIntent mLastMediaButtonReceiver; 638 private ComponentName mRestoredMediaButtonReceiver; 639 private int mRestoredMediaButtonReceiverUserId; 640 641 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener; 642 private int mOnVolumeKeyLongPressListenerUid; 643 private KeyEvent mInitialDownVolumeKeyEvent; 644 private int mInitialDownVolumeStream; 645 private boolean mInitialDownMusicOnly; 646 647 private IOnMediaKeyListener mOnMediaKeyListener; 648 private int mOnMediaKeyListenerUid; 649 private ICallback mCallback; 650 651 public FullUserRecord(int fullUserId) { 652 mFullUserId = fullUserId; 653 mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this); 654 // Restore the remembered media button receiver before the boot. 655 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver, 656 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); 657 if (mediaButtonReceiver == null) { 658 return; 659 } 660 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM); 661 if (tokens == null || tokens.length != 2) { 662 return; 663 } 664 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]); 665 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]); 666 } 667 668 public void destroySessionsForUserLocked(int userId) { 669 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId); 670 for (MediaSessionRecord session : sessions) { 671 MediaSessionService.this.destroySessionLocked(session); 672 } 673 } 674 675 public void dumpLocked(PrintWriter pw, String prefix) { 676 pw.print(prefix + "Record for full_user=" + mFullUserId); 677 // Dump managed profile user ids associated with this user. 678 int size = mFullUserIds.size(); 679 for (int i = 0; i < size; i++) { 680 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i) 681 && mFullUserIds.valueAt(i) == mFullUserId) { 682 pw.print(", profile_user=" + mFullUserIds.keyAt(i)); 683 } 684 } 685 pw.println(); 686 String indent = prefix + " "; 687 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener); 688 pw.println(indent + "Volume key long-press listener package: " + 689 getCallingPackageName(mOnVolumeKeyLongPressListenerUid)); 690 pw.println(indent + "Media key listener: " + mOnMediaKeyListener); 691 pw.println(indent + "Media key listener package: " + 692 getCallingPackageName(mOnMediaKeyListenerUid)); 693 pw.println(indent + "Callback: " + mCallback); 694 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver); 695 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver); 696 mPriorityStack.dump(pw, indent); 697 } 698 699 @Override 700 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, 701 MediaSessionRecord newMediaButtonSession) { 702 if (DEBUG_KEY_EVENT) { 703 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession); 704 } 705 synchronized (mLock) { 706 if (oldMediaButtonSession != null) { 707 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId()); 708 } 709 if (newMediaButtonSession != null) { 710 rememberMediaButtonReceiverLocked(newMediaButtonSession); 711 mHandler.postSessionsChanged(newMediaButtonSession.getUserId()); 712 } 713 pushAddressedPlayerChangedLocked(); 714 } 715 } 716 717 // Remember media button receiver and keep it in the persistent storage. 718 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { 719 PendingIntent receiver = record.getMediaButtonReceiver(); 720 mLastMediaButtonReceiver = receiver; 721 mRestoredMediaButtonReceiver = null; 722 String componentName = ""; 723 if (receiver != null) { 724 ComponentName component = receiver.getIntent().getComponent(); 725 if (component != null 726 && record.getPackageName().equals(component.getPackageName())) { 727 componentName = component.flattenToString(); 728 } 729 } 730 Settings.Secure.putStringForUser(mContentResolver, 731 Settings.System.MEDIA_BUTTON_RECEIVER, 732 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(), 733 mFullUserId); 734 } 735 736 private void pushAddressedPlayerChangedLocked() { 737 if (mCallback == null) { 738 return; 739 } 740 try { 741 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); 742 if (mediaButtonSession != null) { 743 mCallback.onAddressedPlayerChangedToMediaSession( 744 new MediaSession.Token(mediaButtonSession.getControllerBinder())); 745 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { 746 mCallback.onAddressedPlayerChangedToMediaButtonReceiver( 747 mCurrentFullUserRecord.mLastMediaButtonReceiver 748 .getIntent().getComponent()); 749 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { 750 mCallback.onAddressedPlayerChangedToMediaButtonReceiver( 751 mCurrentFullUserRecord.mRestoredMediaButtonReceiver); 752 } 753 } catch (RemoteException e) { 754 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); 755 } 756 } 757 758 private MediaSessionRecord getMediaButtonSessionLocked() { 759 return isGlobalPriorityActiveLocked() 760 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); 761 } 762 } 763 764 final class SessionsListenerRecord implements IBinder.DeathRecipient { 765 private final IActiveSessionsListener mListener; 766 private final ComponentName mComponentName; 767 private final int mUserId; 768 private final int mPid; 769 private final int mUid; 770 771 public SessionsListenerRecord(IActiveSessionsListener listener, 772 ComponentName componentName, 773 int userId, int pid, int uid) { 774 mListener = listener; 775 mComponentName = componentName; 776 mUserId = userId; 777 mPid = pid; 778 mUid = uid; 779 } 780 781 @Override 782 public void binderDied() { 783 synchronized (mLock) { 784 mSessionsListeners.remove(this); 785 } 786 } 787 } 788 789 final class SettingsObserver extends ContentObserver { 790 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( 791 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 792 793 private SettingsObserver() { 794 super(null); 795 } 796 797 private void observe() { 798 mContentResolver.registerContentObserver(mSecureSettingsUri, 799 false, this, UserHandle.USER_ALL); 800 } 801 802 @Override 803 public void onChange(boolean selfChange, Uri uri) { 804 updateActiveSessionListeners(); 805 } 806 } 807 808 class SessionManagerImpl extends ISessionManager.Stub { 809 private static final String EXTRA_WAKELOCK_ACQUIRED = 810 "android.media.AudioService.WAKELOCK_ACQUIRED"; 811 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number 812 813 private boolean mVoiceButtonDown = false; 814 private boolean mVoiceButtonHandled = false; 815 816 @Override 817 public ISession createSession(String packageName, ISessionCallback cb, String tag, 818 int userId) throws RemoteException { 819 final int pid = Binder.getCallingPid(); 820 final int uid = Binder.getCallingUid(); 821 final long token = Binder.clearCallingIdentity(); 822 try { 823 enforcePackageName(packageName, uid); 824 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 825 false /* allowAll */, true /* requireFull */, "createSession", packageName); 826 if (cb == null) { 827 throw new IllegalArgumentException("Controller callback cannot be null"); 828 } 829 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) 830 .getSessionBinder(); 831 } finally { 832 Binder.restoreCallingIdentity(token); 833 } 834 } 835 836 @Override 837 public List<IBinder> getSessions(ComponentName componentName, int userId) { 838 final int pid = Binder.getCallingPid(); 839 final int uid = Binder.getCallingUid(); 840 final long token = Binder.clearCallingIdentity(); 841 842 try { 843 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 844 ArrayList<IBinder> binders = new ArrayList<IBinder>(); 845 synchronized (mLock) { 846 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId); 847 for (MediaSessionRecord record : records) { 848 binders.add(record.getControllerBinder().asBinder()); 849 } 850 } 851 return binders; 852 } finally { 853 Binder.restoreCallingIdentity(token); 854 } 855 } 856 857 @Override 858 public void addSessionsListener(IActiveSessionsListener listener, 859 ComponentName componentName, int userId) throws RemoteException { 860 final int pid = Binder.getCallingPid(); 861 final int uid = Binder.getCallingUid(); 862 final long token = Binder.clearCallingIdentity(); 863 864 try { 865 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 866 synchronized (mLock) { 867 int index = findIndexOfSessionsListenerLocked(listener); 868 if (index != -1) { 869 Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); 870 return; 871 } 872 SessionsListenerRecord record = new SessionsListenerRecord(listener, 873 componentName, resolvedUserId, pid, uid); 874 try { 875 listener.asBinder().linkToDeath(record, 0); 876 } catch (RemoteException e) { 877 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); 878 return; 879 } 880 mSessionsListeners.add(record); 881 } 882 } finally { 883 Binder.restoreCallingIdentity(token); 884 } 885 } 886 887 @Override 888 public void removeSessionsListener(IActiveSessionsListener listener) 889 throws RemoteException { 890 synchronized (mLock) { 891 int index = findIndexOfSessionsListenerLocked(listener); 892 if (index != -1) { 893 SessionsListenerRecord record = mSessionsListeners.remove(index); 894 try { 895 record.mListener.asBinder().unlinkToDeath(record, 0); 896 } catch (Exception e) { 897 // ignore exceptions, the record is being removed 898 } 899 } 900 } 901 } 902 903 /** 904 * Handles the dispatching of the media button events to one of the 905 * registered listeners, or if there was none, broadcast an 906 * ACTION_MEDIA_BUTTON intent to the rest of the system. 907 * 908 * @param keyEvent a non-null KeyEvent whose key code is one of the 909 * supported media buttons 910 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held 911 * while this key event is dispatched. 912 */ 913 @Override 914 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 915 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) { 916 Log.w(TAG, "Attempted to dispatch null or non-media key event."); 917 return; 918 } 919 920 final int pid = Binder.getCallingPid(); 921 final int uid = Binder.getCallingUid(); 922 final long token = Binder.clearCallingIdentity(); 923 try { 924 if (DEBUG) { 925 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event=" 926 + keyEvent); 927 } 928 if (!isUserSetupComplete()) { 929 // Global media key handling can have the side-effect of starting new 930 // activities which is undesirable while setup is in progress. 931 Slog.i(TAG, "Not dispatching media key event because user " 932 + "setup is in progress."); 933 return; 934 } 935 936 synchronized (mLock) { 937 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked(); 938 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) { 939 // Prevent dispatching key event through reflection while the global 940 // priority session is active. 941 Slog.i(TAG, "Only the system can dispatch media key event " 942 + "to the global priority session."); 943 return; 944 } 945 if (!isGlobalPriorityActive) { 946 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) { 947 if (DEBUG_KEY_EVENT) { 948 Log.d(TAG, "Send " + keyEvent + " to the media key listener"); 949 } 950 try { 951 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent, 952 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock)); 953 return; 954 } catch (RemoteException e) { 955 Log.w(TAG, "Failed to send " + keyEvent 956 + " to the media key listener"); 957 } 958 } 959 } 960 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) { 961 handleVoiceKeyEventLocked(keyEvent, needWakeLock); 962 } else { 963 dispatchMediaKeyEventLocked(keyEvent, needWakeLock); 964 } 965 } 966 } finally { 967 Binder.restoreCallingIdentity(token); 968 } 969 } 970 971 @Override 972 public void setCallback(ICallback callback) { 973 final int pid = Binder.getCallingPid(); 974 final int uid = Binder.getCallingUid(); 975 final long token = Binder.clearCallingIdentity(); 976 try { 977 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) { 978 throw new SecurityException("Only Bluetooth service processes can set" 979 + " Callback"); 980 } 981 synchronized (mLock) { 982 int userId = UserHandle.getUserId(uid); 983 FullUserRecord user = getFullUserRecordLocked(userId); 984 if (user == null || user.mFullUserId != userId) { 985 Log.w(TAG, "Only the full user can set the callback" 986 + ", userId=" + userId); 987 return; 988 } 989 user.mCallback = callback; 990 Log.d(TAG, "The callback " + user.mCallback 991 + " is set by " + getCallingPackageName(uid)); 992 if (user.mCallback == null) { 993 return; 994 } 995 try { 996 user.mCallback.asBinder().linkToDeath( 997 new IBinder.DeathRecipient() { 998 @Override 999 public void binderDied() { 1000 synchronized (mLock) { 1001 user.mCallback = null; 1002 } 1003 } 1004 }, 0); 1005 user.pushAddressedPlayerChangedLocked(); 1006 } catch (RemoteException e) { 1007 Log.w(TAG, "Failed to set callback", e); 1008 user.mCallback = null; 1009 } 1010 } 1011 } finally { 1012 Binder.restoreCallingIdentity(token); 1013 } 1014 } 1015 1016 @Override 1017 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) { 1018 final int pid = Binder.getCallingPid(); 1019 final int uid = Binder.getCallingUid(); 1020 final long token = Binder.clearCallingIdentity(); 1021 try { 1022 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission. 1023 if (getContext().checkPermission( 1024 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid) 1025 != PackageManager.PERMISSION_GRANTED) { 1026 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" + 1027 " permission."); 1028 } 1029 1030 synchronized (mLock) { 1031 int userId = UserHandle.getUserId(uid); 1032 FullUserRecord user = getFullUserRecordLocked(userId); 1033 if (user == null || user.mFullUserId != userId) { 1034 Log.w(TAG, "Only the full user can set the volume key long-press listener" 1035 + ", userId=" + userId); 1036 return; 1037 } 1038 if (user.mOnVolumeKeyLongPressListener != null && 1039 user.mOnVolumeKeyLongPressListenerUid != uid) { 1040 Log.w(TAG, "The volume key long-press listener cannot be reset" 1041 + " by another app , mOnVolumeKeyLongPressListener=" 1042 + user.mOnVolumeKeyLongPressListenerUid 1043 + ", uid=" + uid); 1044 return; 1045 } 1046 1047 user.mOnVolumeKeyLongPressListener = listener; 1048 user.mOnVolumeKeyLongPressListenerUid = uid; 1049 1050 Log.d(TAG, "The volume key long-press listener " 1051 + listener + " is set by " + getCallingPackageName(uid)); 1052 1053 if (user.mOnVolumeKeyLongPressListener != null) { 1054 try { 1055 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath( 1056 new IBinder.DeathRecipient() { 1057 @Override 1058 public void binderDied() { 1059 synchronized (mLock) { 1060 user.mOnVolumeKeyLongPressListener = null; 1061 } 1062 } 1063 }, 0); 1064 } catch (RemoteException e) { 1065 Log.w(TAG, "Failed to set death recipient " 1066 + user.mOnVolumeKeyLongPressListener); 1067 user.mOnVolumeKeyLongPressListener = null; 1068 } 1069 } 1070 } 1071 } finally { 1072 Binder.restoreCallingIdentity(token); 1073 } 1074 } 1075 1076 @Override 1077 public void setOnMediaKeyListener(IOnMediaKeyListener listener) { 1078 final int pid = Binder.getCallingPid(); 1079 final int uid = Binder.getCallingUid(); 1080 final long token = Binder.clearCallingIdentity(); 1081 try { 1082 // Enforce SET_MEDIA_KEY_LISTENER permission. 1083 if (getContext().checkPermission( 1084 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid) 1085 != PackageManager.PERMISSION_GRANTED) { 1086 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" + 1087 " permission."); 1088 } 1089 1090 synchronized (mLock) { 1091 int userId = UserHandle.getUserId(uid); 1092 FullUserRecord user = getFullUserRecordLocked(userId); 1093 if (user == null || user.mFullUserId != userId) { 1094 Log.w(TAG, "Only the full user can set the media key listener" 1095 + ", userId=" + userId); 1096 return; 1097 } 1098 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) { 1099 Log.w(TAG, "The media key listener cannot be reset by another app. " 1100 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid 1101 + ", uid=" + uid); 1102 return; 1103 } 1104 1105 user.mOnMediaKeyListener = listener; 1106 user.mOnMediaKeyListenerUid = uid; 1107 1108 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener 1109 + " is set by " + getCallingPackageName(uid)); 1110 1111 if (user.mOnMediaKeyListener != null) { 1112 try { 1113 user.mOnMediaKeyListener.asBinder().linkToDeath( 1114 new IBinder.DeathRecipient() { 1115 @Override 1116 public void binderDied() { 1117 synchronized (mLock) { 1118 user.mOnMediaKeyListener = null; 1119 } 1120 } 1121 }, 0); 1122 } catch (RemoteException e) { 1123 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener); 1124 user.mOnMediaKeyListener = null; 1125 } 1126 } 1127 } 1128 } finally { 1129 Binder.restoreCallingIdentity(token); 1130 } 1131 } 1132 1133 /** 1134 * Handles the dispatching of the volume button events to one of the 1135 * registered listeners. If there's a volume key long-press listener and 1136 * there's no active global priority session, long-pressess will be sent to the 1137 * long-press listener instead of adjusting volume. 1138 * 1139 * @param keyEvent a non-null KeyEvent whose key code is one of the 1140 * {@link KeyEvent#KEYCODE_VOLUME_UP}, 1141 * {@link KeyEvent#KEYCODE_VOLUME_DOWN}, 1142 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}. 1143 * @param stream stream type to adjust volume. 1144 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume. 1145 */ 1146 @Override 1147 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) { 1148 if (keyEvent == null || 1149 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP 1150 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN 1151 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) { 1152 Log.w(TAG, "Attempted to dispatch null or non-volume key event."); 1153 return; 1154 } 1155 1156 final int pid = Binder.getCallingPid(); 1157 final int uid = Binder.getCallingUid(); 1158 final long token = Binder.clearCallingIdentity(); 1159 1160 if (DEBUG_KEY_EVENT) { 1161 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event=" 1162 + keyEvent); 1163 } 1164 1165 try { 1166 synchronized (mLock) { 1167 if (isGlobalPriorityActiveLocked() 1168 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { 1169 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly); 1170 } else { 1171 // TODO: Consider the case when both volume up and down keys are pressed 1172 // at the same time. 1173 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { 1174 if (keyEvent.getRepeatCount() == 0) { 1175 // Keeps the copy of the KeyEvent because it can be reused. 1176 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = 1177 KeyEvent.obtain(keyEvent); 1178 mCurrentFullUserRecord.mInitialDownVolumeStream = stream; 1179 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly; 1180 mHandler.sendMessageDelayed( 1181 mHandler.obtainMessage( 1182 MessageHandler.MSG_VOLUME_INITIAL_DOWN, 1183 mCurrentFullUserRecord.mFullUserId, 0), 1184 mLongPressTimeout); 1185 } 1186 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { 1187 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); 1188 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) { 1189 dispatchVolumeKeyLongPressLocked( 1190 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent); 1191 // Mark that the key is already handled. 1192 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null; 1193 } 1194 dispatchVolumeKeyLongPressLocked(keyEvent); 1195 } 1196 } else { // if up 1197 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); 1198 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null 1199 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent 1200 .getDownTime() == keyEvent.getDownTime()) { 1201 // Short-press. Should change volume. 1202 dispatchVolumeKeyEventLocked( 1203 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent, 1204 mCurrentFullUserRecord.mInitialDownVolumeStream, 1205 mCurrentFullUserRecord.mInitialDownMusicOnly); 1206 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly); 1207 } else { 1208 dispatchVolumeKeyLongPressLocked(keyEvent); 1209 } 1210 } 1211 } 1212 } 1213 } finally { 1214 Binder.restoreCallingIdentity(token); 1215 } 1216 } 1217 1218 private void dispatchVolumeKeyEventLocked( 1219 KeyEvent keyEvent, int stream, boolean musicOnly) { 1220 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; 1221 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; 1222 int direction = 0; 1223 boolean isMute = false; 1224 switch (keyEvent.getKeyCode()) { 1225 case KeyEvent.KEYCODE_VOLUME_UP: 1226 direction = AudioManager.ADJUST_RAISE; 1227 break; 1228 case KeyEvent.KEYCODE_VOLUME_DOWN: 1229 direction = AudioManager.ADJUST_LOWER; 1230 break; 1231 case KeyEvent.KEYCODE_VOLUME_MUTE: 1232 isMute = true; 1233 break; 1234 } 1235 if (down || up) { 1236 int flags = AudioManager.FLAG_FROM_KEY; 1237 if (musicOnly) { 1238 // This flag is used when the screen is off to only affect active media. 1239 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY; 1240 } else { 1241 // These flags are consistent with the home screen 1242 if (up) { 1243 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; 1244 } else { 1245 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE; 1246 } 1247 } 1248 if (direction != 0) { 1249 // If this is action up we want to send a beep for non-music events 1250 if (up) { 1251 direction = 0; 1252 } 1253 dispatchAdjustVolumeLocked(stream, direction, flags); 1254 } else if (isMute) { 1255 if (down && keyEvent.getRepeatCount() == 0) { 1256 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags); 1257 } 1258 } 1259 } 1260 } 1261 1262 @Override 1263 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) { 1264 final long token = Binder.clearCallingIdentity(); 1265 try { 1266 synchronized (mLock) { 1267 dispatchAdjustVolumeLocked(suggestedStream, delta, flags); 1268 } 1269 } finally { 1270 Binder.restoreCallingIdentity(token); 1271 } 1272 } 1273 1274 @Override 1275 public void setRemoteVolumeController(IRemoteVolumeController rvc) { 1276 final int pid = Binder.getCallingPid(); 1277 final int uid = Binder.getCallingUid(); 1278 final long token = Binder.clearCallingIdentity(); 1279 try { 1280 enforceSystemUiPermission("listen for volume changes", pid, uid); 1281 mRvc = rvc; 1282 } finally { 1283 Binder.restoreCallingIdentity(token); 1284 } 1285 } 1286 1287 @Override 1288 public boolean isGlobalPriorityActive() { 1289 synchronized (mLock) { 1290 return isGlobalPriorityActiveLocked(); 1291 } 1292 } 1293 1294 @Override 1295 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 1296 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; 1297 1298 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); 1299 pw.println(); 1300 1301 synchronized (mLock) { 1302 pw.println(mSessionsListeners.size() + " sessions listeners."); 1303 pw.println("Global priority session is " + mGlobalPrioritySession); 1304 if (mGlobalPrioritySession != null) { 1305 mGlobalPrioritySession.dump(pw, " "); 1306 } 1307 pw.println("User Records:"); 1308 int count = mUserRecords.size(); 1309 for (int i = 0; i < count; i++) { 1310 mUserRecords.valueAt(i).dumpLocked(pw, ""); 1311 } 1312 mAudioPlaybackMonitor.dump(pw, ""); 1313 } 1314 } 1315 1316 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, 1317 final int uid) { 1318 String packageName = null; 1319 if (componentName != null) { 1320 // If they gave us a component name verify they own the 1321 // package 1322 packageName = componentName.getPackageName(); 1323 enforcePackageName(packageName, uid); 1324 } 1325 // Check that they can make calls on behalf of the user and 1326 // get the final user id 1327 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 1328 true /* allowAll */, true /* requireFull */, "getSessions", packageName); 1329 // Check if they have the permissions or their component is 1330 // enabled for the user they're calling from. 1331 enforceMediaPermissions(componentName, pid, uid, resolvedUserId); 1332 return resolvedUserId; 1333 } 1334 1335 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) { 1336 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession 1337 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); 1338 1339 boolean preferSuggestedStream = false; 1340 if (isValidLocalStreamType(suggestedStream) 1341 && AudioSystem.isStreamActive(suggestedStream, 0)) { 1342 preferSuggestedStream = true; 1343 } 1344 if (DEBUG_KEY_EVENT) { 1345 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags=" 1346 + flags + ", suggestedStream=" + suggestedStream 1347 + ", preferSuggestedStream=" + preferSuggestedStream); 1348 } 1349 if (session == null || preferSuggestedStream) { 1350 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0 1351 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { 1352 if (DEBUG) { 1353 Log.d(TAG, "No active session to adjust, skipping media only volume event"); 1354 } 1355 return; 1356 } 1357 1358 // Execute mAudioService.adjustSuggestedStreamVolume() on 1359 // handler thread of MediaSessionService. 1360 // This will release the MediaSessionService.mLock sooner and avoid 1361 // a potential deadlock between MediaSessionService.mLock and 1362 // ActivityManagerService lock. 1363 mHandler.post(new Runnable() { 1364 @Override 1365 public void run() { 1366 try { 1367 String packageName = getContext().getOpPackageName(); 1368 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, 1369 flags, packageName, TAG); 1370 } catch (RemoteException e) { 1371 Log.e(TAG, "Error adjusting default volume.", e); 1372 } catch (IllegalArgumentException e) { 1373 Log.e(TAG, "Cannot adjust volume: direction=" + direction 1374 + ", suggestedStream=" + suggestedStream + ", flags=" + flags, 1375 e); 1376 } 1377 } 1378 }); 1379 } else { 1380 session.adjustVolume(direction, flags, getContext().getPackageName(), 1381 Process.SYSTEM_UID, true); 1382 } 1383 } 1384 1385 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { 1386 int action = keyEvent.getAction(); 1387 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; 1388 if (action == KeyEvent.ACTION_DOWN) { 1389 if (keyEvent.getRepeatCount() == 0) { 1390 mVoiceButtonDown = true; 1391 mVoiceButtonHandled = false; 1392 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) { 1393 mVoiceButtonHandled = true; 1394 startVoiceInput(needWakeLock); 1395 } 1396 } else if (action == KeyEvent.ACTION_UP) { 1397 if (mVoiceButtonDown) { 1398 mVoiceButtonDown = false; 1399 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { 1400 // Resend the down then send this event through 1401 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); 1402 dispatchMediaKeyEventLocked(downEvent, needWakeLock); 1403 dispatchMediaKeyEventLocked(keyEvent, needWakeLock); 1404 } 1405 } 1406 } 1407 } 1408 1409 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { 1410 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); 1411 if (session != null) { 1412 if (DEBUG_KEY_EVENT) { 1413 Log.d(TAG, "Sending " + keyEvent + " to " + session); 1414 } 1415 if (needWakeLock) { 1416 mKeyEventReceiver.aquireWakeLockLocked(); 1417 } 1418 // If we don't need a wakelock use -1 as the id so we won't release it later. 1419 session.sendMediaButton(keyEvent, 1420 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, 1421 mKeyEventReceiver, Process.SYSTEM_UID, 1422 getContext().getPackageName()); 1423 if (mCurrentFullUserRecord.mCallback != null) { 1424 try { 1425 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession( 1426 keyEvent, 1427 new MediaSession.Token(session.getControllerBinder())); 1428 } catch (RemoteException e) { 1429 Log.w(TAG, "Failed to send callback", e); 1430 } 1431 } 1432 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null 1433 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { 1434 if (needWakeLock) { 1435 mKeyEventReceiver.aquireWakeLockLocked(); 1436 } 1437 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 1438 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1439 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 1440 try { 1441 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { 1442 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver; 1443 if (DEBUG_KEY_EVENT) { 1444 Log.d(TAG, "Sending " + keyEvent 1445 + " to the last known PendingIntent " + receiver); 1446 } 1447 receiver.send(getContext(), 1448 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, 1449 mediaButtonIntent, mKeyEventReceiver, mHandler); 1450 if (mCurrentFullUserRecord.mCallback != null) { 1451 ComponentName componentName = mCurrentFullUserRecord 1452 .mLastMediaButtonReceiver.getIntent().getComponent(); 1453 if (componentName != null) { 1454 mCurrentFullUserRecord.mCallback 1455 .onMediaKeyEventDispatchedToMediaButtonReceiver( 1456 keyEvent, componentName); 1457 } 1458 } 1459 } else { 1460 ComponentName receiver = 1461 mCurrentFullUserRecord.mRestoredMediaButtonReceiver; 1462 if (DEBUG_KEY_EVENT) { 1463 Log.d(TAG, "Sending " + keyEvent + " to the restored intent " 1464 + receiver); 1465 } 1466 mediaButtonIntent.setComponent(receiver); 1467 getContext().sendBroadcastAsUser(mediaButtonIntent, 1468 UserHandle.of(mCurrentFullUserRecord 1469 .mRestoredMediaButtonReceiverUserId)); 1470 if (mCurrentFullUserRecord.mCallback != null) { 1471 mCurrentFullUserRecord.mCallback 1472 .onMediaKeyEventDispatchedToMediaButtonReceiver( 1473 keyEvent, receiver); 1474 } 1475 } 1476 } catch (CanceledException e) { 1477 Log.i(TAG, "Error sending key event to media button receiver " 1478 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e); 1479 } catch (RemoteException e) { 1480 Log.w(TAG, "Failed to send callback", e); 1481 } 1482 } 1483 } 1484 1485 private void startVoiceInput(boolean needWakeLock) { 1486 Intent voiceIntent = null; 1487 // select which type of search to launch: 1488 // - screen on and device unlocked: action is ACTION_WEB_SEARCH 1489 // - device locked or screen off: action is 1490 // ACTION_VOICE_SEARCH_HANDS_FREE 1491 // with EXTRA_SECURE set to true if the device is securely locked 1492 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 1493 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); 1494 if (!isLocked && pm.isScreenOn()) { 1495 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); 1496 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH"); 1497 } else { 1498 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); 1499 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, 1500 isLocked && mKeyguardManager.isKeyguardSecure()); 1501 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE"); 1502 } 1503 // start the search activity 1504 if (needWakeLock) { 1505 mMediaEventWakeLock.acquire(); 1506 } 1507 try { 1508 if (voiceIntent != null) { 1509 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1510 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 1511 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent); 1512 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT); 1513 } 1514 } catch (ActivityNotFoundException e) { 1515 Log.w(TAG, "No activity for search: " + e); 1516 } finally { 1517 if (needWakeLock) { 1518 mMediaEventWakeLock.release(); 1519 } 1520 } 1521 } 1522 1523 private boolean isVoiceKey(int keyCode) { 1524 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK 1525 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 1526 } 1527 1528 private boolean isUserSetupComplete() { 1529 return Settings.Secure.getIntForUser(getContext().getContentResolver(), 1530 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; 1531 } 1532 1533 // we only handle public stream types, which are 0-5 1534 private boolean isValidLocalStreamType(int streamType) { 1535 return streamType >= AudioManager.STREAM_VOICE_CALL 1536 && streamType <= AudioManager.STREAM_NOTIFICATION; 1537 } 1538 1539 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { 1540 private KeyEvent mKeyEvent; 1541 private boolean mNeedWakeLock; 1542 private boolean mHandled; 1543 1544 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) { 1545 super(mHandler); 1546 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT); 1547 mKeyEvent = keyEvent; 1548 mNeedWakeLock = needWakeLock; 1549 } 1550 1551 @Override 1552 public void run() { 1553 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent); 1554 dispatchMediaKeyEvent(); 1555 } 1556 1557 @Override 1558 protected void onReceiveResult(int resultCode, Bundle resultData) { 1559 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) { 1560 mHandled = true; 1561 mHandler.removeCallbacks(this); 1562 return; 1563 } 1564 dispatchMediaKeyEvent(); 1565 } 1566 1567 private void dispatchMediaKeyEvent() { 1568 if (mHandled) { 1569 return; 1570 } 1571 mHandled = true; 1572 mHandler.removeCallbacks(this); 1573 synchronized (mLock) { 1574 if (!isGlobalPriorityActiveLocked() 1575 && isVoiceKey(mKeyEvent.getKeyCode())) { 1576 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock); 1577 } else { 1578 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock); 1579 } 1580 } 1581 } 1582 } 1583 1584 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); 1585 1586 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable, 1587 PendingIntent.OnFinished { 1588 private final Handler mHandler; 1589 private int mRefCount = 0; 1590 private int mLastTimeoutId = 0; 1591 1592 public KeyEventWakeLockReceiver(Handler handler) { 1593 super(handler); 1594 mHandler = handler; 1595 } 1596 1597 public void onTimeout() { 1598 synchronized (mLock) { 1599 if (mRefCount == 0) { 1600 // We've already released it, so just return 1601 return; 1602 } 1603 mLastTimeoutId++; 1604 mRefCount = 0; 1605 releaseWakeLockLocked(); 1606 } 1607 } 1608 1609 public void aquireWakeLockLocked() { 1610 if (mRefCount == 0) { 1611 mMediaEventWakeLock.acquire(); 1612 } 1613 mRefCount++; 1614 mHandler.removeCallbacks(this); 1615 mHandler.postDelayed(this, WAKELOCK_TIMEOUT); 1616 1617 } 1618 1619 @Override 1620 public void run() { 1621 onTimeout(); 1622 } 1623 1624 @Override 1625 protected void onReceiveResult(int resultCode, Bundle resultData) { 1626 if (resultCode < mLastTimeoutId) { 1627 // Ignore results from calls that were before the last 1628 // timeout, just in case. 1629 return; 1630 } else { 1631 synchronized (mLock) { 1632 if (mRefCount > 0) { 1633 mRefCount--; 1634 if (mRefCount == 0) { 1635 releaseWakeLockLocked(); 1636 } 1637 } 1638 } 1639 } 1640 } 1641 1642 private void releaseWakeLockLocked() { 1643 mMediaEventWakeLock.release(); 1644 mHandler.removeCallbacks(this); 1645 } 1646 1647 @Override 1648 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, 1649 String resultData, Bundle resultExtras) { 1650 onReceiveResult(resultCode, null); 1651 } 1652 }; 1653 1654 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { 1655 @Override 1656 public void onReceive(Context context, Intent intent) { 1657 if (intent == null) { 1658 return; 1659 } 1660 Bundle extras = intent.getExtras(); 1661 if (extras == null) { 1662 return; 1663 } 1664 synchronized (mLock) { 1665 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED) 1666 && mMediaEventWakeLock.isHeld()) { 1667 mMediaEventWakeLock.release(); 1668 } 1669 } 1670 } 1671 }; 1672 } 1673 1674 final class MessageHandler extends Handler { 1675 private static final int MSG_SESSIONS_CHANGED = 1; 1676 private static final int MSG_VOLUME_INITIAL_DOWN = 2; 1677 private final SparseArray<Integer> mIntegerCache = new SparseArray<>(); 1678 1679 @Override 1680 public void handleMessage(Message msg) { 1681 switch (msg.what) { 1682 case MSG_SESSIONS_CHANGED: 1683 pushSessionsChanged((int) msg.obj); 1684 break; 1685 case MSG_VOLUME_INITIAL_DOWN: 1686 synchronized (mLock) { 1687 FullUserRecord user = mUserRecords.get((int) msg.arg1); 1688 if (user != null && user.mInitialDownVolumeKeyEvent != null) { 1689 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent); 1690 // Mark that the key is already handled. 1691 user.mInitialDownVolumeKeyEvent = null; 1692 } 1693 } 1694 break; 1695 } 1696 } 1697 1698 public void postSessionsChanged(int userId) { 1699 // Use object instead of the arguments when posting message to remove pending requests. 1700 Integer userIdInteger = mIntegerCache.get(userId); 1701 if (userIdInteger == null) { 1702 userIdInteger = Integer.valueOf(userId); 1703 mIntegerCache.put(userId, userIdInteger); 1704 } 1705 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger); 1706 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget(); 1707 } 1708 } 1709 } 1710