Home | History | Annotate | Download | only in media
      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 android.Manifest;
     20 import android.annotation.NonNull;
     21 import android.app.Activity;
     22 import android.app.ActivityManager;
     23 import android.app.KeyguardManager;
     24 import android.app.PendingIntent;
     25 import android.app.PendingIntent.CanceledException;
     26 import android.content.ActivityNotFoundException;
     27 import android.content.BroadcastReceiver;
     28 import android.content.ComponentName;
     29 import android.content.ContentResolver;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.pm.PackageManager;
     33 import android.content.pm.UserInfo;
     34 import android.database.ContentObserver;
     35 import android.media.AudioManager;
     36 import android.media.AudioManagerInternal;
     37 import android.media.AudioSystem;
     38 import android.media.IAudioService;
     39 import android.media.IRemoteVolumeController;
     40 import android.media.session.IActiveSessionsListener;
     41 import android.media.session.ICallback;
     42 import android.media.session.IOnMediaKeyListener;
     43 import android.media.session.IOnVolumeKeyLongPressListener;
     44 import android.media.session.ISession;
     45 import android.media.session.ISessionCallback;
     46 import android.media.session.ISessionManager;
     47 import android.media.session.MediaSession;
     48 import android.media.session.MediaSessionManager;
     49 import android.net.Uri;
     50 import android.os.Binder;
     51 import android.os.Bundle;
     52 import android.os.Handler;
     53 import android.os.IBinder;
     54 import android.os.Message;
     55 import android.os.PowerManager;
     56 import android.os.Process;
     57 import android.os.RemoteException;
     58 import android.os.ResultReceiver;
     59 import android.os.ServiceManager;
     60 import android.os.SystemProperties;
     61 import android.os.UserHandle;
     62 import android.os.UserManager;
     63 import android.provider.Settings;
     64 import android.speech.RecognizerIntent;
     65 import android.text.TextUtils;
     66 import android.util.IntArray;
     67 import android.util.Log;
     68 import android.util.Slog;
     69 import android.util.SparseArray;
     70 import android.util.SparseIntArray;
     71 import android.view.KeyEvent;
     72 import android.view.ViewConfiguration;
     73 
     74 import com.android.internal.util.DumpUtils;
     75 import com.android.server.LocalServices;
     76 import com.android.server.SystemService;
     77 import com.android.server.Watchdog;
     78 import com.android.server.Watchdog.Monitor;
     79 
     80 import java.io.FileDescriptor;
     81 import java.io.PrintWriter;
     82 import java.util.ArrayList;
     83 import java.util.Arrays;
     84 import java.util.List;
     85 
     86 /**
     87  * System implementation of MediaSessionManager
     88  */
     89 public class MediaSessionService extends SystemService implements Monitor {
     90     private static final String TAG = "MediaSessionService";
     91     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     92     // Leave log for key event always.
     93     private static final boolean DEBUG_KEY_EVENT = true;
     94 
     95     private static final int WAKELOCK_TIMEOUT = 5000;
     96     private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
     97 
     98     private final SessionManagerImpl mSessionManagerImpl;
     99 
    100     // Keeps the full user id for each user.
    101     private final SparseIntArray mFullUserIds = new SparseIntArray();
    102     private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
    103     private final ArrayList<SessionsListenerRecord> mSessionsListeners
    104             = new ArrayList<SessionsListenerRecord>();
    105     private final Object mLock = new Object();
    106     private final MessageHandler mHandler = new MessageHandler();
    107     private final PowerManager.WakeLock mMediaEventWakeLock;
    108     private final int mLongPressTimeout;
    109 
    110     private KeyguardManager mKeyguardManager;
    111     private IAudioService mAudioService;
    112     private AudioManagerInternal mAudioManagerInternal;
    113     private ContentResolver mContentResolver;
    114     private SettingsObserver mSettingsObserver;
    115 
    116     // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
    117     // It's always not null after the MediaSessionService is started.
    118     private FullUserRecord mCurrentFullUserRecord;
    119     private MediaSessionRecord mGlobalPrioritySession;
    120     private AudioPlaybackMonitor mAudioPlaybackMonitor;
    121 
    122     // Used to notify system UI when remote volume was changed. TODO find a
    123     // better way to handle this.
    124     private IRemoteVolumeController mRvc;
    125 
    126     public MediaSessionService(Context context) {
    127         super(context);
    128         mSessionManagerImpl = new SessionManagerImpl();
    129         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    130         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
    131         mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
    132     }
    133 
    134     @Override
    135     public void onStart() {
    136         publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
    137         Watchdog.getInstance().addMonitor(this);
    138         mKeyguardManager =
    139                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
    140         mAudioService = getAudioService();
    141         mAudioPlaybackMonitor = new AudioPlaybackMonitor(getContext(), mAudioService,
    142                 new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() {
    143                     @Override
    144                     public void onAudioPlaybackStarted(int uid) {
    145                         synchronized (mLock) {
    146                             FullUserRecord user =
    147                                     getFullUserRecordLocked(UserHandle.getUserId(uid));
    148                             if (user != null) {
    149                                 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
    150                             }
    151                         }
    152                     }
    153                 });
    154         mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
    155         mContentResolver = getContext().getContentResolver();
    156         mSettingsObserver = new SettingsObserver();
    157         mSettingsObserver.observe();
    158 
    159         updateUser();
    160     }
    161 
    162     private IAudioService getAudioService() {
    163         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
    164         return IAudioService.Stub.asInterface(b);
    165     }
    166 
    167     private boolean isGlobalPriorityActiveLocked() {
    168         return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
    169     }
    170 
    171     public void updateSession(MediaSessionRecord record) {
    172         synchronized (mLock) {
    173             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    174             if (user == null) {
    175                 Log.w(TAG, "Unknown session updated. Ignoring.");
    176                 return;
    177             }
    178             if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
    179                 if (mGlobalPrioritySession != record) {
    180                     Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
    181                             + " to " + record);
    182                     mGlobalPrioritySession = record;
    183                     if (user != null && user.mPriorityStack.contains(record)) {
    184                         // Handle the global priority session separately.
    185                         // Otherwise, it will be the media button session even after it becomes
    186                         // inactive because it has been the lastly played media app.
    187                         user.mPriorityStack.removeSession(record);
    188                     }
    189                 }
    190                 if (DEBUG_KEY_EVENT) {
    191                     Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
    192                 }
    193                 user.pushAddressedPlayerChangedLocked();
    194             } else {
    195                 if (!user.mPriorityStack.contains(record)) {
    196                     Log.w(TAG, "Unknown session updated. Ignoring.");
    197                     return;
    198                 }
    199                 user.mPriorityStack.onSessionStateChange(record);
    200             }
    201             mHandler.postSessionsChanged(record.getUserId());
    202         }
    203     }
    204 
    205     private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
    206         List<MediaSessionRecord> records;
    207         if (userId == UserHandle.USER_ALL) {
    208             records = new ArrayList<>();
    209             int size = mUserRecords.size();
    210             for (int i = 0; i < size; i++) {
    211                 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
    212             }
    213         } else {
    214             FullUserRecord user = getFullUserRecordLocked(userId);
    215             if (user == null) {
    216                 Log.w(TAG, "getSessions failed. Unknown user " + userId);
    217                 return new ArrayList<>();
    218             }
    219             records = user.mPriorityStack.getActiveSessions(userId);
    220         }
    221 
    222         // Return global priority session at the first whenever it's asked.
    223         if (isGlobalPriorityActiveLocked()
    224                 && (userId == UserHandle.USER_ALL
    225                     || userId == mGlobalPrioritySession.getUserId())) {
    226             records.add(0, mGlobalPrioritySession);
    227         }
    228         return records;
    229     }
    230 
    231     /**
    232      * Tells the system UI that volume has changed on an active remote session.
    233      */
    234     public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
    235         if (mRvc == null || !session.isActive()) {
    236             return;
    237         }
    238         try {
    239             mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
    240         } catch (Exception e) {
    241             Log.wtf(TAG, "Error sending volume change to system UI.", e);
    242         }
    243     }
    244 
    245     public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
    246         synchronized (mLock) {
    247             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    248             if (user == null || !user.mPriorityStack.contains(record)) {
    249                 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
    250                 return;
    251             }
    252             user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
    253         }
    254     }
    255 
    256     public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
    257         synchronized (mLock) {
    258             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    259             if (user == null || !user.mPriorityStack.contains(record)) {
    260                 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
    261                 return;
    262             }
    263             pushRemoteVolumeUpdateLocked(record.getUserId());
    264         }
    265     }
    266 
    267     @Override
    268     public void onStartUser(int userId) {
    269         if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
    270         updateUser();
    271     }
    272 
    273     @Override
    274     public void onSwitchUser(int userId) {
    275         if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
    276         updateUser();
    277     }
    278 
    279     @Override
    280     public void onStopUser(int userId) {
    281         if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
    282         synchronized (mLock) {
    283             FullUserRecord user = getFullUserRecordLocked(userId);
    284             if (user != null) {
    285                 if (user.mFullUserId == userId) {
    286                     user.destroySessionsForUserLocked(UserHandle.USER_ALL);
    287                     mUserRecords.remove(userId);
    288                 } else {
    289                     user.destroySessionsForUserLocked(userId);
    290                 }
    291             }
    292             updateUser();
    293         }
    294     }
    295 
    296     @Override
    297     public void monitor() {
    298         synchronized (mLock) {
    299             // Check for deadlock
    300         }
    301     }
    302 
    303     protected void enforcePhoneStatePermission(int pid, int uid) {
    304         if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
    305                 != PackageManager.PERMISSION_GRANTED) {
    306             throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
    307         }
    308     }
    309 
    310     void sessionDied(MediaSessionRecord session) {
    311         synchronized (mLock) {
    312             destroySessionLocked(session);
    313         }
    314     }
    315 
    316     void destroySession(MediaSessionRecord session) {
    317         synchronized (mLock) {
    318             destroySessionLocked(session);
    319         }
    320     }
    321 
    322     private void updateUser() {
    323         synchronized (mLock) {
    324             UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
    325             mFullUserIds.clear();
    326             List<UserInfo> allUsers = manager.getUsers();
    327             if (allUsers != null) {
    328                 for (UserInfo userInfo : allUsers) {
    329                     if (userInfo.isManagedProfile()) {
    330                         mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
    331                     } else {
    332                         mFullUserIds.put(userInfo.id, userInfo.id);
    333                         if (mUserRecords.get(userInfo.id) == null) {
    334                             mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
    335                         }
    336                     }
    337                 }
    338             }
    339             // Ensure that the current full user exists.
    340             int currentFullUserId = ActivityManager.getCurrentUser();
    341             mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
    342             if (mCurrentFullUserRecord == null) {
    343                 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
    344                 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
    345                 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
    346             }
    347             mFullUserIds.put(currentFullUserId, currentFullUserId);
    348         }
    349     }
    350 
    351     private void updateActiveSessionListeners() {
    352         synchronized (mLock) {
    353             for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
    354                 SessionsListenerRecord listener = mSessionsListeners.get(i);
    355                 try {
    356                     enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
    357                             listener.mUserId);
    358                 } catch (SecurityException e) {
    359                     Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
    360                             + " is no longer authorized. Disconnecting.");
    361                     mSessionsListeners.remove(i);
    362                     try {
    363                         listener.mListener
    364                                 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
    365                     } catch (Exception e1) {
    366                         // ignore
    367                     }
    368                 }
    369             }
    370         }
    371     }
    372 
    373     /*
    374      * When a session is removed several things need to happen.
    375      * 1. We need to remove it from the relevant user.
    376      * 2. We need to remove it from the priority stack.
    377      * 3. We need to remove it from all sessions.
    378      * 4. If this is the system priority session we need to clear it.
    379      * 5. We need to unlink to death from the cb binder
    380      * 6. We need to tell the session to do any final cleanup (onDestroy)
    381      */
    382     private void destroySessionLocked(MediaSessionRecord session) {
    383         if (DEBUG) {
    384             Log.d(TAG, "Destroying " + session);
    385         }
    386         FullUserRecord user = getFullUserRecordLocked(session.getUserId());
    387         if (mGlobalPrioritySession == session) {
    388             mGlobalPrioritySession = null;
    389             if (session.isActive() && user != null) {
    390                 user.pushAddressedPlayerChangedLocked();
    391             }
    392         } else {
    393             if (user != null) {
    394                 user.mPriorityStack.removeSession(session);
    395             }
    396         }
    397 
    398         try {
    399             session.getCallback().asBinder().unlinkToDeath(session, 0);
    400         } catch (Exception e) {
    401             // ignore exceptions while destroying a session.
    402         }
    403         session.onDestroy();
    404         mHandler.postSessionsChanged(session.getUserId());
    405     }
    406 
    407     private void enforcePackageName(String packageName, int uid) {
    408         if (TextUtils.isEmpty(packageName)) {
    409             throw new IllegalArgumentException("packageName may not be empty");
    410         }
    411         String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
    412         final int packageCount = packages.length;
    413         for (int i = 0; i < packageCount; i++) {
    414             if (packageName.equals(packages[i])) {
    415                 return;
    416             }
    417         }
    418         throw new IllegalArgumentException("packageName is not owned by the calling process");
    419     }
    420 
    421     /**
    422      * Checks a caller's authorization to register an IRemoteControlDisplay.
    423      * Authorization is granted if one of the following is true:
    424      * <ul>
    425      * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
    426      * permission</li>
    427      * <li>the caller's listener is one of the enabled notification listeners
    428      * for the caller's user</li>
    429      * </ul>
    430      */
    431     private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
    432             int resolvedUserId) {
    433         if (isCurrentVolumeController(uid, pid)) return;
    434         if (getContext()
    435                 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
    436                     != PackageManager.PERMISSION_GRANTED
    437                 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
    438                         resolvedUserId)) {
    439             throw new SecurityException("Missing permission to control media.");
    440         }
    441     }
    442 
    443     private boolean isCurrentVolumeController(int uid, int pid) {
    444         return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
    445                 pid, uid) == PackageManager.PERMISSION_GRANTED;
    446     }
    447 
    448     private void enforceSystemUiPermission(String action, int pid, int uid) {
    449         if (!isCurrentVolumeController(uid, pid)) {
    450             throw new SecurityException("Only system ui may " + action);
    451         }
    452     }
    453 
    454     /**
    455      * This checks if the component is an enabled notification listener for the
    456      * specified user. Enabled components may only operate on behalf of the user
    457      * they're running as.
    458      *
    459      * @param compName The component that is enabled.
    460      * @param userId The user id of the caller.
    461      * @param forUserId The user id they're making the request on behalf of.
    462      * @return True if the component is enabled, false otherwise
    463      */
    464     private boolean isEnabledNotificationListener(ComponentName compName, int userId,
    465             int forUserId) {
    466         if (userId != forUserId) {
    467             // You may not access another user's content as an enabled listener.
    468             return false;
    469         }
    470         if (DEBUG) {
    471             Log.d(TAG, "Checking if enabled notification listener " + compName);
    472         }
    473         if (compName != null) {
    474             final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
    475                     Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
    476                     userId);
    477             if (enabledNotifListeners != null) {
    478                 final String[] components = enabledNotifListeners.split(":");
    479                 for (int i = 0; i < components.length; i++) {
    480                     final ComponentName component =
    481                             ComponentName.unflattenFromString(components[i]);
    482                     if (component != null) {
    483                         if (compName.equals(component)) {
    484                             if (DEBUG) {
    485                                 Log.d(TAG, "ok to get sessions. " + component +
    486                                         " is authorized notification listener");
    487                             }
    488                             return true;
    489                         }
    490                     }
    491                 }
    492             }
    493             if (DEBUG) {
    494                 Log.d(TAG, "not ok to get sessions. " + compName +
    495                         " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
    496             }
    497         }
    498         return false;
    499     }
    500 
    501     private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
    502             String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
    503         synchronized (mLock) {
    504             return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
    505         }
    506     }
    507 
    508     /*
    509      * When a session is created the following things need to happen.
    510      * 1. Its callback binder needs a link to death
    511      * 2. It needs to be added to all sessions.
    512      * 3. It needs to be added to the priority stack.
    513      * 4. It needs to be added to the relevant user record.
    514      */
    515     private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
    516             String callerPackageName, ISessionCallback cb, String tag) {
    517         FullUserRecord user = getFullUserRecordLocked(userId);
    518         if (user == null) {
    519             Log.wtf(TAG, "Request from invalid user: " +  userId);
    520             throw new RuntimeException("Session request from invalid user.");
    521         }
    522 
    523         final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
    524                 callerPackageName, cb, tag, this, mHandler.getLooper());
    525         try {
    526             cb.asBinder().linkToDeath(session, 0);
    527         } catch (RemoteException e) {
    528             throw new RuntimeException("Media Session owner died prematurely.", e);
    529         }
    530 
    531         user.mPriorityStack.addSession(session);
    532         mHandler.postSessionsChanged(userId);
    533 
    534         if (DEBUG) {
    535             Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
    536         }
    537         return session;
    538     }
    539 
    540     private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
    541         for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
    542             if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
    543                 return i;
    544             }
    545         }
    546         return -1;
    547     }
    548 
    549     private void pushSessionsChanged(int userId) {
    550         synchronized (mLock) {
    551             FullUserRecord user = getFullUserRecordLocked(userId);
    552             if (user == null) {
    553                 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
    554                 return;
    555             }
    556             List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
    557             int size = records.size();
    558             ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
    559             for (int i = 0; i < size; i++) {
    560                 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
    561             }
    562             pushRemoteVolumeUpdateLocked(userId);
    563             for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
    564                 SessionsListenerRecord record = mSessionsListeners.get(i);
    565                 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
    566                     try {
    567                         record.mListener.onActiveSessionsChanged(tokens);
    568                     } catch (RemoteException e) {
    569                         Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
    570                                 e);
    571                         mSessionsListeners.remove(i);
    572                     }
    573                 }
    574             }
    575         }
    576     }
    577 
    578     private void pushRemoteVolumeUpdateLocked(int userId) {
    579         if (mRvc != null) {
    580             try {
    581                 FullUserRecord user = getFullUserRecordLocked(userId);
    582                 if (user == null) {
    583                     Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
    584                     return;
    585                 }
    586                 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
    587                 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
    588             } catch (RemoteException e) {
    589                 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
    590             }
    591         }
    592     }
    593 
    594     /**
    595      * Called when the media button receiver for the {@param record} is changed.
    596      *
    597      * @param record the media session whose media button receiver is updated.
    598      */
    599     public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
    600         synchronized (mLock) {
    601             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    602             MediaSessionRecord mediaButtonSession =
    603                     user.mPriorityStack.getMediaButtonSession();
    604             if (record == mediaButtonSession) {
    605                 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
    606             }
    607         }
    608     }
    609 
    610     private String getCallingPackageName(int uid) {
    611         String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
    612         if (packages != null && packages.length > 0) {
    613             return packages[0];
    614         }
    615         return "";
    616     }
    617 
    618     private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
    619         try {
    620             mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
    621         } catch (RemoteException e) {
    622             Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
    623         }
    624     }
    625 
    626     private FullUserRecord getFullUserRecordLocked(int userId) {
    627         int fullUserId = mFullUserIds.get(userId, -1);
    628         if (fullUserId < 0) {
    629             return null;
    630         }
    631         return mUserRecords.get(fullUserId);
    632     }
    633 
    634     /**
    635      * Information about a full user and its corresponding managed profiles.
    636      *
    637      * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
    638      * them when he/she presses a media/volume button. So keeping media sessions for them in one
    639      * place makes more sense and increases the readability.</p>
    640      * <p>The contents of this object is guarded by {@link #mLock}.
    641      */
    642     final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
    643         private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
    644         private final int mFullUserId;
    645         private final MediaSessionStack mPriorityStack;
    646         private PendingIntent mLastMediaButtonReceiver;
    647         private ComponentName mRestoredMediaButtonReceiver;
    648         private int mRestoredMediaButtonReceiverUserId;
    649 
    650         private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
    651         private int mOnVolumeKeyLongPressListenerUid;
    652         private KeyEvent mInitialDownVolumeKeyEvent;
    653         private int mInitialDownVolumeStream;
    654         private boolean mInitialDownMusicOnly;
    655 
    656         private IOnMediaKeyListener mOnMediaKeyListener;
    657         private int mOnMediaKeyListenerUid;
    658         private ICallback mCallback;
    659 
    660         public FullUserRecord(int fullUserId) {
    661             mFullUserId = fullUserId;
    662             mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this);
    663             // Restore the remembered media button receiver before the boot.
    664             String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
    665                     Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
    666             if (mediaButtonReceiver == null) {
    667                 return;
    668             }
    669             String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
    670             if (tokens == null || tokens.length != 2) {
    671                 return;
    672             }
    673             mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
    674             mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
    675         }
    676 
    677         public void destroySessionsForUserLocked(int userId) {
    678             List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
    679             for (MediaSessionRecord session : sessions) {
    680                 MediaSessionService.this.destroySessionLocked(session);
    681             }
    682         }
    683 
    684         public void dumpLocked(PrintWriter pw, String prefix) {
    685             pw.print(prefix + "Record for full_user=" + mFullUserId);
    686             // Dump managed profile user ids associated with this user.
    687             int size = mFullUserIds.size();
    688             for (int i = 0; i < size; i++) {
    689                 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
    690                         && mFullUserIds.valueAt(i) == mFullUserId) {
    691                     pw.print(", profile_user=" + mFullUserIds.keyAt(i));
    692                 }
    693             }
    694             pw.println();
    695             String indent = prefix + "  ";
    696             pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
    697             pw.println(indent + "Volume key long-press listener package: " +
    698                     getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
    699             pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
    700             pw.println(indent + "Media key listener package: " +
    701                     getCallingPackageName(mOnMediaKeyListenerUid));
    702             pw.println(indent + "Callback: " + mCallback);
    703             pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
    704             pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
    705             mPriorityStack.dump(pw, indent);
    706         }
    707 
    708         @Override
    709         public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
    710                 MediaSessionRecord newMediaButtonSession) {
    711             if (DEBUG_KEY_EVENT) {
    712                 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
    713             }
    714             synchronized (mLock) {
    715                 if (oldMediaButtonSession != null) {
    716                     mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
    717                 }
    718                 if (newMediaButtonSession != null) {
    719                     rememberMediaButtonReceiverLocked(newMediaButtonSession);
    720                     mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
    721                 }
    722                 pushAddressedPlayerChangedLocked();
    723             }
    724         }
    725 
    726         // Remember media button receiver and keep it in the persistent storage.
    727         public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
    728             PendingIntent receiver = record.getMediaButtonReceiver();
    729             mLastMediaButtonReceiver = receiver;
    730             mRestoredMediaButtonReceiver = null;
    731             String componentName = "";
    732             if (receiver != null) {
    733                 ComponentName component = receiver.getIntent().getComponent();
    734                 if (component != null
    735                         && record.getPackageName().equals(component.getPackageName())) {
    736                     componentName = component.flattenToString();
    737                 }
    738             }
    739             Settings.Secure.putStringForUser(mContentResolver,
    740                     Settings.System.MEDIA_BUTTON_RECEIVER,
    741                     componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
    742                     mFullUserId);
    743         }
    744 
    745         private void pushAddressedPlayerChangedLocked() {
    746             if (mCallback == null) {
    747                 return;
    748             }
    749             try {
    750                 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
    751                 if (mediaButtonSession != null) {
    752                     mCallback.onAddressedPlayerChangedToMediaSession(
    753                             new MediaSession.Token(mediaButtonSession.getControllerBinder()));
    754                 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
    755                     mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
    756                             mCurrentFullUserRecord.mLastMediaButtonReceiver
    757                                     .getIntent().getComponent());
    758                 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
    759                     mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
    760                             mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
    761                 }
    762             } catch (RemoteException e) {
    763                 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
    764             }
    765         }
    766 
    767         private MediaSessionRecord getMediaButtonSessionLocked() {
    768             return isGlobalPriorityActiveLocked()
    769                     ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
    770         }
    771     }
    772 
    773     final class SessionsListenerRecord implements IBinder.DeathRecipient {
    774         private final IActiveSessionsListener mListener;
    775         private final ComponentName mComponentName;
    776         private final int mUserId;
    777         private final int mPid;
    778         private final int mUid;
    779 
    780         public SessionsListenerRecord(IActiveSessionsListener listener,
    781                 ComponentName componentName,
    782                 int userId, int pid, int uid) {
    783             mListener = listener;
    784             mComponentName = componentName;
    785             mUserId = userId;
    786             mPid = pid;
    787             mUid = uid;
    788         }
    789 
    790         @Override
    791         public void binderDied() {
    792             synchronized (mLock) {
    793                 mSessionsListeners.remove(this);
    794             }
    795         }
    796     }
    797 
    798     final class SettingsObserver extends ContentObserver {
    799         private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
    800                 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
    801 
    802         private SettingsObserver() {
    803             super(null);
    804         }
    805 
    806         private void observe() {
    807             mContentResolver.registerContentObserver(mSecureSettingsUri,
    808                     false, this, UserHandle.USER_ALL);
    809         }
    810 
    811         @Override
    812         public void onChange(boolean selfChange, Uri uri) {
    813             updateActiveSessionListeners();
    814         }
    815     }
    816 
    817     class SessionManagerImpl extends ISessionManager.Stub {
    818         private static final String EXTRA_WAKELOCK_ACQUIRED =
    819                 "android.media.AudioService.WAKELOCK_ACQUIRED";
    820         private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
    821 
    822         private boolean mVoiceButtonDown = false;
    823         private boolean mVoiceButtonHandled = false;
    824 
    825         @Override
    826         public ISession createSession(String packageName, ISessionCallback cb, String tag,
    827                 int userId) throws RemoteException {
    828             final int pid = Binder.getCallingPid();
    829             final int uid = Binder.getCallingUid();
    830             final long token = Binder.clearCallingIdentity();
    831             try {
    832                 enforcePackageName(packageName, uid);
    833                 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
    834                         false /* allowAll */, true /* requireFull */, "createSession", packageName);
    835                 if (cb == null) {
    836                     throw new IllegalArgumentException("Controller callback cannot be null");
    837                 }
    838                 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
    839                         .getSessionBinder();
    840             } finally {
    841                 Binder.restoreCallingIdentity(token);
    842             }
    843         }
    844 
    845         @Override
    846         public List<IBinder> getSessions(ComponentName componentName, int userId) {
    847             final int pid = Binder.getCallingPid();
    848             final int uid = Binder.getCallingUid();
    849             final long token = Binder.clearCallingIdentity();
    850 
    851             try {
    852                 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
    853                 ArrayList<IBinder> binders = new ArrayList<IBinder>();
    854                 synchronized (mLock) {
    855                     List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
    856                     for (MediaSessionRecord record : records) {
    857                         binders.add(record.getControllerBinder().asBinder());
    858                     }
    859                 }
    860                 return binders;
    861             } finally {
    862                 Binder.restoreCallingIdentity(token);
    863             }
    864         }
    865 
    866         @Override
    867         public void addSessionsListener(IActiveSessionsListener listener,
    868                 ComponentName componentName, int userId) throws RemoteException {
    869             final int pid = Binder.getCallingPid();
    870             final int uid = Binder.getCallingUid();
    871             final long token = Binder.clearCallingIdentity();
    872 
    873             try {
    874                 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
    875                 synchronized (mLock) {
    876                     int index = findIndexOfSessionsListenerLocked(listener);
    877                     if (index != -1) {
    878                         Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
    879                         return;
    880                     }
    881                     SessionsListenerRecord record = new SessionsListenerRecord(listener,
    882                             componentName, resolvedUserId, pid, uid);
    883                     try {
    884                         listener.asBinder().linkToDeath(record, 0);
    885                     } catch (RemoteException e) {
    886                         Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
    887                         return;
    888                     }
    889                     mSessionsListeners.add(record);
    890                 }
    891             } finally {
    892                 Binder.restoreCallingIdentity(token);
    893             }
    894         }
    895 
    896         @Override
    897         public void removeSessionsListener(IActiveSessionsListener listener)
    898                 throws RemoteException {
    899             synchronized (mLock) {
    900                 int index = findIndexOfSessionsListenerLocked(listener);
    901                 if (index != -1) {
    902                     SessionsListenerRecord record = mSessionsListeners.remove(index);
    903                     try {
    904                         record.mListener.asBinder().unlinkToDeath(record, 0);
    905                     } catch (Exception e) {
    906                         // ignore exceptions, the record is being removed
    907                     }
    908                 }
    909             }
    910         }
    911 
    912         /**
    913          * Handles the dispatching of the media button events to one of the
    914          * registered listeners, or if there was none, broadcast an
    915          * ACTION_MEDIA_BUTTON intent to the rest of the system.
    916          *
    917          * @param keyEvent a non-null KeyEvent whose key code is one of the
    918          *            supported media buttons
    919          * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
    920          *            while this key event is dispatched.
    921          */
    922         @Override
    923         public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
    924             if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
    925                 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
    926                 return;
    927             }
    928 
    929             final int pid = Binder.getCallingPid();
    930             final int uid = Binder.getCallingUid();
    931             final long token = Binder.clearCallingIdentity();
    932             try {
    933                 if (DEBUG) {
    934                     Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
    935                             + keyEvent);
    936                 }
    937                 if (!isUserSetupComplete()) {
    938                     // Global media key handling can have the side-effect of starting new
    939                     // activities which is undesirable while setup is in progress.
    940                     Slog.i(TAG, "Not dispatching media key event because user "
    941                             + "setup is in progress.");
    942                     return;
    943                 }
    944 
    945                 synchronized (mLock) {
    946                     boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
    947                     if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
    948                         // Prevent dispatching key event through reflection while the global
    949                         // priority session is active.
    950                         Slog.i(TAG, "Only the system can dispatch media key event "
    951                                 + "to the global priority session.");
    952                         return;
    953                     }
    954                     if (!isGlobalPriorityActive) {
    955                         if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
    956                             if (DEBUG_KEY_EVENT) {
    957                                 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
    958                             }
    959                             try {
    960                                 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
    961                                         new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
    962                                 return;
    963                             } catch (RemoteException e) {
    964                                 Log.w(TAG, "Failed to send " + keyEvent
    965                                         + " to the media key listener");
    966                             }
    967                         }
    968                     }
    969                     if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
    970                         handleVoiceKeyEventLocked(keyEvent, needWakeLock);
    971                     } else {
    972                         dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
    973                     }
    974                 }
    975             } finally {
    976                 Binder.restoreCallingIdentity(token);
    977             }
    978         }
    979 
    980         @Override
    981         public void setCallback(ICallback callback) {
    982             final int pid = Binder.getCallingPid();
    983             final int uid = Binder.getCallingUid();
    984             final long token = Binder.clearCallingIdentity();
    985             try {
    986                 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
    987                     throw new SecurityException("Only Bluetooth service processes can set"
    988                             + " Callback");
    989                 }
    990                 synchronized (mLock) {
    991                     int userId = UserHandle.getUserId(uid);
    992                     FullUserRecord user = getFullUserRecordLocked(userId);
    993                     if (user == null || user.mFullUserId != userId) {
    994                         Log.w(TAG, "Only the full user can set the callback"
    995                                 + ", userId=" + userId);
    996                         return;
    997                     }
    998                     user.mCallback = callback;
    999                     Log.d(TAG, "The callback " + user.mCallback
   1000                             + " is set by " + getCallingPackageName(uid));
   1001                     if (user.mCallback == null) {
   1002                         return;
   1003                     }
   1004                     try {
   1005                         user.mCallback.asBinder().linkToDeath(
   1006                                 new IBinder.DeathRecipient() {
   1007                                     @Override
   1008                                     public void binderDied() {
   1009                                         synchronized (mLock) {
   1010                                             user.mCallback = null;
   1011                                         }
   1012                                     }
   1013                                 }, 0);
   1014                         user.pushAddressedPlayerChangedLocked();
   1015                     } catch (RemoteException e) {
   1016                         Log.w(TAG, "Failed to set callback", e);
   1017                         user.mCallback = null;
   1018                     }
   1019                 }
   1020             } finally {
   1021                 Binder.restoreCallingIdentity(token);
   1022             }
   1023         }
   1024 
   1025         @Override
   1026         public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
   1027             final int pid = Binder.getCallingPid();
   1028             final int uid = Binder.getCallingUid();
   1029             final long token = Binder.clearCallingIdentity();
   1030             try {
   1031                 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
   1032                 if (getContext().checkPermission(
   1033                         android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
   1034                             != PackageManager.PERMISSION_GRANTED) {
   1035                     throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
   1036                             " permission.");
   1037                 }
   1038 
   1039                 synchronized (mLock) {
   1040                     int userId = UserHandle.getUserId(uid);
   1041                     FullUserRecord user = getFullUserRecordLocked(userId);
   1042                     if (user == null || user.mFullUserId != userId) {
   1043                         Log.w(TAG, "Only the full user can set the volume key long-press listener"
   1044                                 + ", userId=" + userId);
   1045                         return;
   1046                     }
   1047                     if (user.mOnVolumeKeyLongPressListener != null &&
   1048                             user.mOnVolumeKeyLongPressListenerUid != uid) {
   1049                         Log.w(TAG, "The volume key long-press listener cannot be reset"
   1050                                 + " by another app , mOnVolumeKeyLongPressListener="
   1051                                 + user.mOnVolumeKeyLongPressListenerUid
   1052                                 + ", uid=" + uid);
   1053                         return;
   1054                     }
   1055 
   1056                     user.mOnVolumeKeyLongPressListener = listener;
   1057                     user.mOnVolumeKeyLongPressListenerUid = uid;
   1058 
   1059                     Log.d(TAG, "The volume key long-press listener "
   1060                             + listener + " is set by " + getCallingPackageName(uid));
   1061 
   1062                     if (user.mOnVolumeKeyLongPressListener != null) {
   1063                         try {
   1064                             user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
   1065                                     new IBinder.DeathRecipient() {
   1066                                         @Override
   1067                                         public void binderDied() {
   1068                                             synchronized (mLock) {
   1069                                                 user.mOnVolumeKeyLongPressListener = null;
   1070                                             }
   1071                                         }
   1072                                     }, 0);
   1073                         } catch (RemoteException e) {
   1074                             Log.w(TAG, "Failed to set death recipient "
   1075                                     + user.mOnVolumeKeyLongPressListener);
   1076                             user.mOnVolumeKeyLongPressListener = null;
   1077                         }
   1078                     }
   1079                 }
   1080             } finally {
   1081                 Binder.restoreCallingIdentity(token);
   1082             }
   1083         }
   1084 
   1085         @Override
   1086         public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
   1087             final int pid = Binder.getCallingPid();
   1088             final int uid = Binder.getCallingUid();
   1089             final long token = Binder.clearCallingIdentity();
   1090             try {
   1091                 // Enforce SET_MEDIA_KEY_LISTENER permission.
   1092                 if (getContext().checkPermission(
   1093                         android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
   1094                             != PackageManager.PERMISSION_GRANTED) {
   1095                     throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
   1096                             " permission.");
   1097                 }
   1098 
   1099                 synchronized (mLock) {
   1100                     int userId = UserHandle.getUserId(uid);
   1101                     FullUserRecord user = getFullUserRecordLocked(userId);
   1102                     if (user == null || user.mFullUserId != userId) {
   1103                         Log.w(TAG, "Only the full user can set the media key listener"
   1104                                 + ", userId=" + userId);
   1105                         return;
   1106                     }
   1107                     if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
   1108                         Log.w(TAG, "The media key listener cannot be reset by another app. "
   1109                                 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
   1110                                 + ", uid=" + uid);
   1111                         return;
   1112                     }
   1113 
   1114                     user.mOnMediaKeyListener = listener;
   1115                     user.mOnMediaKeyListenerUid = uid;
   1116 
   1117                     Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
   1118                             + " is set by " + getCallingPackageName(uid));
   1119 
   1120                     if (user.mOnMediaKeyListener != null) {
   1121                         try {
   1122                             user.mOnMediaKeyListener.asBinder().linkToDeath(
   1123                                     new IBinder.DeathRecipient() {
   1124                                         @Override
   1125                                         public void binderDied() {
   1126                                             synchronized (mLock) {
   1127                                                 user.mOnMediaKeyListener = null;
   1128                                             }
   1129                                         }
   1130                                     }, 0);
   1131                         } catch (RemoteException e) {
   1132                             Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
   1133                             user.mOnMediaKeyListener = null;
   1134                         }
   1135                     }
   1136                 }
   1137             } finally {
   1138                 Binder.restoreCallingIdentity(token);
   1139             }
   1140         }
   1141 
   1142         /**
   1143          * Handles the dispatching of the volume button events to one of the
   1144          * registered listeners. If there's a volume key long-press listener and
   1145          * there's no active global priority session, long-pressess will be sent to the
   1146          * long-press listener instead of adjusting volume.
   1147          *
   1148          * @param keyEvent a non-null KeyEvent whose key code is one of the
   1149          *            {@link KeyEvent#KEYCODE_VOLUME_UP},
   1150          *            {@link KeyEvent#KEYCODE_VOLUME_DOWN},
   1151          *            or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
   1152          * @param stream stream type to adjust volume.
   1153          * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
   1154          */
   1155         @Override
   1156         public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
   1157             if (keyEvent == null ||
   1158                     (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
   1159                              && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
   1160                              && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
   1161                 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
   1162                 return;
   1163             }
   1164 
   1165             final int pid = Binder.getCallingPid();
   1166             final int uid = Binder.getCallingUid();
   1167             final long token = Binder.clearCallingIdentity();
   1168 
   1169             if (DEBUG_KEY_EVENT) {
   1170                 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
   1171                         + keyEvent);
   1172             }
   1173 
   1174             try {
   1175                 synchronized (mLock) {
   1176                     if (isGlobalPriorityActiveLocked()
   1177                             || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
   1178                         dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
   1179                     } else {
   1180                         // TODO: Consider the case when both volume up and down keys are pressed
   1181                         //       at the same time.
   1182                         if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
   1183                             if (keyEvent.getRepeatCount() == 0) {
   1184                                 // Keeps the copy of the KeyEvent because it can be reused.
   1185                                 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
   1186                                         KeyEvent.obtain(keyEvent);
   1187                                 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
   1188                                 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
   1189                                 mHandler.sendMessageDelayed(
   1190                                         mHandler.obtainMessage(
   1191                                                 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
   1192                                                 mCurrentFullUserRecord.mFullUserId, 0),
   1193                                         mLongPressTimeout);
   1194                             }
   1195                             if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
   1196                                 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
   1197                                 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
   1198                                     dispatchVolumeKeyLongPressLocked(
   1199                                             mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
   1200                                     // Mark that the key is already handled.
   1201                                     mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
   1202                                 }
   1203                                 dispatchVolumeKeyLongPressLocked(keyEvent);
   1204                             }
   1205                         } else { // if up
   1206                             mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
   1207                             if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
   1208                                     && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
   1209                                             .getDownTime() == keyEvent.getDownTime()) {
   1210                                 // Short-press. Should change volume.
   1211                                 dispatchVolumeKeyEventLocked(
   1212                                         mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
   1213                                         mCurrentFullUserRecord.mInitialDownVolumeStream,
   1214                                         mCurrentFullUserRecord.mInitialDownMusicOnly);
   1215                                 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
   1216                             } else {
   1217                                 dispatchVolumeKeyLongPressLocked(keyEvent);
   1218                             }
   1219                         }
   1220                     }
   1221                 }
   1222             } finally {
   1223                 Binder.restoreCallingIdentity(token);
   1224             }
   1225         }
   1226 
   1227         private void dispatchVolumeKeyEventLocked(
   1228                 KeyEvent keyEvent, int stream, boolean musicOnly) {
   1229             boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
   1230             boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
   1231             int direction = 0;
   1232             boolean isMute = false;
   1233             switch (keyEvent.getKeyCode()) {
   1234                 case KeyEvent.KEYCODE_VOLUME_UP:
   1235                     direction = AudioManager.ADJUST_RAISE;
   1236                     break;
   1237                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   1238                     direction = AudioManager.ADJUST_LOWER;
   1239                     break;
   1240                 case KeyEvent.KEYCODE_VOLUME_MUTE:
   1241                     isMute = true;
   1242                     break;
   1243             }
   1244             if (down || up) {
   1245                 int flags = AudioManager.FLAG_FROM_KEY;
   1246                 if (musicOnly) {
   1247                     // This flag is used when the screen is off to only affect active media.
   1248                     flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
   1249                 } else {
   1250                     // These flags are consistent with the home screen
   1251                     if (up) {
   1252                         flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
   1253                     } else {
   1254                         flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
   1255                     }
   1256                 }
   1257                 if (direction != 0) {
   1258                     // If this is action up we want to send a beep for non-music events
   1259                     if (up) {
   1260                         direction = 0;
   1261                     }
   1262                     dispatchAdjustVolumeLocked(stream, direction, flags);
   1263                 } else if (isMute) {
   1264                     if (down && keyEvent.getRepeatCount() == 0) {
   1265                         dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
   1266                     }
   1267                 }
   1268             }
   1269         }
   1270 
   1271         @Override
   1272         public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
   1273             final long token = Binder.clearCallingIdentity();
   1274             try {
   1275                 synchronized (mLock) {
   1276                     dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
   1277                 }
   1278             } finally {
   1279                 Binder.restoreCallingIdentity(token);
   1280             }
   1281         }
   1282 
   1283         @Override
   1284         public void setRemoteVolumeController(IRemoteVolumeController rvc) {
   1285             final int pid = Binder.getCallingPid();
   1286             final int uid = Binder.getCallingUid();
   1287             final long token = Binder.clearCallingIdentity();
   1288             try {
   1289                 enforceSystemUiPermission("listen for volume changes", pid, uid);
   1290                 mRvc = rvc;
   1291             } finally {
   1292                 Binder.restoreCallingIdentity(token);
   1293             }
   1294         }
   1295 
   1296         @Override
   1297         public boolean isGlobalPriorityActive() {
   1298             synchronized (mLock) {
   1299                 return isGlobalPriorityActiveLocked();
   1300             }
   1301         }
   1302 
   1303         @Override
   1304         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
   1305             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
   1306 
   1307             pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
   1308             pw.println();
   1309 
   1310             synchronized (mLock) {
   1311                 pw.println(mSessionsListeners.size() + " sessions listeners.");
   1312                 pw.println("Global priority session is " + mGlobalPrioritySession);
   1313                 if (mGlobalPrioritySession != null) {
   1314                     mGlobalPrioritySession.dump(pw, "  ");
   1315                 }
   1316                 pw.println("User Records:");
   1317                 int count = mUserRecords.size();
   1318                 for (int i = 0; i < count; i++) {
   1319                     mUserRecords.valueAt(i).dumpLocked(pw, "");
   1320                 }
   1321                 mAudioPlaybackMonitor.dump(pw, "");
   1322             }
   1323         }
   1324 
   1325         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
   1326                 final int uid) {
   1327             String packageName = null;
   1328             if (componentName != null) {
   1329                 // If they gave us a component name verify they own the
   1330                 // package
   1331                 packageName = componentName.getPackageName();
   1332                 enforcePackageName(packageName, uid);
   1333             }
   1334             // Check that they can make calls on behalf of the user and
   1335             // get the final user id
   1336             int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
   1337                     true /* allowAll */, true /* requireFull */, "getSessions", packageName);
   1338             // Check if they have the permissions or their component is
   1339             // enabled for the user they're calling from.
   1340             enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
   1341             return resolvedUserId;
   1342         }
   1343 
   1344         private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
   1345             MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
   1346                     : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
   1347 
   1348             boolean preferSuggestedStream = false;
   1349             if (isValidLocalStreamType(suggestedStream)
   1350                     && AudioSystem.isStreamActive(suggestedStream, 0)) {
   1351                 preferSuggestedStream = true;
   1352             }
   1353             if (DEBUG_KEY_EVENT) {
   1354                 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
   1355                         + flags + ", suggestedStream=" + suggestedStream
   1356                         + ", preferSuggestedStream=" + preferSuggestedStream);
   1357             }
   1358             if (session == null || preferSuggestedStream) {
   1359                 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
   1360                         && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
   1361                     if (DEBUG) {
   1362                         Log.d(TAG, "No active session to adjust, skipping media only volume event");
   1363                     }
   1364                     return;
   1365                 }
   1366 
   1367                 // Execute mAudioService.adjustSuggestedStreamVolume() on
   1368                 // handler thread of MediaSessionService.
   1369                 // This will release the MediaSessionService.mLock sooner and avoid
   1370                 // a potential deadlock between MediaSessionService.mLock and
   1371                 // ActivityManagerService lock.
   1372                 mHandler.post(new Runnable() {
   1373                     @Override
   1374                     public void run() {
   1375                         try {
   1376                             String packageName = getContext().getOpPackageName();
   1377                             mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
   1378                                     flags, packageName, TAG);
   1379                         } catch (RemoteException e) {
   1380                             Log.e(TAG, "Error adjusting default volume.", e);
   1381                         }
   1382                     }
   1383                 });
   1384             } else {
   1385                 session.adjustVolume(direction, flags, getContext().getPackageName(),
   1386                         Process.SYSTEM_UID, true);
   1387             }
   1388         }
   1389 
   1390         private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
   1391             int action = keyEvent.getAction();
   1392             boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
   1393             if (action == KeyEvent.ACTION_DOWN) {
   1394                 if (keyEvent.getRepeatCount() == 0) {
   1395                     mVoiceButtonDown = true;
   1396                     mVoiceButtonHandled = false;
   1397                 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
   1398                     mVoiceButtonHandled = true;
   1399                     startVoiceInput(needWakeLock);
   1400                 }
   1401             } else if (action == KeyEvent.ACTION_UP) {
   1402                 if (mVoiceButtonDown) {
   1403                     mVoiceButtonDown = false;
   1404                     if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
   1405                         // Resend the down then send this event through
   1406                         KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
   1407                         dispatchMediaKeyEventLocked(downEvent, needWakeLock);
   1408                         dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
   1409                     }
   1410                 }
   1411             }
   1412         }
   1413 
   1414         private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
   1415             MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
   1416             if (session != null) {
   1417                 if (DEBUG_KEY_EVENT) {
   1418                     Log.d(TAG, "Sending " + keyEvent + " to " + session);
   1419                 }
   1420                 if (needWakeLock) {
   1421                     mKeyEventReceiver.aquireWakeLockLocked();
   1422                 }
   1423                 // If we don't need a wakelock use -1 as the id so we won't release it later.
   1424                 session.sendMediaButton(keyEvent,
   1425                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
   1426                         mKeyEventReceiver, Process.SYSTEM_UID,
   1427                         getContext().getPackageName());
   1428                 if (mCurrentFullUserRecord.mCallback != null) {
   1429                     try {
   1430                         mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
   1431                                 keyEvent,
   1432                                 new MediaSession.Token(session.getControllerBinder()));
   1433                     } catch (RemoteException e) {
   1434                         Log.w(TAG, "Failed to send callback", e);
   1435                     }
   1436                 }
   1437             } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
   1438                     || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
   1439                 if (needWakeLock) {
   1440                     mKeyEventReceiver.aquireWakeLockLocked();
   1441                 }
   1442                 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
   1443                 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
   1444                 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
   1445                 try {
   1446                     if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
   1447                         PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
   1448                         if (DEBUG_KEY_EVENT) {
   1449                             Log.d(TAG, "Sending " + keyEvent
   1450                                     + " to the last known PendingIntent " + receiver);
   1451                         }
   1452                         receiver.send(getContext(),
   1453                                 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
   1454                                 mediaButtonIntent, mKeyEventReceiver, mHandler);
   1455                         if (mCurrentFullUserRecord.mCallback != null) {
   1456                             ComponentName componentName = mCurrentFullUserRecord
   1457                                     .mLastMediaButtonReceiver.getIntent().getComponent();
   1458                             if (componentName != null) {
   1459                                 mCurrentFullUserRecord.mCallback
   1460                                         .onMediaKeyEventDispatchedToMediaButtonReceiver(
   1461                                                 keyEvent, componentName);
   1462                             }
   1463                         }
   1464                     } else {
   1465                         ComponentName receiver =
   1466                                 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
   1467                         if (DEBUG_KEY_EVENT) {
   1468                             Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
   1469                                     + receiver);
   1470                         }
   1471                         mediaButtonIntent.setComponent(receiver);
   1472                         getContext().sendBroadcastAsUser(mediaButtonIntent,
   1473                                 UserHandle.of(mCurrentFullUserRecord
   1474                                       .mRestoredMediaButtonReceiverUserId));
   1475                         if (mCurrentFullUserRecord.mCallback != null) {
   1476                             mCurrentFullUserRecord.mCallback
   1477                                     .onMediaKeyEventDispatchedToMediaButtonReceiver(
   1478                                             keyEvent, receiver);
   1479                         }
   1480                     }
   1481                 } catch (CanceledException e) {
   1482                     Log.i(TAG, "Error sending key event to media button receiver "
   1483                             + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
   1484                 } catch (RemoteException e) {
   1485                     Log.w(TAG, "Failed to send callback", e);
   1486                 }
   1487             }
   1488         }
   1489 
   1490         private void startVoiceInput(boolean needWakeLock) {
   1491             Intent voiceIntent = null;
   1492             // select which type of search to launch:
   1493             // - screen on and device unlocked: action is ACTION_WEB_SEARCH
   1494             // - device locked or screen off: action is
   1495             // ACTION_VOICE_SEARCH_HANDS_FREE
   1496             // with EXTRA_SECURE set to true if the device is securely locked
   1497             PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
   1498             boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
   1499             if (!isLocked && pm.isScreenOn()) {
   1500                 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
   1501                 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
   1502             } else {
   1503                 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
   1504                 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
   1505                         isLocked && mKeyguardManager.isKeyguardSecure());
   1506                 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
   1507             }
   1508             // start the search activity
   1509             if (needWakeLock) {
   1510                 mMediaEventWakeLock.acquire();
   1511             }
   1512             try {
   1513                 if (voiceIntent != null) {
   1514                     voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1515                             | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1516                     if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
   1517                     getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
   1518                 }
   1519             } catch (ActivityNotFoundException e) {
   1520                 Log.w(TAG, "No activity for search: " + e);
   1521             } finally {
   1522                 if (needWakeLock) {
   1523                     mMediaEventWakeLock.release();
   1524                 }
   1525             }
   1526         }
   1527 
   1528         private boolean isVoiceKey(int keyCode) {
   1529             return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
   1530                     || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
   1531         }
   1532 
   1533         private boolean isUserSetupComplete() {
   1534             return Settings.Secure.getIntForUser(getContext().getContentResolver(),
   1535                     Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
   1536         }
   1537 
   1538         // we only handle public stream types, which are 0-5
   1539         private boolean isValidLocalStreamType(int streamType) {
   1540             return streamType >= AudioManager.STREAM_VOICE_CALL
   1541                     && streamType <= AudioManager.STREAM_NOTIFICATION;
   1542         }
   1543 
   1544         private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
   1545             private KeyEvent mKeyEvent;
   1546             private boolean mNeedWakeLock;
   1547             private boolean mHandled;
   1548 
   1549             private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
   1550                 super(mHandler);
   1551                 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
   1552                 mKeyEvent = keyEvent;
   1553                 mNeedWakeLock = needWakeLock;
   1554             }
   1555 
   1556             @Override
   1557             public void run() {
   1558                 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
   1559                 dispatchMediaKeyEvent();
   1560             }
   1561 
   1562             @Override
   1563             protected void onReceiveResult(int resultCode, Bundle resultData) {
   1564                 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
   1565                     mHandled = true;
   1566                     mHandler.removeCallbacks(this);
   1567                     return;
   1568                 }
   1569                 dispatchMediaKeyEvent();
   1570             }
   1571 
   1572             private void dispatchMediaKeyEvent() {
   1573                 if (mHandled) {
   1574                     return;
   1575                 }
   1576                 mHandled = true;
   1577                 mHandler.removeCallbacks(this);
   1578                 synchronized (mLock) {
   1579                     if (!isGlobalPriorityActiveLocked()
   1580                             && isVoiceKey(mKeyEvent.getKeyCode())) {
   1581                         handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
   1582                     } else {
   1583                         dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
   1584                     }
   1585                 }
   1586             }
   1587         }
   1588 
   1589         private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
   1590 
   1591         class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
   1592                 PendingIntent.OnFinished {
   1593             private final Handler mHandler;
   1594             private int mRefCount = 0;
   1595             private int mLastTimeoutId = 0;
   1596 
   1597             public KeyEventWakeLockReceiver(Handler handler) {
   1598                 super(handler);
   1599                 mHandler = handler;
   1600             }
   1601 
   1602             public void onTimeout() {
   1603                 synchronized (mLock) {
   1604                     if (mRefCount == 0) {
   1605                         // We've already released it, so just return
   1606                         return;
   1607                     }
   1608                     mLastTimeoutId++;
   1609                     mRefCount = 0;
   1610                     releaseWakeLockLocked();
   1611                 }
   1612             }
   1613 
   1614             public void aquireWakeLockLocked() {
   1615                 if (mRefCount == 0) {
   1616                     mMediaEventWakeLock.acquire();
   1617                 }
   1618                 mRefCount++;
   1619                 mHandler.removeCallbacks(this);
   1620                 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
   1621 
   1622             }
   1623 
   1624             @Override
   1625             public void run() {
   1626                 onTimeout();
   1627             }
   1628 
   1629             @Override
   1630             protected void onReceiveResult(int resultCode, Bundle resultData) {
   1631                 if (resultCode < mLastTimeoutId) {
   1632                     // Ignore results from calls that were before the last
   1633                     // timeout, just in case.
   1634                     return;
   1635                 } else {
   1636                     synchronized (mLock) {
   1637                         if (mRefCount > 0) {
   1638                             mRefCount--;
   1639                             if (mRefCount == 0) {
   1640                                 releaseWakeLockLocked();
   1641                             }
   1642                         }
   1643                     }
   1644                 }
   1645             }
   1646 
   1647             private void releaseWakeLockLocked() {
   1648                 mMediaEventWakeLock.release();
   1649                 mHandler.removeCallbacks(this);
   1650             }
   1651 
   1652             @Override
   1653             public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
   1654                     String resultData, Bundle resultExtras) {
   1655                 onReceiveResult(resultCode, null);
   1656             }
   1657         };
   1658 
   1659         BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
   1660             @Override
   1661             public void onReceive(Context context, Intent intent) {
   1662                 if (intent == null) {
   1663                     return;
   1664                 }
   1665                 Bundle extras = intent.getExtras();
   1666                 if (extras == null) {
   1667                     return;
   1668                 }
   1669                 synchronized (mLock) {
   1670                     if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
   1671                             && mMediaEventWakeLock.isHeld()) {
   1672                         mMediaEventWakeLock.release();
   1673                     }
   1674                 }
   1675             }
   1676         };
   1677     }
   1678 
   1679     final class MessageHandler extends Handler {
   1680         private static final int MSG_SESSIONS_CHANGED = 1;
   1681         private static final int MSG_VOLUME_INITIAL_DOWN = 2;
   1682         private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
   1683 
   1684         @Override
   1685         public void handleMessage(Message msg) {
   1686             switch (msg.what) {
   1687                 case MSG_SESSIONS_CHANGED:
   1688                     pushSessionsChanged((int) msg.obj);
   1689                     break;
   1690                 case MSG_VOLUME_INITIAL_DOWN:
   1691                     synchronized (mLock) {
   1692                         FullUserRecord user = mUserRecords.get((int) msg.arg1);
   1693                         if (user != null && user.mInitialDownVolumeKeyEvent != null) {
   1694                             dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
   1695                             // Mark that the key is already handled.
   1696                             user.mInitialDownVolumeKeyEvent = null;
   1697                         }
   1698                     }
   1699                     break;
   1700             }
   1701         }
   1702 
   1703         public void postSessionsChanged(int userId) {
   1704             // Use object instead of the arguments when posting message to remove pending requests.
   1705             Integer userIdInteger = mIntegerCache.get(userId);
   1706             if (userIdInteger == null) {
   1707                 userIdInteger = Integer.valueOf(userId);
   1708                 mIntegerCache.put(userId, userIdInteger);
   1709             }
   1710             removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
   1711             obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
   1712         }
   1713     }
   1714 }
   1715