• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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