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