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