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