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