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