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