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 static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 20 import static android.os.UserHandle.ALL; 21 import static android.os.UserHandle.CURRENT; 22 23 import static com.android.server.media.MediaKeyDispatcher.KEY_EVENT_LONG_PRESS; 24 import static com.android.server.media.MediaKeyDispatcher.isDoubleTapOverridden; 25 import static com.android.server.media.MediaKeyDispatcher.isLongPressOverridden; 26 import static com.android.server.media.MediaKeyDispatcher.isSingleTapOverridden; 27 import static com.android.server.media.MediaKeyDispatcher.isTripleTapOverridden; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.app.ActivityManager; 32 import android.app.KeyguardManager; 33 import android.app.NotificationManager; 34 import android.app.PendingIntent; 35 import android.content.ActivityNotFoundException; 36 import android.content.BroadcastReceiver; 37 import android.content.ComponentName; 38 import android.content.ContentResolver; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.pm.PackageManager; 43 import android.content.pm.PackageManagerInternal; 44 import android.media.AudioManager; 45 import android.media.AudioPlaybackConfiguration; 46 import android.media.AudioSystem; 47 import android.media.IRemoteSessionCallback; 48 import android.media.MediaCommunicationManager; 49 import android.media.Session2Token; 50 import android.media.session.IActiveSessionsListener; 51 import android.media.session.IOnMediaKeyEventDispatchedListener; 52 import android.media.session.IOnMediaKeyEventSessionChangedListener; 53 import android.media.session.IOnMediaKeyListener; 54 import android.media.session.IOnVolumeKeyLongPressListener; 55 import android.media.session.ISession; 56 import android.media.session.ISession2TokensListener; 57 import android.media.session.ISessionCallback; 58 import android.media.session.ISessionManager; 59 import android.media.session.MediaController; 60 import android.media.session.MediaSession; 61 import android.media.session.MediaSessionManager; 62 import android.os.Binder; 63 import android.os.Bundle; 64 import android.os.Handler; 65 import android.os.HandlerThread; 66 import android.os.IBinder; 67 import android.os.Message; 68 import android.os.PowerExemptionManager; 69 import android.os.PowerManager; 70 import android.os.Process; 71 import android.os.RemoteCallbackList; 72 import android.os.RemoteException; 73 import android.os.ResultReceiver; 74 import android.os.ShellCallback; 75 import android.os.UserHandle; 76 import android.os.UserManager; 77 import android.provider.Settings; 78 import android.speech.RecognizerIntent; 79 import android.text.TextUtils; 80 import android.util.Log; 81 import android.util.SparseArray; 82 import android.util.SparseIntArray; 83 import android.view.KeyEvent; 84 import android.view.ViewConfiguration; 85 86 import com.android.internal.R; 87 import com.android.internal.annotations.GuardedBy; 88 import com.android.server.LocalManagerRegistry; 89 import com.android.server.LocalServices; 90 import com.android.server.SystemService; 91 import com.android.server.Watchdog; 92 import com.android.server.Watchdog.Monitor; 93 import com.android.server.am.ActivityManagerLocal; 94 95 import java.io.FileDescriptor; 96 import java.io.PrintWriter; 97 import java.lang.reflect.Constructor; 98 import java.lang.reflect.InvocationTargetException; 99 import java.util.ArrayList; 100 import java.util.HashMap; 101 import java.util.List; 102 103 /** 104 * System implementation of MediaSessionManager 105 */ 106 public class MediaSessionService extends SystemService implements Monitor { 107 private static final String TAG = "MediaSessionService"; 108 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 109 // Leave log for key event always. 110 static final boolean DEBUG_KEY_EVENT = true; 111 112 private static final int WAKELOCK_TIMEOUT = 5000; 113 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; 114 private static final int SESSION_CREATION_LIMIT_PER_UID = 100; 115 private static final int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() 116 + /* Buffer for delayed delivery of key event */ 50; 117 private static final int MULTI_TAP_TIMEOUT = ViewConfiguration.getMultiPressTimeout(); 118 /** 119 * Copied from Settings.System.MEDIA_BUTTON_RECEIVER 120 */ 121 private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver"; 122 123 private final Context mContext; 124 private final SessionManagerImpl mSessionManagerImpl; 125 private final MessageHandler mHandler = new MessageHandler(); 126 private final PowerManager.WakeLock mMediaEventWakeLock; 127 private final NotificationManager mNotificationManager; 128 private final Object mLock = new Object(); 129 private final HandlerThread mRecordThread = new HandlerThread("SessionRecordThread"); 130 // Keeps the full user id for each user. 131 @GuardedBy("mLock") 132 private final SparseIntArray mFullUserIds = new SparseIntArray(); 133 @GuardedBy("mLock") 134 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>(); 135 @GuardedBy("mLock") 136 private final ArrayList<SessionsListenerRecord> mSessionsListeners = 137 new ArrayList<SessionsListenerRecord>(); 138 @GuardedBy("mLock") 139 private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords = 140 new ArrayList<>(); 141 142 private KeyguardManager mKeyguardManager; 143 private AudioManager mAudioManager; 144 private boolean mHasFeatureLeanback; 145 private ActivityManagerLocal mActivityManagerLocal; 146 147 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) 148 // It's always not null after the MediaSessionService is started. 149 private FullUserRecord mCurrentFullUserRecord; 150 private MediaSessionRecord mGlobalPrioritySession; 151 private AudioPlayerStateMonitor mAudioPlayerStateMonitor; 152 153 // Used to notify System UI and Settings when remote volume was changed. 154 @GuardedBy("mLock") 155 final RemoteCallbackList<IRemoteSessionCallback> mRemoteVolumeControllers = 156 new RemoteCallbackList<>(); 157 158 private MediaSessionPolicyProvider mCustomMediaSessionPolicyProvider; 159 private MediaKeyDispatcher mCustomMediaKeyDispatcher; 160 161 private MediaCommunicationManager mCommunicationManager; 162 private final MediaCommunicationManager.SessionCallback mSession2TokenCallback = 163 new MediaCommunicationManager.SessionCallback() { 164 @Override 165 public void onSession2TokenCreated(Session2Token token) { 166 if (DEBUG) { 167 Log.d(TAG, "Session2 is created " + token); 168 } 169 MediaSession2Record record = new MediaSession2Record(token, 170 MediaSessionService.this, mRecordThread.getLooper(), 0); 171 synchronized (mLock) { 172 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 173 if (user != null) { 174 user.mPriorityStack.addSession(record); 175 } 176 } 177 } 178 }; 179 MediaSessionService(Context context)180 public MediaSessionService(Context context) { 181 super(context); 182 mContext = context; 183 mSessionManagerImpl = new SessionManagerImpl(); 184 PowerManager pm = mContext.getSystemService(PowerManager.class); 185 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); 186 mNotificationManager = mContext.getSystemService(NotificationManager.class); 187 mAudioManager = mContext.getSystemService(AudioManager.class); 188 } 189 190 @Override onStart()191 public void onStart() { 192 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); 193 Watchdog.getInstance().addMonitor(this); 194 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 195 mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(mContext); 196 mAudioPlayerStateMonitor.registerListener( 197 (config, isRemoved) -> { 198 if (DEBUG) { 199 Log.d(TAG, "Audio playback is changed, config=" + config 200 + ", removed=" + isRemoved); 201 } 202 if (config.getPlayerType() 203 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { 204 return; 205 } 206 synchronized (mLock) { 207 FullUserRecord user = getFullUserRecordLocked( 208 UserHandle.getUserHandleForUid(config.getClientUid()) 209 .getIdentifier()); 210 if (user != null) { 211 user.mPriorityStack.updateMediaButtonSessionIfNeeded(); 212 } 213 } 214 }, null /* handler */); 215 mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature( 216 PackageManager.FEATURE_LEANBACK); 217 218 updateUser(); 219 220 instantiateCustomProvider(mContext.getResources().getString( 221 R.string.config_customMediaSessionPolicyProvider)); 222 instantiateCustomDispatcher(mContext.getResources().getString( 223 R.string.config_customMediaKeyDispatcher)); 224 mRecordThread.start(); 225 226 final IntentFilter filter = new IntentFilter( 227 NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED); 228 mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter); 229 230 mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class); 231 } 232 233 @Override onBootPhase(int phase)234 public void onBootPhase(int phase) { 235 super.onBootPhase(phase); 236 switch (phase) { 237 // This ensures MediaCommunicationService is started 238 case PHASE_BOOT_COMPLETED: 239 mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class); 240 mCommunicationManager.registerSessionCallback(new HandlerExecutor(mHandler), 241 mSession2TokenCallback); 242 break; 243 case PHASE_ACTIVITY_MANAGER_READY: 244 MediaSessionDeviceConfig.initialize(mContext); 245 break; 246 } 247 } 248 249 private final BroadcastReceiver mNotificationListenerEnabledChangedReceiver = 250 new BroadcastReceiver() { 251 @Override 252 public void onReceive(Context context, Intent intent) { 253 updateActiveSessionListeners(); 254 } 255 }; 256 isGlobalPriorityActiveLocked()257 private boolean isGlobalPriorityActiveLocked() { 258 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); 259 } 260 onSessionActiveStateChanged(MediaSessionRecordImpl record)261 void onSessionActiveStateChanged(MediaSessionRecordImpl record) { 262 synchronized (mLock) { 263 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 264 if (user == null) { 265 Log.w(TAG, "Unknown session updated. Ignoring."); 266 return; 267 } 268 if (record.isSystemPriority()) { 269 if (DEBUG_KEY_EVENT) { 270 Log.d(TAG, "Global priority session is updated, active=" + record.isActive()); 271 } 272 user.pushAddressedPlayerChangedLocked(); 273 } else { 274 if (!user.mPriorityStack.contains(record)) { 275 Log.w(TAG, "Unknown session updated. Ignoring."); 276 return; 277 } 278 user.mPriorityStack.onSessionActiveStateChanged(record); 279 } 280 281 mHandler.postSessionsChanged(record); 282 } 283 } 284 285 // Currently only media1 can become global priority session. setGlobalPrioritySession(MediaSessionRecord record)286 void setGlobalPrioritySession(MediaSessionRecord record) { 287 synchronized (mLock) { 288 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 289 if (mGlobalPrioritySession != record) { 290 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession 291 + " to " + record); 292 mGlobalPrioritySession = record; 293 if (user != null && user.mPriorityStack.contains(record)) { 294 // Handle the global priority session separately. 295 // Otherwise, it can be the media button session regardless of the active state 296 // because it or other system components might have been the lastly played media 297 // app. 298 user.mPriorityStack.removeSession(record); 299 } 300 } 301 } 302 } 303 getActiveSessionsLocked(int userId)304 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { 305 List<MediaSessionRecord> records = new ArrayList<>(); 306 if (userId == ALL.getIdentifier()) { 307 int size = mUserRecords.size(); 308 for (int i = 0; i < size; i++) { 309 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); 310 } 311 } else { 312 FullUserRecord user = getFullUserRecordLocked(userId); 313 if (user == null) { 314 Log.w(TAG, "getSessions failed. Unknown user " + userId); 315 return records; 316 } 317 records.addAll(user.mPriorityStack.getActiveSessions(userId)); 318 } 319 320 // Return global priority session at the first whenever it's asked. 321 if (isGlobalPriorityActiveLocked() 322 && (userId == ALL.getIdentifier() 323 || userId == mGlobalPrioritySession.getUserId())) { 324 records.add(0, mGlobalPrioritySession); 325 } 326 return records; 327 } 328 getSession2TokensLocked(int userId)329 List<Session2Token> getSession2TokensLocked(int userId) { 330 List<Session2Token> list = new ArrayList<>(); 331 if (userId == ALL.getIdentifier()) { 332 int size = mUserRecords.size(); 333 for (int i = 0; i < size; i++) { 334 list.addAll(mUserRecords.valueAt(i).mPriorityStack.getSession2Tokens(userId)); 335 } 336 } else { 337 FullUserRecord user = getFullUserRecordLocked(userId); 338 list.addAll(user.mPriorityStack.getSession2Tokens(userId)); 339 } 340 return list; 341 } 342 343 /** 344 * Tells the System UI and Settings app that volume has changed on an active remote session. 345 */ notifyRemoteVolumeChanged(int flags, MediaSessionRecord session)346 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { 347 if (!session.isActive()) { 348 return; 349 } 350 synchronized (mLock) { 351 int size = mRemoteVolumeControllers.beginBroadcast(); 352 MediaSession.Token token = session.getSessionToken(); 353 for (int i = size - 1; i >= 0; i--) { 354 try { 355 IRemoteSessionCallback cb = 356 mRemoteVolumeControllers.getBroadcastItem(i); 357 cb.onVolumeChanged(token, flags); 358 } catch (Exception e) { 359 Log.w(TAG, "Error sending volume change.", e); 360 } 361 } 362 mRemoteVolumeControllers.finishBroadcast(); 363 } 364 } 365 onSessionPlaybackStateChanged(MediaSessionRecordImpl record, boolean shouldUpdatePriority)366 void onSessionPlaybackStateChanged(MediaSessionRecordImpl record, 367 boolean shouldUpdatePriority) { 368 synchronized (mLock) { 369 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 370 if (user == null || !user.mPriorityStack.contains(record)) { 371 Log.d(TAG, "Unknown session changed playback state. Ignoring."); 372 return; 373 } 374 user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority); 375 } 376 } 377 onSessionPlaybackTypeChanged(MediaSessionRecord record)378 void onSessionPlaybackTypeChanged(MediaSessionRecord record) { 379 synchronized (mLock) { 380 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 381 if (user == null || !user.mPriorityStack.contains(record)) { 382 Log.d(TAG, "Unknown session changed playback type. Ignoring."); 383 return; 384 } 385 pushRemoteVolumeUpdateLocked(record.getUserId()); 386 } 387 } 388 389 @Override onUserStarting(@onNull TargetUser user)390 public void onUserStarting(@NonNull TargetUser user) { 391 if (DEBUG) Log.d(TAG, "onStartUser: " + user); 392 updateUser(); 393 } 394 395 @Override onUserSwitching(@ullable TargetUser from, @NonNull TargetUser to)396 public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { 397 if (DEBUG) Log.d(TAG, "onSwitchUser: " + to); 398 updateUser(); 399 } 400 401 @Override onUserStopped(@onNull TargetUser targetUser)402 public void onUserStopped(@NonNull TargetUser targetUser) { 403 int userId = targetUser.getUserIdentifier(); 404 405 if (DEBUG) Log.d(TAG, "onCleanupUser: " + userId); 406 synchronized (mLock) { 407 FullUserRecord user = getFullUserRecordLocked(userId); 408 if (user != null) { 409 if (user.mFullUserId == userId) { 410 user.destroySessionsForUserLocked(ALL.getIdentifier()); 411 mUserRecords.remove(userId); 412 } else { 413 user.destroySessionsForUserLocked(userId); 414 } 415 } 416 updateUser(); 417 } 418 } 419 420 @Override monitor()421 public void monitor() { 422 synchronized (mLock) { 423 // Check for deadlock 424 } 425 } 426 enforcePhoneStatePermission(int pid, int uid)427 protected void enforcePhoneStatePermission(int pid, int uid) { 428 if (mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) 429 != PackageManager.PERMISSION_GRANTED) { 430 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); 431 } 432 } 433 onSessionDied(MediaSessionRecordImpl session)434 void onSessionDied(MediaSessionRecordImpl session) { 435 synchronized (mLock) { 436 destroySessionLocked(session); 437 } 438 } 439 updateUser()440 private void updateUser() { 441 synchronized (mLock) { 442 UserManager manager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 443 mFullUserIds.clear(); 444 List<UserHandle> allUsers = manager.getUserHandles(/*excludeDying=*/false); 445 if (allUsers != null) { 446 for (UserHandle user : allUsers) { 447 UserHandle parent = manager.getProfileParent(user); 448 if (parent != null) { 449 mFullUserIds.put(user.getIdentifier(), parent.getIdentifier()); 450 } else { 451 mFullUserIds.put(user.getIdentifier(), user.getIdentifier()); 452 if (mUserRecords.get(user.getIdentifier()) == null) { 453 mUserRecords.put(user.getIdentifier(), 454 new FullUserRecord(user.getIdentifier())); 455 } 456 } 457 } 458 } 459 // Ensure that the current full user exists. 460 int currentFullUserId = ActivityManager.getCurrentUser(); 461 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId); 462 if (mCurrentFullUserRecord == null) { 463 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); 464 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); 465 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); 466 } 467 mFullUserIds.put(currentFullUserId, currentFullUserId); 468 } 469 } 470 updateActiveSessionListeners()471 private void updateActiveSessionListeners() { 472 synchronized (mLock) { 473 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 474 SessionsListenerRecord listener = mSessionsListeners.get(i); 475 try { 476 String packageName = listener.componentName == null ? null : 477 listener.componentName.getPackageName(); 478 enforceMediaPermissions(packageName, listener.pid, listener.uid, 479 listener.userId); 480 } catch (SecurityException e) { 481 Log.i(TAG, "ActiveSessionsListener " + listener.componentName 482 + " is no longer authorized. Disconnecting."); 483 mSessionsListeners.remove(i); 484 try { 485 listener.listener 486 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); 487 } catch (Exception e1) { 488 // ignore 489 } 490 } 491 } 492 } 493 } 494 495 /* 496 * When a session is removed several things need to happen. 497 * 1. We need to remove it from the relevant user. 498 * 2. We need to remove it from the priority stack. 499 * 3. We need to remove it from all sessions. 500 * 4. If this is the system priority session we need to clear it. 501 * 5. We need to unlink to death from the cb binder 502 * 6. We need to tell the session to do any final cleanup (onDestroy) 503 */ destroySessionLocked(MediaSessionRecordImpl session)504 private void destroySessionLocked(MediaSessionRecordImpl session) { 505 if (DEBUG) { 506 Log.d(TAG, "Destroying " + session); 507 } 508 if (session.isClosed()) { 509 Log.w(TAG, "Destroying already destroyed session. Ignoring."); 510 return; 511 } 512 513 FullUserRecord user = getFullUserRecordLocked(session.getUserId()); 514 515 if (user != null && session instanceof MediaSessionRecord) { 516 final int uid = session.getUid(); 517 final int sessionCount = user.mUidToSessionCount.get(uid, 0); 518 if (sessionCount <= 0) { 519 Log.w(TAG, "destroySessionLocked: sessionCount should be positive. " 520 + "sessionCount=" + sessionCount); 521 } else { 522 user.mUidToSessionCount.put(uid, sessionCount - 1); 523 } 524 } 525 526 if (mGlobalPrioritySession == session) { 527 mGlobalPrioritySession = null; 528 if (session.isActive() && user != null) { 529 user.pushAddressedPlayerChangedLocked(); 530 } 531 } else { 532 if (user != null) { 533 user.mPriorityStack.removeSession(session); 534 } 535 } 536 537 session.close(); 538 mHandler.postSessionsChanged(session); 539 } 540 enforcePackageName(String packageName, int uid)541 private void enforcePackageName(String packageName, int uid) { 542 if (TextUtils.isEmpty(packageName)) { 543 throw new IllegalArgumentException("packageName may not be empty"); 544 } 545 if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) { 546 // If the caller is shell, then trust the packageName given and allow it 547 // to proceed. 548 return; 549 } 550 final PackageManagerInternal packageManagerInternal = 551 LocalServices.getService(PackageManagerInternal.class); 552 final int actualUid = packageManagerInternal.getPackageUid( 553 packageName, 0 /* flags */, UserHandle.getUserId(uid)); 554 if (!UserHandle.isSameApp(uid, actualUid)) { 555 throw new IllegalArgumentException("packageName does not belong to the calling uid; " 556 + "pkg=" + packageName + ", uid=" + uid); 557 } 558 } 559 tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage, int callingPid, int callingUid, String callingPackage, String reason)560 void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage, 561 int callingPid, int callingUid, String callingPackage, String reason) { 562 final long token = Binder.clearCallingIdentity(); 563 try { 564 enforcePackageName(callingPackage, callingUid); 565 if (targetUid != callingUid) { 566 boolean canAllowWhileInUse = mActivityManagerLocal 567 .canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage); 568 boolean canStartFgs = canAllowWhileInUse 569 || mActivityManagerLocal.canStartForegroundService(callingPid, callingUid, 570 callingPackage); 571 Log.i(TAG, "tempAllowlistTargetPkgIfPossible callingPackage:" 572 + callingPackage + " targetPackage:" + targetPackage 573 + " reason:" + reason 574 + (canAllowWhileInUse ? " [WIU]" : "") 575 + (canStartFgs ? " [FGS]" : "")); 576 if (canAllowWhileInUse) { 577 mActivityManagerLocal.tempAllowWhileInUsePermissionInFgs(targetUid, 578 MediaSessionDeviceConfig 579 .getMediaSessionCallbackFgsWhileInUseTempAllowDurationMs()); 580 } 581 if (canStartFgs) { 582 final Context userContext = mContext.createContextAsUser( 583 UserHandle.of(UserHandle.getUserId(targetUid)), /* flags= */ 0); 584 final PowerExemptionManager powerExemptionManager = 585 userContext.getSystemService( 586 PowerExemptionManager.class); 587 powerExemptionManager.addToTemporaryAllowList(targetPackage, 588 PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason, 589 MediaSessionDeviceConfig.getMediaSessionCallbackFgsAllowlistDurationMs()); 590 } 591 } 592 } finally { 593 Binder.restoreCallingIdentity(token); 594 } 595 } 596 597 /** 598 * Checks a caller's authorization to register an IRemoteControlDisplay. 599 * Authorization is granted if one of the following is true: 600 * <ul> 601 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL 602 * permission</li> 603 * <li>the caller's listener is one of the enabled notification listeners 604 * for the caller's user</li> 605 * </ul> 606 */ enforceMediaPermissions(String packageName, int pid, int uid, int resolvedUserId)607 private void enforceMediaPermissions(String packageName, int pid, int uid, 608 int resolvedUserId) { 609 if (hasStatusBarServicePermission(pid, uid)) return; 610 if (hasMediaControlPermission(pid, uid)) return; 611 612 if (packageName == null || !hasEnabledNotificationListener( 613 packageName, UserHandle.getUserHandleForUid(uid), resolvedUserId)) { 614 throw new SecurityException("Missing permission to control media."); 615 } 616 } 617 hasStatusBarServicePermission(int pid, int uid)618 private boolean hasStatusBarServicePermission(int pid, int uid) { 619 return mContext.checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 620 pid, uid) == PackageManager.PERMISSION_GRANTED; 621 } 622 enforceStatusBarServicePermission(String action, int pid, int uid)623 private void enforceStatusBarServicePermission(String action, int pid, int uid) { 624 if (!hasStatusBarServicePermission(pid, uid)) { 625 throw new SecurityException("Only System UI and Settings may " + action); 626 } 627 } 628 hasMediaControlPermission(int pid, int uid)629 private boolean hasMediaControlPermission(int pid, int uid) { 630 // Check if it's system server or has MEDIA_CONTENT_CONTROL. 631 // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra 632 // check here. 633 if (uid == Process.SYSTEM_UID || mContext.checkPermission( 634 android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) 635 == PackageManager.PERMISSION_GRANTED) { 636 return true; 637 } else if (DEBUG) { 638 Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); 639 } 640 return false; 641 } 642 643 /** 644 * This checks if the given package has an enabled notification listener for the 645 * specified user. Enabled components may only operate on behalf of the user 646 * they're running as. 647 * 648 * @param packageName The package name. 649 * @param userHandle The user handle of the caller. 650 * @param forUserId The user id they're making the request on behalf of. 651 * @return True if the app has an enabled notification listener for the user, false otherwise 652 */ hasEnabledNotificationListener(String packageName, UserHandle userHandle, int forUserId)653 private boolean hasEnabledNotificationListener(String packageName, 654 UserHandle userHandle, int forUserId) { 655 if (userHandle.getIdentifier() != forUserId) { 656 // You may not access another user's content as an enabled listener. 657 return false; 658 } 659 if (DEBUG) { 660 Log.d(TAG, "Checking whether the package " + packageName + " has an" 661 + " enabled notification listener."); 662 } 663 return mNotificationManager.hasEnabledNotificationListener(packageName, userHandle); 664 } 665 666 /* 667 * When a session is created the following things need to happen. 668 * 1. Its callback binder needs a link to death 669 * 2. It needs to be added to all sessions. 670 * 3. It needs to be added to the priority stack. 671 * 4. It needs to be added to the relevant user record. 672 */ createSessionInternal(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo)673 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, 674 String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) { 675 synchronized (mLock) { 676 int policies = 0; 677 if (mCustomMediaSessionPolicyProvider != null) { 678 policies = mCustomMediaSessionPolicyProvider.getSessionPoliciesForApplication( 679 callerUid, callerPackageName); 680 } 681 682 FullUserRecord user = getFullUserRecordLocked(userId); 683 if (user == null) { 684 Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); 685 throw new RuntimeException("Session request from invalid user."); 686 } 687 688 final int sessionCount = user.mUidToSessionCount.get(callerUid, 0); 689 if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID 690 && !hasMediaControlPermission(callerPid, callerUid)) { 691 throw new RuntimeException("Created too many sessions. count=" 692 + sessionCount + ")"); 693 } 694 695 final MediaSessionRecord session; 696 try { 697 session = new MediaSessionRecord(callerPid, callerUid, userId, 698 callerPackageName, cb, tag, sessionInfo, this, 699 mRecordThread.getLooper(), policies); 700 } catch (RemoteException e) { 701 throw new RuntimeException("Media Session owner died prematurely.", e); 702 } 703 704 user.mUidToSessionCount.put(callerUid, sessionCount + 1); 705 706 user.mPriorityStack.addSession(session); 707 mHandler.postSessionsChanged(session); 708 709 if (DEBUG) { 710 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); 711 } 712 return session; 713 } 714 } 715 findIndexOfSessionsListenerLocked(IActiveSessionsListener listener)716 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { 717 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 718 if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) { 719 return i; 720 } 721 } 722 return -1; 723 } 724 findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener)725 private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) { 726 for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { 727 if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) { 728 return i; 729 } 730 } 731 return -1; 732 } 733 pushSession1Changed(int userId)734 private void pushSession1Changed(int userId) { 735 synchronized (mLock) { 736 FullUserRecord user = getFullUserRecordLocked(userId); 737 if (user == null) { 738 Log.w(TAG, "pushSession1ChangedOnHandler failed. No user with id=" + userId); 739 return; 740 } 741 List<MediaSessionRecord> records = getActiveSessionsLocked(userId); 742 int size = records.size(); 743 ArrayList<MediaSession.Token> tokens = new ArrayList<>(); 744 for (int i = 0; i < size; i++) { 745 tokens.add(records.get(i).getSessionToken()); 746 } 747 pushRemoteVolumeUpdateLocked(userId); 748 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 749 SessionsListenerRecord record = mSessionsListeners.get(i); 750 if (record.userId == ALL.getIdentifier() || record.userId == userId) { 751 try { 752 record.listener.onActiveSessionsChanged(tokens); 753 } catch (RemoteException e) { 754 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", 755 e); 756 mSessionsListeners.remove(i); 757 } 758 } 759 } 760 } 761 } 762 pushSession2Changed(int userId)763 void pushSession2Changed(int userId) { 764 synchronized (mLock) { 765 List<Session2Token> allSession2Tokens = getSession2TokensLocked(ALL.getIdentifier()); 766 List<Session2Token> session2Tokens = getSession2TokensLocked(userId); 767 768 for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { 769 Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); 770 try { 771 if (listenerRecord.userId == ALL.getIdentifier()) { 772 listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); 773 } else if (listenerRecord.userId == userId) { 774 listenerRecord.listener.onSession2TokensChanged(session2Tokens); 775 } 776 } catch (RemoteException e) { 777 Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); 778 mSession2TokensListenerRecords.remove(i); 779 } 780 } 781 } 782 } 783 pushRemoteVolumeUpdateLocked(int userId)784 private void pushRemoteVolumeUpdateLocked(int userId) { 785 FullUserRecord user = getFullUserRecordLocked(userId); 786 if (user == null) { 787 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId); 788 return; 789 } 790 791 synchronized (mLock) { 792 int size = mRemoteVolumeControllers.beginBroadcast(); 793 MediaSessionRecordImpl record = user.mPriorityStack.getDefaultRemoteSession(userId); 794 if (record instanceof MediaSession2Record) { 795 // TODO(jaewan): Implement 796 return; 797 } 798 MediaSession.Token token = record == null 799 ? null : ((MediaSessionRecord) record).getSessionToken(); 800 801 for (int i = size - 1; i >= 0; i--) { 802 try { 803 IRemoteSessionCallback cb = 804 mRemoteVolumeControllers.getBroadcastItem(i); 805 cb.onSessionChanged(token); 806 } catch (Exception e) { 807 Log.w(TAG, "Error sending default remote volume.", e); 808 } 809 } 810 mRemoteVolumeControllers.finishBroadcast(); 811 } 812 } 813 814 /** 815 * Called when the media button receiver for the {@code record} is changed. 816 * 817 * @param record the media session whose media button receiver is updated. 818 */ onMediaButtonReceiverChanged(MediaSessionRecordImpl record)819 public void onMediaButtonReceiverChanged(MediaSessionRecordImpl record) { 820 synchronized (mLock) { 821 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 822 MediaSessionRecordImpl mediaButtonSession = 823 user.mPriorityStack.getMediaButtonSession(); 824 if (record == mediaButtonSession) { 825 user.rememberMediaButtonReceiverLocked(mediaButtonSession); 826 } 827 } 828 } 829 getCallingPackageName(int uid)830 private String getCallingPackageName(int uid) { 831 String[] packages = mContext.getPackageManager().getPackagesForUid(uid); 832 if (packages != null && packages.length > 0) { 833 return packages[0]; 834 } 835 return ""; 836 } 837 dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent)838 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) { 839 if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { 840 return; 841 } 842 try { 843 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent); 844 } catch (RemoteException e) { 845 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener"); 846 } 847 } 848 getFullUserRecordLocked(int userId)849 private FullUserRecord getFullUserRecordLocked(int userId) { 850 int fullUserId = mFullUserIds.get(userId, -1); 851 if (fullUserId < 0) { 852 return null; 853 } 854 return mUserRecords.get(fullUserId); 855 } 856 getMediaSessionRecordLocked(MediaSession.Token sessionToken)857 private MediaSessionRecord getMediaSessionRecordLocked(MediaSession.Token sessionToken) { 858 FullUserRecord user = getFullUserRecordLocked( 859 UserHandle.getUserHandleForUid(sessionToken.getUid()).getIdentifier()); 860 if (user != null) { 861 return user.mPriorityStack.getMediaSessionRecord(sessionToken); 862 } 863 return null; 864 } 865 instantiateCustomDispatcher(String componentName)866 private void instantiateCustomDispatcher(String componentName) { 867 synchronized (mLock) { 868 mCustomMediaKeyDispatcher = null; 869 870 try { 871 if (componentName != null && !TextUtils.isEmpty(componentName)) { 872 Class customDispatcherClass = Class.forName(componentName); 873 Constructor constructor = 874 customDispatcherClass.getDeclaredConstructor(Context.class); 875 mCustomMediaKeyDispatcher = 876 (MediaKeyDispatcher) constructor.newInstance(mContext); 877 } 878 } catch (ClassNotFoundException | InstantiationException | InvocationTargetException 879 | IllegalAccessException | NoSuchMethodException e) { 880 mCustomMediaKeyDispatcher = null; 881 Log.w(TAG, "Encountered problem while using reflection", e); 882 } 883 } 884 } 885 instantiateCustomProvider(String componentName)886 private void instantiateCustomProvider(String componentName) { 887 synchronized (mLock) { 888 mCustomMediaSessionPolicyProvider = null; 889 890 try { 891 if (componentName != null && !TextUtils.isEmpty(componentName)) { 892 Class customProviderClass = Class.forName(componentName); 893 Constructor constructor = 894 customProviderClass.getDeclaredConstructor(Context.class); 895 mCustomMediaSessionPolicyProvider = 896 (MediaSessionPolicyProvider) constructor.newInstance(mContext); 897 } 898 } catch (ClassNotFoundException | InstantiationException | InvocationTargetException 899 | IllegalAccessException | NoSuchMethodException e) { 900 Log.w(TAG, "Encountered problem while using reflection", e); 901 } 902 } 903 } 904 905 /** 906 * Information about a full user and its corresponding managed profiles. 907 * 908 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate 909 * them when they press a media/volume button. So keeping media sessions for them in one 910 * place makes more sense and increases the readability.</p> 911 * <p>The contents of this object is guarded by {@link #mLock}. 912 */ 913 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener { 914 private final int mFullUserId; 915 private final ContentResolver mContentResolver; 916 private final MediaSessionStack mPriorityStack; 917 private final HashMap<IBinder, OnMediaKeyEventDispatchedListenerRecord> 918 mOnMediaKeyEventDispatchedListeners = new HashMap<>(); 919 private final HashMap<IBinder, OnMediaKeyEventSessionChangedListenerRecord> 920 mOnMediaKeyEventSessionChangedListeners = new HashMap<>(); 921 private final SparseIntArray mUidToSessionCount = new SparseIntArray(); 922 923 private MediaButtonReceiverHolder mLastMediaButtonReceiverHolder; 924 925 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener; 926 private int mOnVolumeKeyLongPressListenerUid; 927 928 private IOnMediaKeyListener mOnMediaKeyListener; 929 private int mOnMediaKeyListenerUid; 930 FullUserRecord(int fullUserId)931 FullUserRecord(int fullUserId) { 932 mFullUserId = fullUserId; 933 mContentResolver = mContext.createContextAsUser(UserHandle.of(mFullUserId), 0) 934 .getContentResolver(); 935 mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); 936 // Restore the remembered media button receiver before the boot. 937 String mediaButtonReceiverInfo = Settings.Secure.getString(mContentResolver, 938 MEDIA_BUTTON_RECEIVER); 939 mLastMediaButtonReceiverHolder = 940 MediaButtonReceiverHolder.unflattenFromString( 941 mContext, mediaButtonReceiverInfo); 942 } 943 destroySessionsForUserLocked(int userId)944 public void destroySessionsForUserLocked(int userId) { 945 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId); 946 for (MediaSessionRecord session : sessions) { 947 destroySessionLocked(session); 948 } 949 } 950 addOnMediaKeyEventDispatchedListenerLocked( IOnMediaKeyEventDispatchedListener listener, int uid)951 public void addOnMediaKeyEventDispatchedListenerLocked( 952 IOnMediaKeyEventDispatchedListener listener, int uid) { 953 IBinder cbBinder = listener.asBinder(); 954 OnMediaKeyEventDispatchedListenerRecord cr = 955 new OnMediaKeyEventDispatchedListenerRecord(listener, uid); 956 mOnMediaKeyEventDispatchedListeners.put(cbBinder, cr); 957 try { 958 cbBinder.linkToDeath(cr, 0); 959 } catch (RemoteException e) { 960 Log.w(TAG, "Failed to add listener", e); 961 mOnMediaKeyEventDispatchedListeners.remove(cbBinder); 962 } 963 } 964 removeOnMediaKeyEventDispatchedListenerLocked( IOnMediaKeyEventDispatchedListener listener)965 public void removeOnMediaKeyEventDispatchedListenerLocked( 966 IOnMediaKeyEventDispatchedListener listener) { 967 IBinder cbBinder = listener.asBinder(); 968 OnMediaKeyEventDispatchedListenerRecord cr = 969 mOnMediaKeyEventDispatchedListeners.remove(cbBinder); 970 cbBinder.unlinkToDeath(cr, 0); 971 } 972 addOnMediaKeyEventSessionChangedListenerLocked( IOnMediaKeyEventSessionChangedListener listener, int uid)973 public void addOnMediaKeyEventSessionChangedListenerLocked( 974 IOnMediaKeyEventSessionChangedListener listener, int uid) { 975 IBinder cbBinder = listener.asBinder(); 976 OnMediaKeyEventSessionChangedListenerRecord cr = 977 new OnMediaKeyEventSessionChangedListenerRecord(listener, uid); 978 mOnMediaKeyEventSessionChangedListeners.put(cbBinder, cr); 979 try { 980 cbBinder.linkToDeath(cr, 0); 981 } catch (RemoteException e) { 982 Log.w(TAG, "Failed to add listener", e); 983 mOnMediaKeyEventSessionChangedListeners.remove(cbBinder); 984 } 985 } 986 removeOnMediaKeyEventSessionChangedListener( IOnMediaKeyEventSessionChangedListener listener)987 public void removeOnMediaKeyEventSessionChangedListener( 988 IOnMediaKeyEventSessionChangedListener listener) { 989 IBinder cbBinder = listener.asBinder(); 990 OnMediaKeyEventSessionChangedListenerRecord cr = 991 mOnMediaKeyEventSessionChangedListeners.remove(cbBinder); 992 cbBinder.unlinkToDeath(cr, 0); 993 } 994 dumpLocked(PrintWriter pw, String prefix)995 public void dumpLocked(PrintWriter pw, String prefix) { 996 pw.print(prefix + "Record for full_user=" + mFullUserId); 997 // Dump managed profile user ids associated with this user. 998 int size = mFullUserIds.size(); 999 for (int i = 0; i < size; i++) { 1000 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i) 1001 && mFullUserIds.valueAt(i) == mFullUserId) { 1002 pw.print(", profile_user=" + mFullUserIds.keyAt(i)); 1003 } 1004 } 1005 pw.println(); 1006 String indent = prefix + " "; 1007 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener); 1008 pw.println(indent + "Volume key long-press listener package: " 1009 + getCallingPackageName(mOnVolumeKeyLongPressListenerUid)); 1010 pw.println(indent + "Media key listener: " + mOnMediaKeyListener); 1011 pw.println(indent + "Media key listener package: " 1012 + getCallingPackageName(mOnMediaKeyListenerUid)); 1013 pw.println(indent + "OnMediaKeyEventDispatchedListener: added " 1014 + mOnMediaKeyEventDispatchedListeners.size() + " listener(s)"); 1015 for (OnMediaKeyEventDispatchedListenerRecord cr 1016 : mOnMediaKeyEventDispatchedListeners.values()) { 1017 pw.println(indent + " from " + getCallingPackageName(cr.uid)); 1018 } 1019 pw.println(indent + "OnMediaKeyEventSessionChangedListener: added " 1020 + mOnMediaKeyEventSessionChangedListeners.size() + " listener(s)"); 1021 for (OnMediaKeyEventSessionChangedListenerRecord cr 1022 : mOnMediaKeyEventSessionChangedListeners.values()) { 1023 pw.println(indent + " from " + getCallingPackageName(cr.uid)); 1024 } 1025 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiverHolder); 1026 mPriorityStack.dump(pw, indent); 1027 } 1028 1029 @Override onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession, MediaSessionRecordImpl newMediaButtonSession)1030 public void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession, 1031 MediaSessionRecordImpl newMediaButtonSession) { 1032 if (DEBUG_KEY_EVENT) { 1033 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession); 1034 } 1035 synchronized (mLock) { 1036 if (oldMediaButtonSession != null) { 1037 mHandler.postSessionsChanged(oldMediaButtonSession); 1038 } 1039 if (newMediaButtonSession != null) { 1040 rememberMediaButtonReceiverLocked(newMediaButtonSession); 1041 mHandler.postSessionsChanged(newMediaButtonSession); 1042 } 1043 pushAddressedPlayerChangedLocked(); 1044 } 1045 } 1046 1047 // Remember media button receiver and keep it in the persistent storage. rememberMediaButtonReceiverLocked(MediaSessionRecordImpl record)1048 public void rememberMediaButtonReceiverLocked(MediaSessionRecordImpl record) { 1049 if (record instanceof MediaSession2Record) { 1050 // TODO(jaewan): Implement 1051 return; 1052 } 1053 MediaSessionRecord sessionRecord = (MediaSessionRecord) record; 1054 mLastMediaButtonReceiverHolder = sessionRecord.getMediaButtonReceiver(); 1055 String mediaButtonReceiverInfo = (mLastMediaButtonReceiverHolder == null) 1056 ? "" : mLastMediaButtonReceiverHolder.flattenToString(); 1057 Settings.Secure.putString(mContentResolver, 1058 MEDIA_BUTTON_RECEIVER, 1059 mediaButtonReceiverInfo); 1060 } 1061 pushAddressedPlayerChangedLocked( IOnMediaKeyEventSessionChangedListener callback)1062 private void pushAddressedPlayerChangedLocked( 1063 IOnMediaKeyEventSessionChangedListener callback) { 1064 try { 1065 MediaSessionRecordImpl mediaButtonSession = getMediaButtonSessionLocked(); 1066 if (mediaButtonSession != null) { 1067 if (mediaButtonSession instanceof MediaSessionRecord) { 1068 MediaSessionRecord session1 = (MediaSessionRecord) mediaButtonSession; 1069 callback.onMediaKeyEventSessionChanged(session1.getPackageName(), 1070 session1.getSessionToken()); 1071 } else { 1072 // TODO(jaewan): Implement 1073 } 1074 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiverHolder != null) { 1075 String packageName = 1076 mCurrentFullUserRecord.mLastMediaButtonReceiverHolder.getPackageName(); 1077 callback.onMediaKeyEventSessionChanged(packageName, null); 1078 } else { 1079 callback.onMediaKeyEventSessionChanged("", null); 1080 } 1081 } catch (RemoteException e) { 1082 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); 1083 } 1084 } 1085 pushAddressedPlayerChangedLocked()1086 private void pushAddressedPlayerChangedLocked() { 1087 for (OnMediaKeyEventSessionChangedListenerRecord cr 1088 : mOnMediaKeyEventSessionChangedListeners.values()) { 1089 pushAddressedPlayerChangedLocked(cr.callback); 1090 } 1091 } 1092 getMediaButtonSessionLocked()1093 private MediaSessionRecordImpl getMediaButtonSessionLocked() { 1094 return isGlobalPriorityActiveLocked() 1095 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); 1096 } 1097 1098 final class OnMediaKeyEventDispatchedListenerRecord implements IBinder.DeathRecipient { 1099 public final IOnMediaKeyEventDispatchedListener callback; 1100 public final int uid; 1101 OnMediaKeyEventDispatchedListenerRecord(IOnMediaKeyEventDispatchedListener callback, int uid)1102 OnMediaKeyEventDispatchedListenerRecord(IOnMediaKeyEventDispatchedListener callback, 1103 int uid) { 1104 this.callback = callback; 1105 this.uid = uid; 1106 } 1107 1108 @Override binderDied()1109 public void binderDied() { 1110 synchronized (mLock) { 1111 mOnMediaKeyEventDispatchedListeners.remove(callback.asBinder()); 1112 } 1113 } 1114 } 1115 1116 final class OnMediaKeyEventSessionChangedListenerRecord implements IBinder.DeathRecipient { 1117 public final IOnMediaKeyEventSessionChangedListener callback; 1118 public final int uid; 1119 OnMediaKeyEventSessionChangedListenerRecord( IOnMediaKeyEventSessionChangedListener callback, int uid)1120 OnMediaKeyEventSessionChangedListenerRecord( 1121 IOnMediaKeyEventSessionChangedListener callback, int uid) { 1122 this.callback = callback; 1123 this.uid = uid; 1124 } 1125 1126 @Override binderDied()1127 public void binderDied() { 1128 synchronized (mLock) { 1129 mOnMediaKeyEventSessionChangedListeners.remove(callback.asBinder()); 1130 } 1131 } 1132 } 1133 } 1134 1135 final class SessionsListenerRecord implements IBinder.DeathRecipient { 1136 public final IActiveSessionsListener listener; 1137 public final ComponentName componentName; 1138 public final int userId; 1139 public final int pid; 1140 public final int uid; 1141 SessionsListenerRecord(IActiveSessionsListener listener, ComponentName componentName, int userId, int pid, int uid)1142 SessionsListenerRecord(IActiveSessionsListener listener, 1143 ComponentName componentName, 1144 int userId, int pid, int uid) { 1145 this.listener = listener; 1146 this.componentName = componentName; 1147 this.userId = userId; 1148 this.pid = pid; 1149 this.uid = uid; 1150 } 1151 1152 @Override binderDied()1153 public void binderDied() { 1154 synchronized (mLock) { 1155 mSessionsListeners.remove(this); 1156 } 1157 } 1158 } 1159 1160 final class Session2TokensListenerRecord implements IBinder.DeathRecipient { 1161 public final ISession2TokensListener listener; 1162 public final int userId; 1163 Session2TokensListenerRecord(ISession2TokensListener listener, int userId)1164 Session2TokensListenerRecord(ISession2TokensListener listener, 1165 int userId) { 1166 this.listener = listener; 1167 this.userId = userId; 1168 } 1169 1170 @Override binderDied()1171 public void binderDied() { 1172 synchronized (mLock) { 1173 mSession2TokensListenerRecords.remove(this); 1174 } 1175 } 1176 } 1177 1178 class SessionManagerImpl extends ISessionManager.Stub { 1179 private static final String EXTRA_WAKELOCK_ACQUIRED = 1180 "android.media.AudioService.WAKELOCK_ACQUIRED"; 1181 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number 1182 1183 private KeyEventHandler mMediaKeyEventHandler = 1184 new KeyEventHandler(KeyEventHandler.KEY_TYPE_MEDIA); 1185 private KeyEventHandler mVolumeKeyEventHandler = 1186 new KeyEventHandler(KeyEventHandler.KEY_TYPE_VOLUME); 1187 1188 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1189 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 1190 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 1191 String[] packageNames = 1192 mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid()); 1193 String packageName = packageNames != null && packageNames.length > 0 1194 ? packageNames[0] 1195 : "com.android.shell"; // We should not need this branch, but defaulting to the 1196 // current shell package name for robustness. See 1197 // b/227109905. 1198 new MediaShellCommand(packageName) 1199 .exec(this, in, out, err, args, callback, resultReceiver); 1200 } 1201 1202 @Override createSession(String packageName, ISessionCallback cb, String tag, Bundle sessionInfo, int userId)1203 public ISession createSession(String packageName, ISessionCallback cb, String tag, 1204 Bundle sessionInfo, int userId) throws RemoteException { 1205 final int pid = Binder.getCallingPid(); 1206 final int uid = Binder.getCallingUid(); 1207 final long token = Binder.clearCallingIdentity(); 1208 try { 1209 enforcePackageName(packageName, uid); 1210 int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName); 1211 if (cb == null) { 1212 throw new IllegalArgumentException("Controller callback cannot be null"); 1213 } 1214 MediaSessionRecord session = createSessionInternal( 1215 pid, uid, resolvedUserId, packageName, cb, tag, sessionInfo); 1216 if (session == null) { 1217 throw new IllegalStateException("Failed to create a new session record"); 1218 } 1219 ISession sessionBinder = session.getSessionBinder(); 1220 if (sessionBinder == null) { 1221 throw new IllegalStateException("Invalid session record"); 1222 } 1223 return sessionBinder; 1224 } catch (Exception e) { 1225 Log.w(TAG, "Exception in creating a new session", e); 1226 throw e; 1227 } finally { 1228 Binder.restoreCallingIdentity(token); 1229 } 1230 } 1231 1232 @Override getSessions(ComponentName componentName, int userId)1233 public List<MediaSession.Token> getSessions(ComponentName componentName, int userId) { 1234 final int pid = Binder.getCallingPid(); 1235 final int uid = Binder.getCallingUid(); 1236 final long token = Binder.clearCallingIdentity(); 1237 1238 try { 1239 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 1240 ArrayList<MediaSession.Token> tokens = new ArrayList<>(); 1241 synchronized (mLock) { 1242 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId); 1243 for (MediaSessionRecord record : records) { 1244 tokens.add(record.getSessionToken()); 1245 } 1246 } 1247 return tokens; 1248 } finally { 1249 Binder.restoreCallingIdentity(token); 1250 } 1251 } 1252 1253 @Override getMediaKeyEventSession(final String packageName)1254 public MediaSession.Token getMediaKeyEventSession(final String packageName) { 1255 final int pid = Binder.getCallingPid(); 1256 final int uid = Binder.getCallingUid(); 1257 final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 1258 final int userId = userHandle.getIdentifier(); 1259 final long token = Binder.clearCallingIdentity(); 1260 try { 1261 enforcePackageName(packageName, uid); 1262 enforceMediaPermissions(packageName, pid, uid, userId); 1263 1264 MediaSessionRecordImpl record; 1265 synchronized (mLock) { 1266 FullUserRecord user = getFullUserRecordLocked(userId); 1267 if (user == null) { 1268 Log.w(TAG, "No matching user record to get the media key event session" 1269 + ", userId=" + userId); 1270 return null; 1271 } 1272 record = user.getMediaButtonSessionLocked(); 1273 } 1274 if (record instanceof MediaSessionRecord) { 1275 return ((MediaSessionRecord) record).getSessionToken(); 1276 } 1277 //TODO: Handle media session 2 case 1278 return null; 1279 } finally { 1280 Binder.restoreCallingIdentity(token); 1281 } 1282 } 1283 1284 @Override getMediaKeyEventSessionPackageName(final String packageName)1285 public String getMediaKeyEventSessionPackageName(final String packageName) { 1286 final int pid = Binder.getCallingPid(); 1287 final int uid = Binder.getCallingUid(); 1288 final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 1289 final int userId = userHandle.getIdentifier(); 1290 final long token = Binder.clearCallingIdentity(); 1291 try { 1292 enforcePackageName(packageName, uid); 1293 enforceMediaPermissions(packageName, pid, uid, userId); 1294 1295 MediaSessionRecordImpl record; 1296 synchronized (mLock) { 1297 FullUserRecord user = getFullUserRecordLocked(userId); 1298 if (user == null) { 1299 Log.w(TAG, "No matching user record to get the media key event session" 1300 + " package , userId=" + userId); 1301 return ""; 1302 } 1303 record = user.getMediaButtonSessionLocked(); 1304 if (record instanceof MediaSessionRecord) { 1305 return record.getPackageName(); 1306 //TODO: Handle media session 2 case 1307 } else if (user.mLastMediaButtonReceiverHolder != null) { 1308 return user.mLastMediaButtonReceiverHolder.getPackageName(); 1309 } 1310 } 1311 return ""; 1312 } finally { 1313 Binder.restoreCallingIdentity(token); 1314 } 1315 } 1316 1317 @Override addSessionsListener(IActiveSessionsListener listener, ComponentName componentName, int userId)1318 public void addSessionsListener(IActiveSessionsListener listener, 1319 ComponentName componentName, int userId) throws RemoteException { 1320 if (listener == null) { 1321 Log.w(TAG, "addSessionsListener: listener is null, ignoring"); 1322 return; 1323 } 1324 final int pid = Binder.getCallingPid(); 1325 final int uid = Binder.getCallingUid(); 1326 final long token = Binder.clearCallingIdentity(); 1327 1328 try { 1329 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 1330 synchronized (mLock) { 1331 int index = findIndexOfSessionsListenerLocked(listener); 1332 if (index != -1) { 1333 Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); 1334 return; 1335 } 1336 SessionsListenerRecord record = new SessionsListenerRecord(listener, 1337 componentName, resolvedUserId, pid, uid); 1338 try { 1339 listener.asBinder().linkToDeath(record, 0); 1340 } catch (RemoteException e) { 1341 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); 1342 return; 1343 } 1344 mSessionsListeners.add(record); 1345 } 1346 } finally { 1347 Binder.restoreCallingIdentity(token); 1348 } 1349 } 1350 1351 @Override removeSessionsListener(IActiveSessionsListener listener)1352 public void removeSessionsListener(IActiveSessionsListener listener) 1353 throws RemoteException { 1354 synchronized (mLock) { 1355 int index = findIndexOfSessionsListenerLocked(listener); 1356 if (index != -1) { 1357 SessionsListenerRecord record = mSessionsListeners.remove(index); 1358 try { 1359 record.listener.asBinder().unlinkToDeath(record, 0); 1360 } catch (Exception e) { 1361 // ignore exceptions, the record is being removed 1362 } 1363 } 1364 } 1365 } 1366 1367 @Override addSession2TokensListener(ISession2TokensListener listener, int userId)1368 public void addSession2TokensListener(ISession2TokensListener listener, 1369 int userId) { 1370 if (listener == null) { 1371 Log.w(TAG, "addSession2TokensListener: listener is null, ignoring"); 1372 return; 1373 } 1374 final int pid = Binder.getCallingPid(); 1375 final int uid = Binder.getCallingUid(); 1376 final long token = Binder.clearCallingIdentity(); 1377 1378 try { 1379 // Check that they can make calls on behalf of the user and get the final user id. 1380 int resolvedUserId = handleIncomingUser(pid, uid, userId, null); 1381 synchronized (mLock) { 1382 int index = findIndexOfSession2TokensListenerLocked(listener); 1383 if (index >= 0) { 1384 Log.w(TAG, "addSession2TokensListener: " 1385 + "listener is already added, ignoring"); 1386 return; 1387 } 1388 mSession2TokensListenerRecords.add( 1389 new Session2TokensListenerRecord(listener, resolvedUserId)); 1390 } 1391 } finally { 1392 Binder.restoreCallingIdentity(token); 1393 } 1394 } 1395 1396 @Override removeSession2TokensListener(ISession2TokensListener listener)1397 public void removeSession2TokensListener(ISession2TokensListener listener) { 1398 final int pid = Binder.getCallingPid(); 1399 final int uid = Binder.getCallingUid(); 1400 final long token = Binder.clearCallingIdentity(); 1401 1402 try { 1403 synchronized (mLock) { 1404 int index = findIndexOfSession2TokensListenerLocked(listener); 1405 if (index >= 0) { 1406 Session2TokensListenerRecord listenerRecord = 1407 mSession2TokensListenerRecords.remove(index); 1408 try { 1409 listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0); 1410 } catch (Exception e) { 1411 // Ignore exception. 1412 } 1413 } 1414 } 1415 } finally { 1416 Binder.restoreCallingIdentity(token); 1417 } 1418 } 1419 1420 /** 1421 * Dispaches media key events. This is called when the foreground activity didn't handled 1422 * the incoming media key event. 1423 * <p> 1424 * Handles the dispatching of the media button events to one of the 1425 * registered listeners, or if there was none, broadcast an 1426 * ACTION_MEDIA_BUTTON intent to the rest of the system. 1427 * 1428 * @param packageName The caller package 1429 * @param asSystemService {@code true} if the event sent to the session came from the 1430 * service instead of the app process. This helps sessions to distinguish between 1431 * the key injection by the app and key events from the hardware devices. Should be 1432 * used only when the hardware key events aren't handled by foreground activity. 1433 * {@code false} otherwise to tell session about the real caller. 1434 * @param keyEvent a non-null KeyEvent whose key code is one of the 1435 * supported media buttons 1436 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held 1437 * while this key event is dispatched. 1438 */ 1439 @Override dispatchMediaKeyEvent(String packageName, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock)1440 public void dispatchMediaKeyEvent(String packageName, boolean asSystemService, 1441 KeyEvent keyEvent, boolean needWakeLock) { 1442 if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { 1443 Log.w(TAG, "Attempted to dispatch null or non-media key event."); 1444 return; 1445 } 1446 1447 final int pid = Binder.getCallingPid(); 1448 final int uid = Binder.getCallingUid(); 1449 final long token = Binder.clearCallingIdentity(); 1450 try { 1451 if (DEBUG) { 1452 Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid 1453 + ", uid=" + uid + ", asSystem=" + asSystemService + ", event=" 1454 + keyEvent); 1455 } 1456 if (!isUserSetupComplete()) { 1457 // Global media key handling can have the side-effect of starting new 1458 // activities which is undesirable while setup is in progress. 1459 Log.i(TAG, "Not dispatching media key event because user " 1460 + "setup is in progress."); 1461 return; 1462 } 1463 1464 synchronized (mLock) { 1465 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked(); 1466 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) { 1467 // Prevent dispatching key event through reflection while the global 1468 // priority session is active. 1469 Log.i(TAG, "Only the system can dispatch media key event " 1470 + "to the global priority session."); 1471 return; 1472 } 1473 if (!isGlobalPriorityActive) { 1474 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) { 1475 if (DEBUG_KEY_EVENT) { 1476 Log.d(TAG, "Send " + keyEvent + " to the media key listener"); 1477 } 1478 try { 1479 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent, 1480 new MediaKeyListenerResultReceiver(packageName, pid, uid, 1481 asSystemService, keyEvent, needWakeLock)); 1482 return; 1483 } catch (RemoteException e) { 1484 Log.w(TAG, "Failed to send " + keyEvent 1485 + " to the media key listener"); 1486 } 1487 } 1488 } 1489 if (isGlobalPriorityActive) { 1490 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, 1491 keyEvent, needWakeLock); 1492 } else { 1493 mMediaKeyEventHandler.handleMediaKeyEventLocked(packageName, pid, uid, 1494 asSystemService, keyEvent, needWakeLock); 1495 } 1496 } 1497 } finally { 1498 Binder.restoreCallingIdentity(token); 1499 } 1500 } 1501 1502 /** 1503 * Dispatches media key events to session as system service. This is used only when the 1504 * foreground activity has set 1505 * {@link android.app.Activity#setMediaController(MediaController)} and a media key was 1506 * pressed. 1507 * 1508 * @param packageName The caller's package name, obtained by Context#getPackageName() 1509 * @param sessionToken token for the session that the controller is pointing to 1510 * @param keyEvent media key event 1511 * @see #dispatchVolumeKeyEvent 1512 */ 1513 @Override dispatchMediaKeyEventToSessionAsSystemService(String packageName, KeyEvent keyEvent, MediaSession.Token sessionToken)1514 public boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName, 1515 KeyEvent keyEvent, MediaSession.Token sessionToken) { 1516 final int pid = Binder.getCallingPid(); 1517 final int uid = Binder.getCallingUid(); 1518 final long token = Binder.clearCallingIdentity(); 1519 try { 1520 synchronized (mLock) { 1521 MediaSessionRecord record = getMediaSessionRecordLocked(sessionToken); 1522 if (DEBUG_KEY_EVENT) { 1523 Log.d(TAG, "dispatchMediaKeyEventToSessionAsSystemService, pkg=" 1524 + packageName + ", pid=" + pid + ", uid=" + uid + ", sessionToken=" 1525 + sessionToken + ", event=" + keyEvent + ", session=" + record); 1526 } 1527 if (record == null) { 1528 Log.w(TAG, "Failed to find session to dispatch key event."); 1529 return false; 1530 } 1531 return record.sendMediaButton(packageName, pid, uid, true /* asSystemService */, 1532 keyEvent, 0, null); 1533 } 1534 } finally { 1535 Binder.restoreCallingIdentity(token); 1536 } 1537 } 1538 1539 @Override addOnMediaKeyEventDispatchedListener( final IOnMediaKeyEventDispatchedListener listener)1540 public void addOnMediaKeyEventDispatchedListener( 1541 final IOnMediaKeyEventDispatchedListener listener) { 1542 if (listener == null) { 1543 Log.w(TAG, "addOnMediaKeyEventDispatchedListener: listener is null, ignoring"); 1544 return; 1545 } 1546 final int pid = Binder.getCallingPid(); 1547 final int uid = Binder.getCallingUid(); 1548 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1549 final long token = Binder.clearCallingIdentity(); 1550 try { 1551 if (!hasMediaControlPermission(pid, uid)) { 1552 throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to" 1553 + " add MediaKeyEventDispatchedListener"); 1554 } 1555 synchronized (mLock) { 1556 FullUserRecord user = getFullUserRecordLocked(userId); 1557 if (user == null || user.mFullUserId != userId) { 1558 Log.w(TAG, "Only the full user can add the listener" 1559 + ", userId=" + userId); 1560 return; 1561 } 1562 user.addOnMediaKeyEventDispatchedListenerLocked(listener, uid); 1563 Log.d(TAG, "The MediaKeyEventDispatchedListener (" + listener.asBinder() 1564 + ") is added by " + getCallingPackageName(uid)); 1565 } 1566 } finally { 1567 Binder.restoreCallingIdentity(token); 1568 } 1569 } 1570 1571 @Override removeOnMediaKeyEventDispatchedListener( final IOnMediaKeyEventDispatchedListener listener)1572 public void removeOnMediaKeyEventDispatchedListener( 1573 final IOnMediaKeyEventDispatchedListener listener) { 1574 if (listener == null) { 1575 Log.w(TAG, "removeOnMediaKeyEventDispatchedListener: listener is null, ignoring"); 1576 return; 1577 } 1578 final int pid = Binder.getCallingPid(); 1579 final int uid = Binder.getCallingUid(); 1580 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1581 final long token = Binder.clearCallingIdentity(); 1582 try { 1583 if (!hasMediaControlPermission(pid, uid)) { 1584 throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to" 1585 + " remove MediaKeyEventDispatchedListener"); 1586 } 1587 synchronized (mLock) { 1588 FullUserRecord user = getFullUserRecordLocked(userId); 1589 if (user == null || user.mFullUserId != userId) { 1590 Log.w(TAG, "Only the full user can remove the listener" 1591 + ", userId=" + userId); 1592 return; 1593 } 1594 user.removeOnMediaKeyEventDispatchedListenerLocked(listener); 1595 Log.d(TAG, "The MediaKeyEventDispatchedListener (" + listener.asBinder() 1596 + ") is removed by " + getCallingPackageName(uid)); 1597 } 1598 } finally { 1599 Binder.restoreCallingIdentity(token); 1600 } 1601 } 1602 1603 @Override addOnMediaKeyEventSessionChangedListener( final IOnMediaKeyEventSessionChangedListener listener, final String packageName)1604 public void addOnMediaKeyEventSessionChangedListener( 1605 final IOnMediaKeyEventSessionChangedListener listener, 1606 final String packageName) { 1607 if (listener == null) { 1608 Log.w(TAG, "addOnMediaKeyEventSessionChangedListener: listener is null, ignoring"); 1609 return; 1610 } 1611 1612 final int pid = Binder.getCallingPid(); 1613 final int uid = Binder.getCallingUid(); 1614 final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 1615 final int userId = userHandle.getIdentifier(); 1616 final long token = Binder.clearCallingIdentity(); 1617 try { 1618 enforcePackageName(packageName, uid); 1619 enforceMediaPermissions(packageName, pid, uid, userId); 1620 1621 synchronized (mLock) { 1622 FullUserRecord user = getFullUserRecordLocked(userId); 1623 if (user == null || user.mFullUserId != userId) { 1624 Log.w(TAG, "Only the full user can add the listener" 1625 + ", userId=" + userId); 1626 return; 1627 } 1628 user.addOnMediaKeyEventSessionChangedListenerLocked(listener, uid); 1629 Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + listener.asBinder() 1630 + ") is added by " + packageName); 1631 } 1632 } finally { 1633 Binder.restoreCallingIdentity(token); 1634 } 1635 } 1636 1637 @Override removeOnMediaKeyEventSessionChangedListener( final IOnMediaKeyEventSessionChangedListener listener)1638 public void removeOnMediaKeyEventSessionChangedListener( 1639 final IOnMediaKeyEventSessionChangedListener listener) { 1640 if (listener == null) { 1641 Log.w(TAG, "removeOnMediaKeyEventSessionChangedListener: listener is null," 1642 + " ignoring"); 1643 return; 1644 } 1645 1646 final int pid = Binder.getCallingPid(); 1647 final int uid = Binder.getCallingUid(); 1648 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1649 final long token = Binder.clearCallingIdentity(); 1650 try { 1651 synchronized (mLock) { 1652 FullUserRecord user = getFullUserRecordLocked(userId); 1653 if (user == null || user.mFullUserId != userId) { 1654 Log.w(TAG, "Only the full user can remove the listener" 1655 + ", userId=" + userId); 1656 return; 1657 } 1658 user.removeOnMediaKeyEventSessionChangedListener(listener); 1659 Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + listener.asBinder() 1660 + ") is removed by " + getCallingPackageName(uid)); 1661 } 1662 } finally { 1663 Binder.restoreCallingIdentity(token); 1664 } 1665 } 1666 1667 @Override setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener)1668 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) { 1669 final int pid = Binder.getCallingPid(); 1670 final int uid = Binder.getCallingUid(); 1671 final long token = Binder.clearCallingIdentity(); 1672 try { 1673 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission. 1674 if (mContext.checkPermission( 1675 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid) 1676 != PackageManager.PERMISSION_GRANTED) { 1677 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" 1678 + " permission."); 1679 } 1680 1681 synchronized (mLock) { 1682 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1683 FullUserRecord user = getFullUserRecordLocked(userId); 1684 if (user == null || user.mFullUserId != userId) { 1685 Log.w(TAG, "Only the full user can set the volume key long-press listener" 1686 + ", userId=" + userId); 1687 return; 1688 } 1689 if (user.mOnVolumeKeyLongPressListener != null 1690 && user.mOnVolumeKeyLongPressListenerUid != uid) { 1691 Log.w(TAG, "The volume key long-press listener cannot be reset" 1692 + " by another app , mOnVolumeKeyLongPressListener=" 1693 + user.mOnVolumeKeyLongPressListenerUid 1694 + ", uid=" + uid); 1695 return; 1696 } 1697 1698 user.mOnVolumeKeyLongPressListener = listener; 1699 user.mOnVolumeKeyLongPressListenerUid = uid; 1700 1701 Log.d(TAG, "The volume key long-press listener " 1702 + listener + " is set by " + getCallingPackageName(uid)); 1703 1704 if (user.mOnVolumeKeyLongPressListener != null) { 1705 try { 1706 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath( 1707 new IBinder.DeathRecipient() { 1708 @Override 1709 public void binderDied() { 1710 synchronized (mLock) { 1711 user.mOnVolumeKeyLongPressListener = null; 1712 } 1713 } 1714 }, 0); 1715 } catch (RemoteException e) { 1716 Log.w(TAG, "Failed to set death recipient " 1717 + user.mOnVolumeKeyLongPressListener); 1718 user.mOnVolumeKeyLongPressListener = null; 1719 } 1720 } 1721 } 1722 } finally { 1723 Binder.restoreCallingIdentity(token); 1724 } 1725 } 1726 1727 @Override setOnMediaKeyListener(IOnMediaKeyListener listener)1728 public void setOnMediaKeyListener(IOnMediaKeyListener listener) { 1729 final int pid = Binder.getCallingPid(); 1730 final int uid = Binder.getCallingUid(); 1731 final long token = Binder.clearCallingIdentity(); 1732 try { 1733 // Enforce SET_MEDIA_KEY_LISTENER permission. 1734 if (mContext.checkPermission( 1735 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid) 1736 != PackageManager.PERMISSION_GRANTED) { 1737 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER permission."); 1738 } 1739 1740 synchronized (mLock) { 1741 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1742 FullUserRecord user = getFullUserRecordLocked(userId); 1743 if (user == null || user.mFullUserId != userId) { 1744 Log.w(TAG, "Only the full user can set the media key listener" 1745 + ", userId=" + userId); 1746 return; 1747 } 1748 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) { 1749 Log.w(TAG, "The media key listener cannot be reset by another app. " 1750 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid 1751 + ", uid=" + uid); 1752 return; 1753 } 1754 1755 user.mOnMediaKeyListener = listener; 1756 user.mOnMediaKeyListenerUid = uid; 1757 1758 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener 1759 + " is set by " + getCallingPackageName(uid)); 1760 1761 if (user.mOnMediaKeyListener != null) { 1762 try { 1763 user.mOnMediaKeyListener.asBinder().linkToDeath( 1764 new IBinder.DeathRecipient() { 1765 @Override 1766 public void binderDied() { 1767 synchronized (mLock) { 1768 user.mOnMediaKeyListener = null; 1769 } 1770 } 1771 }, 0); 1772 } catch (RemoteException e) { 1773 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener); 1774 user.mOnMediaKeyListener = null; 1775 } 1776 } 1777 } 1778 } finally { 1779 Binder.restoreCallingIdentity(token); 1780 } 1781 } 1782 1783 /** 1784 * Dispatches volume key events. This is called when the foreground activity didn't handle 1785 * the incoming volume key event. 1786 * <p> 1787 * Handles the dispatching of the volume button events to one of the 1788 * registered listeners. If there's a volume key long-press listener and 1789 * there's no active global priority session, long-presses will be sent to the 1790 * long-press listener instead of adjusting volume. 1791 * 1792 * @param packageName The caller's package name, obtained by Context#getPackageName() 1793 * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName() 1794 * @param asSystemService {@code true} if the event sent to the session as if it was come 1795 * from the system service instead of the app process. This helps sessions to 1796 * distinguish between the key injection by the app and key events from the 1797 * hardware devices. Should be used only when the volume key events aren't handled 1798 * by foreground activity. {@code false} otherwise to tell session about the real 1799 * caller. 1800 * @param keyEvent a non-null KeyEvent whose key code is one of the 1801 * {@link KeyEvent#KEYCODE_VOLUME_UP}, 1802 * {@link KeyEvent#KEYCODE_VOLUME_DOWN}, 1803 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}. 1804 * @param stream stream type to adjust volume. 1805 * @param musicOnly true if both UI and haptic feedback aren't needed when adjusting volume. 1806 * @see #dispatchVolumeKeyEventToSessionAsSystemService 1807 */ 1808 @Override dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly)1809 public void dispatchVolumeKeyEvent(String packageName, String opPackageName, 1810 boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) { 1811 if (keyEvent == null 1812 || (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP 1813 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN 1814 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) { 1815 Log.w(TAG, "Attempted to dispatch null or non-volume key event."); 1816 return; 1817 } 1818 1819 final int pid = Binder.getCallingPid(); 1820 final int uid = Binder.getCallingUid(); 1821 final long token = Binder.clearCallingIdentity(); 1822 1823 if (DEBUG_KEY_EVENT) { 1824 Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName 1825 + ", opPkg=" + opPackageName + ", pid=" + pid + ", uid=" + uid 1826 + ", asSystem=" + asSystemService + ", event=" + keyEvent 1827 + ", stream=" + stream + ", musicOnly=" + musicOnly); 1828 } 1829 1830 try { 1831 synchronized (mLock) { 1832 if (isGlobalPriorityActiveLocked()) { 1833 dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, 1834 asSystemService, keyEvent, stream, musicOnly); 1835 } else { 1836 // TODO: Consider the case when both volume up and down keys are pressed 1837 // at the same time. 1838 mVolumeKeyEventHandler.handleVolumeKeyEventLocked(packageName, pid, uid, 1839 asSystemService, keyEvent, opPackageName, stream, musicOnly); 1840 } 1841 } 1842 } finally { 1843 Binder.restoreCallingIdentity(token); 1844 } 1845 } 1846 dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly)1847 private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid, 1848 int uid, boolean asSystemService, KeyEvent keyEvent, int stream, 1849 boolean musicOnly) { 1850 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; 1851 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; 1852 int direction = 0; 1853 boolean isMute = false; 1854 switch (keyEvent.getKeyCode()) { 1855 case KeyEvent.KEYCODE_VOLUME_UP: 1856 direction = AudioManager.ADJUST_RAISE; 1857 break; 1858 case KeyEvent.KEYCODE_VOLUME_DOWN: 1859 direction = AudioManager.ADJUST_LOWER; 1860 break; 1861 case KeyEvent.KEYCODE_VOLUME_MUTE: 1862 isMute = true; 1863 break; 1864 } 1865 if (down || up) { 1866 int flags = AudioManager.FLAG_FROM_KEY; 1867 if (!musicOnly) { 1868 // These flags are consistent with the home screen 1869 if (up) { 1870 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; 1871 } else { 1872 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE; 1873 } 1874 } 1875 if (direction != 0) { 1876 // If this is action up we want to send a beep for non-music events 1877 if (up) { 1878 direction = 0; 1879 } 1880 dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, 1881 asSystemService, stream, direction, flags, musicOnly); 1882 } else if (isMute) { 1883 if (down && keyEvent.getRepeatCount() == 0) { 1884 dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, 1885 asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags, 1886 musicOnly); 1887 } 1888 } 1889 } 1890 } 1891 1892 /** 1893 * Dispatches volume key events to session as system service. This is used only when the 1894 * foreground activity has set 1895 * {@link android.app.Activity#setMediaController(MediaController)} and a hardware volume 1896 * key was pressed. 1897 * 1898 * @param packageName The caller's package name, obtained by Context#getPackageName() 1899 * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName() 1900 * @param sessionToken token for the session that the controller is pointing to 1901 * @param keyEvent volume key event 1902 * @see #dispatchVolumeKeyEvent 1903 */ 1904 @Override dispatchVolumeKeyEventToSessionAsSystemService(String packageName, String opPackageName, KeyEvent keyEvent, MediaSession.Token sessionToken)1905 public void dispatchVolumeKeyEventToSessionAsSystemService(String packageName, 1906 String opPackageName, KeyEvent keyEvent, MediaSession.Token sessionToken) { 1907 int pid = Binder.getCallingPid(); 1908 int uid = Binder.getCallingUid(); 1909 final long token = Binder.clearCallingIdentity(); 1910 try { 1911 synchronized (mLock) { 1912 MediaSessionRecord record = getMediaSessionRecordLocked(sessionToken); 1913 if (DEBUG_KEY_EVENT) { 1914 Log.d(TAG, "dispatchVolumeKeyEventToSessionAsSystemService, pkg=" 1915 + packageName + ", opPkg=" + opPackageName + ", pid=" + pid 1916 + ", uid=" + uid + ", sessionToken=" + sessionToken + ", event=" 1917 + keyEvent + ", session=" + record); 1918 } 1919 if (record == null) { 1920 Log.w(TAG, "Failed to find session to dispatch key event, token=" 1921 + sessionToken + ". Fallbacks to the default handling."); 1922 dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, true, 1923 keyEvent, AudioManager.USE_DEFAULT_STREAM_TYPE, false); 1924 return; 1925 } 1926 switch (keyEvent.getAction()) { 1927 case KeyEvent.ACTION_DOWN: { 1928 int direction = 0; 1929 switch (keyEvent.getKeyCode()) { 1930 case KeyEvent.KEYCODE_VOLUME_UP: 1931 direction = AudioManager.ADJUST_RAISE; 1932 break; 1933 case KeyEvent.KEYCODE_VOLUME_DOWN: 1934 direction = AudioManager.ADJUST_LOWER; 1935 break; 1936 case KeyEvent.KEYCODE_VOLUME_MUTE: 1937 direction = AudioManager.ADJUST_TOGGLE_MUTE; 1938 break; 1939 } 1940 record.adjustVolume(packageName, opPackageName, pid, uid, 1941 true /* asSystemService */, direction, 1942 AudioManager.FLAG_SHOW_UI, false /* useSuggested */); 1943 break; 1944 } 1945 1946 case KeyEvent.ACTION_UP: { 1947 final int flags = 1948 AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE 1949 | AudioManager.FLAG_FROM_KEY; 1950 record.adjustVolume(packageName, opPackageName, pid, uid, 1951 true /* asSystemService */, 0, flags, false /* useSuggested */); 1952 } 1953 } 1954 } 1955 } finally { 1956 Binder.restoreCallingIdentity(token); 1957 } 1958 } 1959 1960 @Override dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream, int delta, int flags)1961 public void dispatchAdjustVolume(String packageName, String opPackageName, 1962 int suggestedStream, int delta, int flags) { 1963 final int pid = Binder.getCallingPid(); 1964 final int uid = Binder.getCallingUid(); 1965 final long token = Binder.clearCallingIdentity(); 1966 try { 1967 synchronized (mLock) { 1968 dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false, 1969 suggestedStream, delta, flags, false); 1970 } 1971 } finally { 1972 Binder.restoreCallingIdentity(token); 1973 } 1974 } 1975 1976 @Override registerRemoteSessionCallback(IRemoteSessionCallback rvc)1977 public void registerRemoteSessionCallback(IRemoteSessionCallback rvc) { 1978 final int pid = Binder.getCallingPid(); 1979 final int uid = Binder.getCallingUid(); 1980 final long token = Binder.clearCallingIdentity(); 1981 synchronized (mLock) { 1982 try { 1983 enforceStatusBarServicePermission("listen for volume changes", pid, uid); 1984 mRemoteVolumeControllers.register(rvc); 1985 } finally { 1986 Binder.restoreCallingIdentity(token); 1987 } 1988 } 1989 } 1990 1991 @Override unregisterRemoteSessionCallback(IRemoteSessionCallback rvc)1992 public void unregisterRemoteSessionCallback(IRemoteSessionCallback rvc) { 1993 final int pid = Binder.getCallingPid(); 1994 final int uid = Binder.getCallingUid(); 1995 final long token = Binder.clearCallingIdentity(); 1996 synchronized (mLock) { 1997 try { 1998 enforceStatusBarServicePermission("listen for volume changes", pid, uid); 1999 mRemoteVolumeControllers.unregister(rvc); 2000 } finally { 2001 Binder.restoreCallingIdentity(token); 2002 } 2003 } 2004 } 2005 2006 @Override isGlobalPriorityActive()2007 public boolean isGlobalPriorityActive() { 2008 synchronized (mLock) { 2009 return isGlobalPriorityActiveLocked(); 2010 } 2011 } 2012 2013 @Override dump(FileDescriptor fd, final PrintWriter pw, String[] args)2014 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 2015 if (!MediaServerUtils.checkDumpPermission(mContext, TAG, pw)) return; 2016 2017 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); 2018 pw.println(); 2019 2020 synchronized (mLock) { 2021 pw.println(mSessionsListeners.size() + " sessions listeners."); 2022 pw.println("Global priority session is " + mGlobalPrioritySession); 2023 if (mGlobalPrioritySession != null) { 2024 mGlobalPrioritySession.dump(pw, " "); 2025 } 2026 pw.println("User Records:"); 2027 int count = mUserRecords.size(); 2028 for (int i = 0; i < count; i++) { 2029 mUserRecords.valueAt(i).dumpLocked(pw, ""); 2030 } 2031 mAudioPlayerStateMonitor.dump(mContext, pw, ""); 2032 } 2033 MediaSessionDeviceConfig.dump(pw, ""); 2034 } 2035 2036 /** 2037 * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL 2038 * permission or an enabled notification listener) 2039 * 2040 * @param controllerPackageName package name of the controller app 2041 * @param controllerPid pid of the controller app 2042 * @param controllerUid uid of the controller app 2043 */ 2044 @Override isTrusted(String controllerPackageName, int controllerPid, int controllerUid)2045 public boolean isTrusted(String controllerPackageName, int controllerPid, 2046 int controllerUid) { 2047 final int uid = Binder.getCallingUid(); 2048 final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 2049 final long token = Binder.clearCallingIdentity(); 2050 try { 2051 // Don't perform check between controllerPackageName and controllerUid. 2052 // When an (activity|service) runs on the another apps process by specifying 2053 // android:process in the AndroidManifest.xml, then PID and UID would have the 2054 // running process' information instead of the (activity|service) that has created 2055 // MediaController. 2056 // Note that we can use Context#getOpPackageName() instead of 2057 // Context#getPackageName() for getting package name that matches with the PID/UID, 2058 // but it doesn't tell which package has created the MediaController, so useless. 2059 return hasMediaControlPermission(controllerPid, controllerUid) 2060 || hasEnabledNotificationListener( 2061 userId, controllerPackageName, controllerUid); 2062 } finally { 2063 Binder.restoreCallingIdentity(token); 2064 } 2065 } 2066 2067 @Override setCustomMediaKeyDispatcher(String name)2068 public void setCustomMediaKeyDispatcher(String name) { 2069 instantiateCustomDispatcher(name); 2070 } 2071 2072 @Override setCustomMediaSessionPolicyProvider(String name)2073 public void setCustomMediaSessionPolicyProvider(String name) { 2074 instantiateCustomProvider(name); 2075 } 2076 2077 @Override hasCustomMediaKeyDispatcher(String componentName)2078 public boolean hasCustomMediaKeyDispatcher(String componentName) { 2079 return mCustomMediaKeyDispatcher == null ? false 2080 : TextUtils.equals(componentName, 2081 mCustomMediaKeyDispatcher.getClass().getName()); 2082 } 2083 2084 @Override hasCustomMediaSessionPolicyProvider(String componentName)2085 public boolean hasCustomMediaSessionPolicyProvider(String componentName) { 2086 return mCustomMediaSessionPolicyProvider == null ? false 2087 : TextUtils.equals(componentName, 2088 mCustomMediaSessionPolicyProvider.getClass().getName()); 2089 } 2090 2091 @Override getSessionPolicies(MediaSession.Token token)2092 public int getSessionPolicies(MediaSession.Token token) { 2093 synchronized (mLock) { 2094 MediaSessionRecord record = getMediaSessionRecordLocked(token); 2095 if (record != null) { 2096 return record.getSessionPolicies(); 2097 } 2098 } 2099 return 0; 2100 } 2101 2102 @Override setSessionPolicies(MediaSession.Token token, int policies)2103 public void setSessionPolicies(MediaSession.Token token, int policies) { 2104 final long callingIdentityToken = Binder.clearCallingIdentity(); 2105 try { 2106 synchronized (mLock) { 2107 MediaSessionRecord record = getMediaSessionRecordLocked(token); 2108 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 2109 if (record != null && user != null) { 2110 record.setSessionPolicies(policies); 2111 user.mPriorityStack.updateMediaButtonSessionBySessionPolicyChange(record); 2112 } 2113 } 2114 } finally { 2115 Binder.restoreCallingIdentity(callingIdentityToken); 2116 } 2117 } 2118 2119 // For MediaSession verifySessionsRequest(ComponentName componentName, int userId, final int pid, final int uid)2120 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, 2121 final int uid) { 2122 String packageName = null; 2123 if (componentName != null) { 2124 // If they gave us a component name verify they own the 2125 // package 2126 packageName = componentName.getPackageName(); 2127 enforcePackageName(packageName, uid); 2128 } 2129 // Check that they can make calls on behalf of the user and get the final user id 2130 int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName); 2131 // Check if they have the permissions or their component is enabled for the user 2132 // they're calling from. 2133 enforceMediaPermissions(packageName, pid, uid, resolvedUserId); 2134 return resolvedUserId; 2135 } 2136 2137 // Handles incoming user by checking whether the caller has permission to access the 2138 // given user id's information or not. Permission is not necessary if the given user id is 2139 // equal to the caller's user id, but if not, the caller needs to have the 2140 // INTERACT_ACROSS_USERS_FULL permission. Otherwise, a security exception will be thrown. 2141 // The return value will be the given user id, unless the given user id is 2142 // UserHandle.CURRENT, which will return the ActivityManager.getCurrentUser() value instead. handleIncomingUser(int pid, int uid, int userId, String packageName)2143 private int handleIncomingUser(int pid, int uid, int userId, String packageName) { 2144 int callingUserId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 2145 if (userId == callingUserId) { 2146 return userId; 2147 } 2148 2149 boolean canInteractAcrossUsersFull = mContext.checkPermission( 2150 INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED; 2151 if (canInteractAcrossUsersFull) { 2152 if (userId == CURRENT.getIdentifier()) { 2153 return ActivityManager.getCurrentUser(); 2154 } 2155 return userId; 2156 } 2157 2158 throw new SecurityException("Permission denied while calling from " + packageName 2159 + " with user id: " + userId + "; Need to run as either the calling user id (" 2160 + callingUserId + "), or with " + INTERACT_ACROSS_USERS_FULL + " permission"); 2161 } 2162 hasEnabledNotificationListener(int callingUserId, String controllerPackageName, int controllerUid)2163 private boolean hasEnabledNotificationListener(int callingUserId, 2164 String controllerPackageName, int controllerUid) { 2165 int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier(); 2166 if (callingUserId != controllerUserId) { 2167 // Enabled notification listener only works within the same user. 2168 return false; 2169 } 2170 // Verify whether package name and controller UID. 2171 // It will indirectly check whether the caller has obtained the package name and UID 2172 // via ControllerInfo or with the valid package name visibility. 2173 try { 2174 int actualControllerUid = mContext.getPackageManager().getPackageUidAsUser( 2175 controllerPackageName, 2176 UserHandle.getUserId(controllerUid)); 2177 if (controllerUid != actualControllerUid) { 2178 Log.w(TAG, "Failed to check enabled notification listener. Package name and" 2179 + " UID doesn't match"); 2180 return false; 2181 } 2182 } catch (PackageManager.NameNotFoundException e) { 2183 Log.w(TAG, "Failed to check enabled notification listener. Package name doesn't" 2184 + " exist"); 2185 return false; 2186 } 2187 2188 if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName, 2189 UserHandle.getUserHandleForUid(controllerUid))) { 2190 return true; 2191 } 2192 if (DEBUG) { 2193 Log.d(TAG, controllerPackageName + " (uid=" + controllerUid 2194 + ") doesn't have an enabled notification listener"); 2195 } 2196 return false; 2197 } 2198 dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, int suggestedStream, int direction, int flags, boolean musicOnly)2199 private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid, 2200 int uid, boolean asSystemService, int suggestedStream, int direction, int flags, 2201 boolean musicOnly) { 2202 MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession 2203 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); 2204 2205 boolean preferSuggestedStream = false; 2206 if (isValidLocalStreamType(suggestedStream) 2207 && AudioSystem.isStreamActive(suggestedStream, 0)) { 2208 preferSuggestedStream = true; 2209 } 2210 if (session == null || preferSuggestedStream) { 2211 if (DEBUG_KEY_EVENT) { 2212 Log.d(TAG, "Adjusting suggestedStream=" + suggestedStream + " by " + direction 2213 + ". flags=" + flags + ", preferSuggestedStream=" 2214 + preferSuggestedStream + ", session=" + session); 2215 } 2216 if (musicOnly && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { 2217 if (DEBUG_KEY_EVENT) { 2218 Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event," 2219 + " flags=" + flags); 2220 } 2221 return; 2222 } 2223 2224 // Execute mAudioService.adjustSuggestedStreamVolume() on 2225 // handler thread of MediaSessionService. 2226 // This will release the MediaSessionService.mLock sooner and avoid 2227 // a potential deadlock between MediaSessionService.mLock and 2228 // ActivityManagerService lock. 2229 mHandler.post(new Runnable() { 2230 @Override 2231 public void run() { 2232 final String callingOpPackageName; 2233 final int callingUid; 2234 final int callingPid; 2235 if (asSystemService) { 2236 callingOpPackageName = mContext.getOpPackageName(); 2237 callingUid = Process.myUid(); 2238 callingPid = Process.myPid(); 2239 } else { 2240 callingOpPackageName = opPackageName; 2241 callingUid = uid; 2242 callingPid = pid; 2243 } 2244 try { 2245 mAudioManager.adjustSuggestedStreamVolumeForUid(suggestedStream, 2246 direction, flags, callingOpPackageName, callingUid, callingPid, 2247 getContext().getApplicationInfo().targetSdkVersion); 2248 } catch (SecurityException | IllegalArgumentException e) { 2249 Log.e(TAG, "Cannot adjust volume: direction=" + direction 2250 + ", suggestedStream=" + suggestedStream + ", flags=" + flags 2251 + ", packageName=" + packageName + ", uid=" + uid 2252 + ", asSystemService=" + asSystemService, e); 2253 } 2254 } 2255 }); 2256 } else { 2257 if (DEBUG_KEY_EVENT) { 2258 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags=" 2259 + flags + ", suggestedStream=" + suggestedStream 2260 + ", preferSuggestedStream=" + preferSuggestedStream); 2261 } 2262 session.adjustVolume(packageName, opPackageName, pid, uid, asSystemService, 2263 direction, flags, true); 2264 } 2265 } 2266 dispatchMediaKeyEventLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock)2267 private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, 2268 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { 2269 if (mCurrentFullUserRecord.getMediaButtonSessionLocked() 2270 instanceof MediaSession2Record) { 2271 // TODO(jaewan): Make MediaSession2 to receive media key event 2272 return; 2273 } 2274 MediaSessionRecord session = null; 2275 MediaButtonReceiverHolder mediaButtonReceiverHolder = null; 2276 2277 if (mCustomMediaKeyDispatcher != null) { 2278 MediaSession.Token token = mCustomMediaKeyDispatcher.getMediaSession( 2279 keyEvent, uid, asSystemService); 2280 if (token != null) { 2281 session = getMediaSessionRecordLocked(token); 2282 } 2283 2284 if (session == null) { 2285 PendingIntent pi = mCustomMediaKeyDispatcher.getMediaButtonReceiver(keyEvent, 2286 uid, asSystemService); 2287 if (pi != null) { 2288 mediaButtonReceiverHolder = 2289 MediaButtonReceiverHolder.create( 2290 mCurrentFullUserRecord.mFullUserId, pi, ""); 2291 } 2292 } 2293 } 2294 2295 if (session == null && mediaButtonReceiverHolder == null) { 2296 session = (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked(); 2297 2298 if (session == null) { 2299 mediaButtonReceiverHolder = 2300 mCurrentFullUserRecord.mLastMediaButtonReceiverHolder; 2301 } 2302 } 2303 2304 if (session != null) { 2305 if (DEBUG_KEY_EVENT) { 2306 Log.d(TAG, "Sending " + keyEvent + " to " + session); 2307 } 2308 if (needWakeLock) { 2309 mKeyEventReceiver.acquireWakeLockLocked(); 2310 } 2311 // If we don't need a wakelock use -1 as the id so we won't release it later. 2312 session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent, 2313 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, 2314 mKeyEventReceiver); 2315 try { 2316 for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr 2317 : mCurrentFullUserRecord.mOnMediaKeyEventDispatchedListeners.values()) { 2318 cr.callback.onMediaKeyEventDispatched( 2319 keyEvent, session.getPackageName(), session.getSessionToken()); 2320 } 2321 } catch (RemoteException e) { 2322 Log.w(TAG, "Failed to send callback", e); 2323 } 2324 } else if (mediaButtonReceiverHolder != null) { 2325 if (needWakeLock) { 2326 mKeyEventReceiver.acquireWakeLockLocked(); 2327 } 2328 String callingPackageName = 2329 (asSystemService) ? mContext.getPackageName() : packageName; 2330 boolean sent = mediaButtonReceiverHolder.send( 2331 mContext, keyEvent, callingPackageName, 2332 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver, 2333 mHandler, 2334 MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs()); 2335 if (sent) { 2336 String pkgName = mediaButtonReceiverHolder.getPackageName(); 2337 for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr 2338 : mCurrentFullUserRecord 2339 .mOnMediaKeyEventDispatchedListeners.values()) { 2340 try { 2341 cr.callback.onMediaKeyEventDispatched(keyEvent, pkgName, null); 2342 } catch (RemoteException e) { 2343 Log.w(TAG, "Failed notify key event dispatch, uid=" + cr.uid, e); 2344 } 2345 } 2346 } 2347 } 2348 } 2349 startVoiceInput(boolean needWakeLock)2350 private void startVoiceInput(boolean needWakeLock) { 2351 Intent voiceIntent = null; 2352 // select which type of search to launch: 2353 // - screen on and device unlocked: action is ACTION_WEB_SEARCH 2354 // - device locked or screen off: action is 2355 // ACTION_VOICE_SEARCH_HANDS_FREE 2356 // with EXTRA_SECURE set to true if the device is securely locked 2357 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 2358 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); 2359 if (!isLocked && pm.isScreenOn()) { 2360 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); 2361 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH"); 2362 } else { 2363 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); 2364 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, 2365 isLocked && mKeyguardManager.isKeyguardSecure()); 2366 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE"); 2367 } 2368 // start the search activity 2369 if (needWakeLock) { 2370 mMediaEventWakeLock.acquire(); 2371 } 2372 try { 2373 if (voiceIntent != null) { 2374 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 2375 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 2376 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent); 2377 mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT); 2378 } 2379 } catch (ActivityNotFoundException e) { 2380 Log.w(TAG, "No activity for search: " + e); 2381 } finally { 2382 if (needWakeLock) { 2383 mMediaEventWakeLock.release(); 2384 } 2385 } 2386 } 2387 isVoiceKey(int keyCode)2388 private boolean isVoiceKey(int keyCode) { 2389 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK 2390 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 2391 } 2392 isUserSetupComplete()2393 private boolean isUserSetupComplete() { 2394 return Settings.Secure.getIntForUser(mContext.getContentResolver(), 2395 Settings.Secure.USER_SETUP_COMPLETE, 0, CURRENT.getIdentifier()) != 0; 2396 } 2397 2398 // we only handle public stream types, which are 0-5 isValidLocalStreamType(int streamType)2399 private boolean isValidLocalStreamType(int streamType) { 2400 return streamType >= AudioManager.STREAM_VOICE_CALL 2401 && streamType <= AudioManager.STREAM_NOTIFICATION; 2402 } 2403 2404 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { 2405 private final String mPackageName; 2406 private final int mPid; 2407 private final int mUid; 2408 private final boolean mAsSystemService; 2409 private final KeyEvent mKeyEvent; 2410 private final boolean mNeedWakeLock; 2411 private boolean mHandled; 2412 MediaKeyListenerResultReceiver(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock)2413 private MediaKeyListenerResultReceiver(String packageName, int pid, int uid, 2414 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { 2415 super(mHandler); 2416 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT); 2417 mPackageName = packageName; 2418 mPid = pid; 2419 mUid = uid; 2420 mAsSystemService = asSystemService; 2421 mKeyEvent = keyEvent; 2422 mNeedWakeLock = needWakeLock; 2423 } 2424 2425 @Override run()2426 public void run() { 2427 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent); 2428 dispatchMediaKeyEvent(); 2429 } 2430 2431 @Override onReceiveResult(int resultCode, Bundle resultData)2432 protected void onReceiveResult(int resultCode, Bundle resultData) { 2433 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) { 2434 mHandled = true; 2435 mHandler.removeCallbacks(this); 2436 return; 2437 } 2438 dispatchMediaKeyEvent(); 2439 } 2440 dispatchMediaKeyEvent()2441 private void dispatchMediaKeyEvent() { 2442 if (mHandled) { 2443 return; 2444 } 2445 mHandled = true; 2446 mHandler.removeCallbacks(this); 2447 synchronized (mLock) { 2448 if (isGlobalPriorityActiveLocked()) { 2449 dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, 2450 mKeyEvent, mNeedWakeLock); 2451 } else { 2452 mMediaKeyEventHandler.handleMediaKeyEventLocked(mPackageName, mPid, mUid, 2453 mAsSystemService, mKeyEvent, mNeedWakeLock); 2454 } 2455 } 2456 } 2457 } 2458 2459 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); 2460 2461 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable, 2462 PendingIntent.OnFinished { 2463 private final Handler mHandler; 2464 private int mRefCount = 0; 2465 private int mLastTimeoutId = 0; 2466 KeyEventWakeLockReceiver(Handler handler)2467 KeyEventWakeLockReceiver(Handler handler) { 2468 super(handler); 2469 mHandler = handler; 2470 } 2471 onTimeout()2472 public void onTimeout() { 2473 synchronized (mLock) { 2474 if (mRefCount == 0) { 2475 // We've already released it, so just return 2476 return; 2477 } 2478 mLastTimeoutId++; 2479 mRefCount = 0; 2480 releaseWakeLockLocked(); 2481 } 2482 } 2483 acquireWakeLockLocked()2484 public void acquireWakeLockLocked() { 2485 if (mRefCount == 0) { 2486 mMediaEventWakeLock.acquire(); 2487 } 2488 mRefCount++; 2489 mHandler.removeCallbacks(this); 2490 mHandler.postDelayed(this, WAKELOCK_TIMEOUT); 2491 2492 } 2493 2494 @Override run()2495 public void run() { 2496 onTimeout(); 2497 } 2498 2499 @Override onReceiveResult(int resultCode, Bundle resultData)2500 protected void onReceiveResult(int resultCode, Bundle resultData) { 2501 if (resultCode < mLastTimeoutId) { 2502 // Ignore results from calls that were before the last 2503 // timeout, just in case. 2504 return; 2505 } else { 2506 synchronized (mLock) { 2507 if (mRefCount > 0) { 2508 mRefCount--; 2509 if (mRefCount == 0) { 2510 releaseWakeLockLocked(); 2511 } 2512 } 2513 } 2514 } 2515 } 2516 releaseWakeLockLocked()2517 private void releaseWakeLockLocked() { 2518 mMediaEventWakeLock.release(); 2519 mHandler.removeCallbacks(this); 2520 } 2521 2522 @Override onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, String resultData, Bundle resultExtras)2523 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, 2524 String resultData, Bundle resultExtras) { 2525 onReceiveResult(resultCode, null); 2526 } 2527 }; 2528 2529 // A long press is determined by: 2530 // 1) A KeyEvent.ACTION_DOWN KeyEvent and repeat count of 0, followed by 2531 // 2) A KeyEvent.ACTION_DOWN KeyEvent with the same key code, a repeat count of 1, and 2532 // FLAG_LONG_PRESS received within ViewConfiguration.getLongPressTimeout(). 2533 // A tap is determined by: 2534 // 1) A KeyEvent.ACTION_DOWN KeyEvent followed by 2535 // 2) A KeyEvent.ACTION_UP KeyEvent with the same key code. 2536 class KeyEventHandler { 2537 private static final int KEY_TYPE_MEDIA = 0; 2538 private static final int KEY_TYPE_VOLUME = 1; 2539 2540 private KeyEvent mTrackingFirstDownKeyEvent; 2541 private boolean mIsLongPressing; 2542 private Runnable mLongPressTimeoutRunnable; 2543 private int mMultiTapCount; 2544 private Runnable mMultiTapTimeoutRunnable; 2545 private int mMultiTapKeyCode; 2546 private int mKeyType; 2547 KeyEventHandler(int keyType)2548 KeyEventHandler(int keyType) { 2549 mKeyType = keyType; 2550 } 2551 handleMediaKeyEventLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock)2552 void handleMediaKeyEventLocked(String packageName, int pid, int uid, 2553 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { 2554 handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, needWakeLock, 2555 null, 0, false); 2556 } 2557 handleVolumeKeyEventLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, String opPackageName, int stream, boolean musicOnly)2558 void handleVolumeKeyEventLocked(String packageName, int pid, int uid, 2559 boolean asSystemService, KeyEvent keyEvent, String opPackageName, int stream, 2560 boolean musicOnly) { 2561 handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, false, 2562 opPackageName, stream, musicOnly); 2563 } 2564 handleKeyEventLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, String opPackageName, int stream, boolean musicOnly)2565 void handleKeyEventLocked(String packageName, int pid, int uid, 2566 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, 2567 String opPackageName, int stream, boolean musicOnly) { 2568 if (keyEvent.isCanceled()) { 2569 return; 2570 } 2571 2572 int overriddenKeyEvents = 0; 2573 if (mCustomMediaKeyDispatcher != null 2574 && mCustomMediaKeyDispatcher.getOverriddenKeyEvents() != null) { 2575 overriddenKeyEvents = mCustomMediaKeyDispatcher.getOverriddenKeyEvents() 2576 .get(keyEvent.getKeyCode()); 2577 } 2578 cancelTrackingIfNeeded(packageName, pid, uid, asSystemService, keyEvent, 2579 needWakeLock, opPackageName, stream, musicOnly, overriddenKeyEvents); 2580 if (!needTracking(keyEvent, overriddenKeyEvents)) { 2581 if (mKeyType == KEY_TYPE_VOLUME) { 2582 dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, 2583 asSystemService, keyEvent, stream, musicOnly); 2584 } else { 2585 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, 2586 keyEvent, needWakeLock); 2587 } 2588 return; 2589 } 2590 2591 if (isFirstDownKeyEvent(keyEvent)) { 2592 mTrackingFirstDownKeyEvent = keyEvent; 2593 mIsLongPressing = false; 2594 return; 2595 } 2596 2597 // Long press is always overridden here, otherwise the key event would have been 2598 // already handled 2599 if (isFirstLongPressKeyEvent(keyEvent)) { 2600 mIsLongPressing = true; 2601 } 2602 if (mIsLongPressing) { 2603 handleLongPressLocked(keyEvent, needWakeLock, overriddenKeyEvents); 2604 return; 2605 } 2606 2607 if (keyEvent.getAction() == KeyEvent.ACTION_UP) { 2608 mTrackingFirstDownKeyEvent = null; 2609 if (shouldTrackForMultipleTapsLocked(overriddenKeyEvents)) { 2610 if (mMultiTapCount == 0) { 2611 mMultiTapTimeoutRunnable = createSingleTapRunnable(packageName, pid, 2612 uid, asSystemService, keyEvent, needWakeLock, 2613 opPackageName, stream, musicOnly, 2614 isSingleTapOverridden(overriddenKeyEvents)); 2615 if (isSingleTapOverridden(overriddenKeyEvents) 2616 && !isDoubleTapOverridden(overriddenKeyEvents) 2617 && !isTripleTapOverridden(overriddenKeyEvents)) { 2618 mMultiTapTimeoutRunnable.run(); 2619 } else { 2620 mHandler.postDelayed(mMultiTapTimeoutRunnable, 2621 MULTI_TAP_TIMEOUT); 2622 mMultiTapCount = 1; 2623 mMultiTapKeyCode = keyEvent.getKeyCode(); 2624 } 2625 } else if (mMultiTapCount == 1) { 2626 mHandler.removeCallbacks(mMultiTapTimeoutRunnable); 2627 mMultiTapTimeoutRunnable = createDoubleTapRunnable(packageName, pid, 2628 uid, asSystemService, keyEvent, needWakeLock, opPackageName, 2629 stream, musicOnly, isSingleTapOverridden(overriddenKeyEvents), 2630 isDoubleTapOverridden(overriddenKeyEvents)); 2631 if (isTripleTapOverridden(overriddenKeyEvents)) { 2632 mHandler.postDelayed(mMultiTapTimeoutRunnable, MULTI_TAP_TIMEOUT); 2633 mMultiTapCount = 2; 2634 } else { 2635 mMultiTapTimeoutRunnable.run(); 2636 } 2637 } else if (mMultiTapCount == 2) { 2638 mHandler.removeCallbacks(mMultiTapTimeoutRunnable); 2639 onTripleTap(keyEvent); 2640 } 2641 } else { 2642 dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService, 2643 keyEvent, needWakeLock, opPackageName, stream, musicOnly); 2644 } 2645 } 2646 } 2647 shouldTrackForMultipleTapsLocked(int overriddenKeyEvents)2648 private boolean shouldTrackForMultipleTapsLocked(int overriddenKeyEvents) { 2649 return isSingleTapOverridden(overriddenKeyEvents) 2650 || isDoubleTapOverridden(overriddenKeyEvents) 2651 || isTripleTapOverridden(overriddenKeyEvents); 2652 } 2653 cancelTrackingIfNeeded(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, String opPackageName, int stream, boolean musicOnly, int overriddenKeyEvents)2654 private void cancelTrackingIfNeeded(String packageName, int pid, int uid, 2655 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, 2656 String opPackageName, int stream, boolean musicOnly, int overriddenKeyEvents) { 2657 if (mTrackingFirstDownKeyEvent == null && mMultiTapTimeoutRunnable == null) { 2658 return; 2659 } 2660 2661 if (isFirstDownKeyEvent(keyEvent)) { 2662 if (mLongPressTimeoutRunnable != null) { 2663 mHandler.removeCallbacks(mLongPressTimeoutRunnable); 2664 mLongPressTimeoutRunnable.run(); 2665 } 2666 if (mMultiTapTimeoutRunnable != null 2667 && keyEvent.getKeyCode() != mMultiTapKeyCode) { 2668 runExistingMultiTapRunnableLocked(); 2669 } 2670 resetLongPressTracking(); 2671 return; 2672 } 2673 2674 if (mTrackingFirstDownKeyEvent != null 2675 && mTrackingFirstDownKeyEvent.getDownTime() == keyEvent.getDownTime() 2676 && mTrackingFirstDownKeyEvent.getKeyCode() == keyEvent.getKeyCode() 2677 && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { 2678 if (isFirstLongPressKeyEvent(keyEvent)) { 2679 if (mMultiTapTimeoutRunnable != null) { 2680 runExistingMultiTapRunnableLocked(); 2681 } 2682 if ((overriddenKeyEvents & KEY_EVENT_LONG_PRESS) == 0) { 2683 if (mKeyType == KEY_TYPE_VOLUME) { 2684 if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { 2685 dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, 2686 uid, asSystemService, keyEvent, stream, musicOnly); 2687 mTrackingFirstDownKeyEvent = null; 2688 } 2689 } else if (!isVoiceKey(keyEvent.getKeyCode())) { 2690 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, 2691 keyEvent, needWakeLock); 2692 mTrackingFirstDownKeyEvent = null; 2693 } 2694 } 2695 } else if (keyEvent.getRepeatCount() > 1 && !mIsLongPressing) { 2696 resetLongPressTracking(); 2697 } 2698 } 2699 } 2700 needTracking(KeyEvent keyEvent, int overriddenKeyEvents)2701 private boolean needTracking(KeyEvent keyEvent, int overriddenKeyEvents) { 2702 if (!isFirstDownKeyEvent(keyEvent)) { 2703 if (mTrackingFirstDownKeyEvent == null) { 2704 return false; 2705 } else if (mTrackingFirstDownKeyEvent.getDownTime() != keyEvent.getDownTime() 2706 || mTrackingFirstDownKeyEvent.getKeyCode() != keyEvent.getKeyCode()) { 2707 return false; 2708 } 2709 } 2710 if (overriddenKeyEvents == 0) { 2711 if (mKeyType == KEY_TYPE_VOLUME) { 2712 if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { 2713 return false; 2714 } 2715 } else if (!isVoiceKey(keyEvent.getKeyCode())) { 2716 return false; 2717 } 2718 } 2719 return true; 2720 } 2721 runExistingMultiTapRunnableLocked()2722 private void runExistingMultiTapRunnableLocked() { 2723 mHandler.removeCallbacks(mMultiTapTimeoutRunnable); 2724 mMultiTapTimeoutRunnable.run(); 2725 } 2726 resetMultiTapTrackingLocked()2727 private void resetMultiTapTrackingLocked() { 2728 mMultiTapCount = 0; 2729 mMultiTapTimeoutRunnable = null; 2730 mMultiTapKeyCode = 0; 2731 } 2732 handleLongPressLocked(KeyEvent keyEvent, boolean needWakeLock, int overriddenKeyEvents)2733 private void handleLongPressLocked(KeyEvent keyEvent, boolean needWakeLock, 2734 int overriddenKeyEvents) { 2735 if (mCustomMediaKeyDispatcher != null 2736 && isLongPressOverridden(overriddenKeyEvents)) { 2737 mCustomMediaKeyDispatcher.onLongPress(keyEvent); 2738 2739 if (mLongPressTimeoutRunnable != null) { 2740 mHandler.removeCallbacks(mLongPressTimeoutRunnable); 2741 } 2742 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { 2743 if (mLongPressTimeoutRunnable == null) { 2744 mLongPressTimeoutRunnable = createLongPressTimeoutRunnable(keyEvent); 2745 } 2746 mHandler.postDelayed(mLongPressTimeoutRunnable, LONG_PRESS_TIMEOUT); 2747 } else { 2748 resetLongPressTracking(); 2749 } 2750 } else { 2751 if (mKeyType == KEY_TYPE_VOLUME) { 2752 if (isFirstLongPressKeyEvent(keyEvent)) { 2753 dispatchVolumeKeyLongPressLocked(mTrackingFirstDownKeyEvent); 2754 } 2755 dispatchVolumeKeyLongPressLocked(keyEvent); 2756 } else if (isFirstLongPressKeyEvent(keyEvent) 2757 && isVoiceKey(keyEvent.getKeyCode())) { 2758 // Default implementation 2759 startVoiceInput(needWakeLock); 2760 resetLongPressTracking(); 2761 } 2762 } 2763 } 2764 createLongPressTimeoutRunnable(KeyEvent keyEvent)2765 private Runnable createLongPressTimeoutRunnable(KeyEvent keyEvent) { 2766 return new Runnable() { 2767 @Override 2768 public void run() { 2769 if (mCustomMediaKeyDispatcher != null) { 2770 mCustomMediaKeyDispatcher.onLongPress(createCanceledKeyEvent(keyEvent)); 2771 } 2772 resetLongPressTracking(); 2773 } 2774 }; 2775 } 2776 resetLongPressTracking()2777 private void resetLongPressTracking() { 2778 mTrackingFirstDownKeyEvent = null; 2779 mIsLongPressing = false; 2780 mLongPressTimeoutRunnable = null; 2781 } 2782 createCanceledKeyEvent(KeyEvent keyEvent)2783 private KeyEvent createCanceledKeyEvent(KeyEvent keyEvent) { 2784 KeyEvent upEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP); 2785 return KeyEvent.changeTimeRepeat(upEvent, System.currentTimeMillis(), 0, 2786 KeyEvent.FLAG_CANCELED); 2787 } 2788 isFirstLongPressKeyEvent(KeyEvent keyEvent)2789 private boolean isFirstLongPressKeyEvent(KeyEvent keyEvent) { 2790 return ((keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) 2791 && keyEvent.getRepeatCount() == 1; 2792 } 2793 isFirstDownKeyEvent(KeyEvent keyEvent)2794 private boolean isFirstDownKeyEvent(KeyEvent keyEvent) { 2795 return keyEvent.getAction() == KeyEvent.ACTION_DOWN 2796 && keyEvent.getRepeatCount() == 0; 2797 } 2798 dispatchDownAndUpKeyEventsLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, String opPackageName, int stream, boolean musicOnly)2799 private void dispatchDownAndUpKeyEventsLocked(String packageName, int pid, int uid, 2800 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, 2801 String opPackageName, int stream, boolean musicOnly) { 2802 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); 2803 if (mKeyType == KEY_TYPE_VOLUME) { 2804 dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, 2805 asSystemService, downEvent, stream, musicOnly); 2806 dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid, 2807 asSystemService, keyEvent, stream, musicOnly); 2808 } else { 2809 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, downEvent, 2810 needWakeLock); 2811 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, 2812 needWakeLock); 2813 } 2814 } 2815 createSingleTapRunnable(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, String opPackageName, int stream, boolean musicOnly, boolean overridden)2816 Runnable createSingleTapRunnable(String packageName, int pid, int uid, 2817 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, 2818 String opPackageName, int stream, boolean musicOnly, boolean overridden) { 2819 return new Runnable() { 2820 @Override 2821 public void run() { 2822 resetMultiTapTrackingLocked(); 2823 if (overridden) { 2824 mCustomMediaKeyDispatcher.onSingleTap(keyEvent); 2825 } else { 2826 dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService, 2827 keyEvent, needWakeLock, opPackageName, stream, musicOnly); 2828 } 2829 } 2830 }; 2831 }; 2832 2833 Runnable createDoubleTapRunnable(String packageName, int pid, int uid, 2834 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock, 2835 String opPackageName, int stream, boolean musicOnly, 2836 boolean singleTapOverridden, boolean doubleTapOverridden) { 2837 return new Runnable() { 2838 @Override 2839 public void run() { 2840 resetMultiTapTrackingLocked(); 2841 if (doubleTapOverridden) { 2842 mCustomMediaKeyDispatcher.onDoubleTap(keyEvent); 2843 } else if (singleTapOverridden) { 2844 mCustomMediaKeyDispatcher.onSingleTap(keyEvent); 2845 mCustomMediaKeyDispatcher.onSingleTap(keyEvent); 2846 } else { 2847 dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService, 2848 keyEvent, needWakeLock, opPackageName, stream, musicOnly); 2849 dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService, 2850 keyEvent, needWakeLock, opPackageName, stream, musicOnly); 2851 } 2852 } 2853 }; 2854 }; 2855 2856 private void onTripleTap(KeyEvent keyEvent) { 2857 resetMultiTapTrackingLocked(); 2858 mCustomMediaKeyDispatcher.onTripleTap(keyEvent); 2859 } 2860 } 2861 } 2862 2863 final class MessageHandler extends Handler { 2864 private static final int MSG_SESSIONS_1_CHANGED = 1; 2865 private static final int MSG_SESSIONS_2_CHANGED = 2; 2866 private final SparseArray<Integer> mIntegerCache = new SparseArray<>(); 2867 2868 @Override 2869 public void handleMessage(Message msg) { 2870 switch (msg.what) { 2871 case MSG_SESSIONS_1_CHANGED: 2872 pushSession1Changed((int) msg.obj); 2873 break; 2874 case MSG_SESSIONS_2_CHANGED: 2875 pushSession2Changed((int) msg.obj); 2876 break; 2877 } 2878 } 2879 2880 public void postSessionsChanged(MediaSessionRecordImpl record) { 2881 // Use object instead of the arguments when posting message to remove pending requests. 2882 Integer userIdInteger = mIntegerCache.get(record.getUserId()); 2883 if (userIdInteger == null) { 2884 userIdInteger = Integer.valueOf(record.getUserId()); 2885 mIntegerCache.put(record.getUserId(), userIdInteger); 2886 } 2887 2888 int msg = (record instanceof MediaSessionRecord) 2889 ? MSG_SESSIONS_1_CHANGED : MSG_SESSIONS_2_CHANGED; 2890 removeMessages(msg, userIdInteger); 2891 obtainMessage(msg, userIdInteger).sendToTarget(); 2892 } 2893 } 2894 } 2895