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 static android.media.SessionToken2.TYPE_SESSION;
     20 
     21 import android.app.ActivityManager;
     22 import android.app.AppGlobals;
     23 import android.app.INotificationManager;
     24 import android.app.KeyguardManager;
     25 import android.app.PendingIntent;
     26 import android.app.PendingIntent.CanceledException;
     27 import android.content.ActivityNotFoundException;
     28 import android.content.BroadcastReceiver;
     29 import android.content.ComponentName;
     30 import android.content.ContentResolver;
     31 import android.content.Context;
     32 import android.content.Intent;
     33 import android.content.IntentFilter;
     34 import android.content.pm.IPackageManager;
     35 import android.content.pm.PackageManager;
     36 import android.content.pm.PackageManager.NameNotFoundException;
     37 import android.content.pm.ResolveInfo;
     38 import android.content.pm.ServiceInfo;
     39 import android.content.pm.UserInfo;
     40 import android.database.ContentObserver;
     41 import android.media.AudioManager;
     42 import android.media.AudioPlaybackConfiguration;
     43 import android.media.AudioSystem;
     44 import android.media.IAudioService;
     45 import android.media.IRemoteVolumeController;
     46 import android.media.ISessionTokensListener;
     47 import android.media.MediaController2;
     48 import android.media.MediaLibraryService2;
     49 import android.media.MediaSessionService2;
     50 import android.media.SessionToken2;
     51 import android.media.session.IActiveSessionsListener;
     52 import android.media.session.ICallback;
     53 import android.media.session.IOnMediaKeyListener;
     54 import android.media.session.IOnVolumeKeyLongPressListener;
     55 import android.media.session.ISession;
     56 import android.media.session.ISessionCallback;
     57 import android.media.session.ISessionManager;
     58 import android.media.session.MediaSession;
     59 import android.media.session.MediaSessionManager;
     60 import android.net.Uri;
     61 import android.os.Binder;
     62 import android.os.Bundle;
     63 import android.os.Handler;
     64 import android.os.IBinder;
     65 import android.os.Message;
     66 import android.os.PowerManager;
     67 import android.os.Process;
     68 import android.os.RemoteException;
     69 import android.os.ResultReceiver;
     70 import android.os.ServiceManager;
     71 import android.os.UserHandle;
     72 import android.os.UserManager;
     73 import android.provider.Settings;
     74 import android.speech.RecognizerIntent;
     75 import android.text.TextUtils;
     76 import android.util.ArrayMap;
     77 import android.util.Log;
     78 import android.util.Slog;
     79 import android.util.SparseArray;
     80 import android.util.SparseIntArray;
     81 import android.view.KeyEvent;
     82 import android.view.ViewConfiguration;
     83 
     84 import com.android.internal.os.BackgroundThread;
     85 import com.android.internal.util.DumpUtils;
     86 import com.android.server.SystemService;
     87 import com.android.server.Watchdog;
     88 import com.android.server.Watchdog.Monitor;
     89 
     90 import java.io.FileDescriptor;
     91 import java.io.PrintWriter;
     92 import java.util.ArrayList;
     93 import java.util.HashSet;
     94 import java.util.List;
     95 import java.util.Map;
     96 import java.util.Set;
     97 import java.util.NoSuchElementException;
     98 
     99 /**
    100  * System implementation of MediaSessionManager
    101  */
    102 public class MediaSessionService extends SystemService implements Monitor {
    103     private static final String TAG = "MediaSessionService";
    104     static final boolean USE_MEDIA2_APIS = false; // TODO: Change this to true when we're ready.
    105     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    106     // Leave log for key event always.
    107     private static final boolean DEBUG_KEY_EVENT = true;
    108 
    109     private static final int WAKELOCK_TIMEOUT = 5000;
    110     private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
    111 
    112     private final SessionManagerImpl mSessionManagerImpl;
    113 
    114     // Keeps the full user id for each user.
    115     private final SparseIntArray mFullUserIds = new SparseIntArray();
    116     private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
    117     private final ArrayList<SessionsListenerRecord> mSessionsListeners
    118             = new ArrayList<SessionsListenerRecord>();
    119     private final Object mLock = new Object();
    120     private final MessageHandler mHandler = new MessageHandler();
    121     private final PowerManager.WakeLock mMediaEventWakeLock;
    122     private final int mLongPressTimeout;
    123     private final INotificationManager mNotificationManager;
    124     private final IPackageManager mPackageManager;
    125 
    126     private KeyguardManager mKeyguardManager;
    127     private IAudioService mAudioService;
    128     private ContentResolver mContentResolver;
    129     private SettingsObserver mSettingsObserver;
    130     private boolean mHasFeatureLeanback;
    131 
    132     // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
    133     // It's always not null after the MediaSessionService is started.
    134     private FullUserRecord mCurrentFullUserRecord;
    135     private MediaSessionRecord mGlobalPrioritySession;
    136     private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
    137 
    138     // Used to notify system UI when remote volume was changed. TODO find a
    139     // better way to handle this.
    140     private IRemoteVolumeController mRvc;
    141 
    142     // MediaSession2 support
    143     // TODO(jaewan): Support multi-user and managed profile. (b/73597722)
    144     // TODO(jaewan): Make it priority list for handling volume/media key. (b/73760382)
    145     private final Map<SessionToken2, MediaController2> mSessionRecords = new ArrayMap<>();
    146 
    147     private final List<SessionTokensListenerRecord> mSessionTokensListeners = new ArrayList<>();
    148 
    149     public MediaSessionService(Context context) {
    150         super(context);
    151         mSessionManagerImpl = new SessionManagerImpl();
    152         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    153         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
    154         mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
    155         mNotificationManager = INotificationManager.Stub.asInterface(
    156                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
    157         mPackageManager = AppGlobals.getPackageManager();
    158     }
    159 
    160     @Override
    161     public void onStart() {
    162         publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
    163         Watchdog.getInstance().addMonitor(this);
    164         mKeyguardManager =
    165                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
    166         mAudioService = getAudioService();
    167         mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
    168         mAudioPlayerStateMonitor.registerListener(
    169                 (config, isRemoved) -> {
    170                     if (isRemoved || !config.isActive() || config.getPlayerType()
    171                             == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
    172                         return;
    173                     }
    174                     synchronized (mLock) {
    175                         FullUserRecord user = getFullUserRecordLocked(
    176                                 UserHandle.getUserId(config.getClientUid()));
    177                         if (user != null) {
    178                             user.mPriorityStack.updateMediaButtonSessionIfNeeded();
    179                         }
    180                     }
    181                 }, null /* handler */);
    182         mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
    183         mContentResolver = getContext().getContentResolver();
    184         mSettingsObserver = new SettingsObserver();
    185         mSettingsObserver.observe();
    186         mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
    187                 PackageManager.FEATURE_LEANBACK);
    188 
    189         updateUser();
    190 
    191         registerPackageBroadcastReceivers();
    192         // TODO(jaewan): Query per users (b/73597722)
    193         buildMediaSessionService2List();
    194     }
    195 
    196     private IAudioService getAudioService() {
    197         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
    198         return IAudioService.Stub.asInterface(b);
    199     }
    200 
    201     private boolean isGlobalPriorityActiveLocked() {
    202         return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
    203     }
    204 
    205     public void updateSession(MediaSessionRecord record) {
    206         synchronized (mLock) {
    207             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    208             if (user == null) {
    209                 Log.w(TAG, "Unknown session updated. Ignoring.");
    210                 return;
    211             }
    212             if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
    213                 if (DEBUG_KEY_EVENT) {
    214                     Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
    215                 }
    216                 user.pushAddressedPlayerChangedLocked();
    217             } else {
    218                 if (!user.mPriorityStack.contains(record)) {
    219                     Log.w(TAG, "Unknown session updated. Ignoring.");
    220                     return;
    221                 }
    222                 user.mPriorityStack.onSessionStateChange(record);
    223             }
    224             mHandler.postSessionsChanged(record.getUserId());
    225         }
    226     }
    227 
    228     public void setGlobalPrioritySession(MediaSessionRecord record) {
    229         synchronized (mLock) {
    230             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    231             if (mGlobalPrioritySession != record) {
    232                 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
    233                         + " to " + record);
    234                 mGlobalPrioritySession = record;
    235                 if (user != null && user.mPriorityStack.contains(record)) {
    236                     // Handle the global priority session separately.
    237                     // Otherwise, it can be the media button session regardless of the active state
    238                     // because it or other system components might have been the lastly played media
    239                     // app.
    240                     user.mPriorityStack.removeSession(record);
    241                 }
    242             }
    243         }
    244     }
    245 
    246     private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
    247         List<MediaSessionRecord> records = new ArrayList<>();
    248         if (userId == UserHandle.USER_ALL) {
    249             int size = mUserRecords.size();
    250             for (int i = 0; i < size; i++) {
    251                 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
    252             }
    253         } else {
    254             FullUserRecord user = getFullUserRecordLocked(userId);
    255             if (user == null) {
    256                 Log.w(TAG, "getSessions failed. Unknown user " + userId);
    257                 return records;
    258             }
    259             records.addAll(user.mPriorityStack.getActiveSessions(userId));
    260         }
    261 
    262         // Return global priority session at the first whenever it's asked.
    263         if (isGlobalPriorityActiveLocked()
    264                 && (userId == UserHandle.USER_ALL
    265                     || userId == mGlobalPrioritySession.getUserId())) {
    266             records.add(0, mGlobalPrioritySession);
    267         }
    268         return records;
    269     }
    270 
    271     /**
    272      * Tells the system UI that volume has changed on an active remote session.
    273      */
    274     public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
    275         if (mRvc == null || !session.isActive()) {
    276             return;
    277         }
    278         try {
    279             mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
    280         } catch (Exception e) {
    281             Log.wtf(TAG, "Error sending volume change to system UI.", e);
    282         }
    283     }
    284 
    285     public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
    286         synchronized (mLock) {
    287             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    288             if (user == null || !user.mPriorityStack.contains(record)) {
    289                 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
    290                 return;
    291             }
    292             user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
    293         }
    294     }
    295 
    296     public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
    297         synchronized (mLock) {
    298             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    299             if (user == null || !user.mPriorityStack.contains(record)) {
    300                 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
    301                 return;
    302             }
    303             pushRemoteVolumeUpdateLocked(record.getUserId());
    304         }
    305     }
    306 
    307     @Override
    308     public void onStartUser(int userId) {
    309         if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
    310         updateUser();
    311     }
    312 
    313     @Override
    314     public void onSwitchUser(int userId) {
    315         if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
    316         updateUser();
    317     }
    318 
    319     @Override
    320     public void onStopUser(int userId) {
    321         if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
    322         synchronized (mLock) {
    323             FullUserRecord user = getFullUserRecordLocked(userId);
    324             if (user != null) {
    325                 if (user.mFullUserId == userId) {
    326                     user.destroySessionsForUserLocked(UserHandle.USER_ALL);
    327                     mUserRecords.remove(userId);
    328                 } else {
    329                     user.destroySessionsForUserLocked(userId);
    330                 }
    331             }
    332             updateUser();
    333         }
    334     }
    335 
    336     @Override
    337     public void monitor() {
    338         synchronized (mLock) {
    339             // Check for deadlock
    340         }
    341     }
    342 
    343     protected void enforcePhoneStatePermission(int pid, int uid) {
    344         if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
    345                 != PackageManager.PERMISSION_GRANTED) {
    346             throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
    347         }
    348     }
    349 
    350     void sessionDied(MediaSessionRecord session) {
    351         synchronized (mLock) {
    352             destroySessionLocked(session);
    353         }
    354     }
    355 
    356     void destroySession(MediaSessionRecord session) {
    357         synchronized (mLock) {
    358             destroySessionLocked(session);
    359         }
    360     }
    361 
    362     private void updateUser() {
    363         synchronized (mLock) {
    364             UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
    365             mFullUserIds.clear();
    366             List<UserInfo> allUsers = manager.getUsers();
    367             if (allUsers != null) {
    368                 for (UserInfo userInfo : allUsers) {
    369                     if (userInfo.isManagedProfile()) {
    370                         mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
    371                     } else {
    372                         mFullUserIds.put(userInfo.id, userInfo.id);
    373                         if (mUserRecords.get(userInfo.id) == null) {
    374                             mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
    375                         }
    376                     }
    377                 }
    378             }
    379             // Ensure that the current full user exists.
    380             int currentFullUserId = ActivityManager.getCurrentUser();
    381             mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
    382             if (mCurrentFullUserRecord == null) {
    383                 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
    384                 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
    385                 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
    386             }
    387             mFullUserIds.put(currentFullUserId, currentFullUserId);
    388         }
    389     }
    390 
    391     private void updateActiveSessionListeners() {
    392         synchronized (mLock) {
    393             for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
    394                 SessionsListenerRecord listener = mSessionsListeners.get(i);
    395                 try {
    396                     enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
    397                             listener.mUserId);
    398                 } catch (SecurityException e) {
    399                     Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
    400                             + " is no longer authorized. Disconnecting.");
    401                     mSessionsListeners.remove(i);
    402                     try {
    403                         listener.mListener
    404                                 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
    405                     } catch (Exception e1) {
    406                         // ignore
    407                     }
    408                 }
    409             }
    410         }
    411     }
    412 
    413     /*
    414      * When a session is removed several things need to happen.
    415      * 1. We need to remove it from the relevant user.
    416      * 2. We need to remove it from the priority stack.
    417      * 3. We need to remove it from all sessions.
    418      * 4. If this is the system priority session we need to clear it.
    419      * 5. We need to unlink to death from the cb binder
    420      * 6. We need to tell the session to do any final cleanup (onDestroy)
    421      */
    422     private void destroySessionLocked(MediaSessionRecord session) {
    423         if (DEBUG) {
    424             Log.d(TAG, "Destroying " + session);
    425         }
    426         FullUserRecord user = getFullUserRecordLocked(session.getUserId());
    427         if (mGlobalPrioritySession == session) {
    428             mGlobalPrioritySession = null;
    429             if (session.isActive() && user != null) {
    430                 user.pushAddressedPlayerChangedLocked();
    431             }
    432         } else {
    433             if (user != null) {
    434                 user.mPriorityStack.removeSession(session);
    435             }
    436         }
    437 
    438         try {
    439             session.getCallback().asBinder().unlinkToDeath(session, 0);
    440         } catch (Exception e) {
    441             // ignore exceptions while destroying a session.
    442         }
    443         session.onDestroy();
    444         mHandler.postSessionsChanged(session.getUserId());
    445     }
    446 
    447     private void registerPackageBroadcastReceivers() {
    448         // TODO(jaewan): Only consider changed packages when building session service list
    449         //               when we make this multi-user aware. At that time,
    450         //               use PackageMonitor.getChangingUserId() to know which user has changed.
    451         //               (b/73597722)
    452         IntentFilter filter = new IntentFilter();
    453         filter.addDataScheme("package");
    454         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
    455         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    456         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    457         filter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
    458         filter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
    459         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
    460         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    461         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
    462 
    463         getContext().registerReceiverAsUser(new BroadcastReceiver() {
    464             @Override
    465             public void onReceive(Context context, Intent intent) {
    466                 final int changeUserId = intent.getIntExtra(
    467                         Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
    468                 if (changeUserId == UserHandle.USER_NULL) {
    469                     Log.w(TAG, "Intent broadcast does not contain user handle: "+ intent);
    470                     return;
    471                 }
    472                 // Check if the package is replacing (i.e. reinstalling)
    473                 final boolean isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
    474                 // TODO(jaewan): Add multi-user support with this. (b/73597722)
    475                 // final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
    476 
    477                 if (DEBUG) {
    478                     Log.d(TAG, "Received change in packages, intent=" + intent);
    479                 }
    480                 switch (intent.getAction()) {
    481                     case Intent.ACTION_PACKAGE_ADDED:
    482                     case Intent.ACTION_PACKAGE_REMOVED:
    483                     case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
    484                     case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
    485                         if (isReplacing) {
    486                             // Ignore if the package(s) are replacing. In that case, followings will
    487                             // happen in order.
    488                             //    1. ACTION_PACKAGE_REMOVED with isReplacing=true
    489                             //    2. ACTION_PACKAGE_ADDED with isReplacing=true
    490                             //    3. ACTION_PACKAGE_REPLACED
    491                             //    (Note that ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and
    492                             //     ACTION_EXTERNAL_APPLICATIONS_AVAILABLE will be also called with
    493                             //     isReplacing=true for both ASEC hosted packages and packages in
    494                             //     external storage)
    495                             // Since we only want to update session service list once, ignore
    496                             // actions above when replacing.
    497                             // Replacing will be handled only once with the ACTION_PACKAGE_REPLACED.
    498                             break;
    499                         }
    500                         // pass-through
    501                     case Intent.ACTION_PACKAGE_CHANGED:
    502                     case Intent.ACTION_PACKAGES_SUSPENDED:
    503                     case Intent.ACTION_PACKAGES_UNSUSPENDED:
    504                     case Intent.ACTION_PACKAGE_REPLACED:
    505                         buildMediaSessionService2List();
    506                 }
    507             }
    508         }, UserHandle.ALL, filter, null, BackgroundThread.getHandler());
    509     }
    510 
    511     private void buildMediaSessionService2List() {
    512         if (!USE_MEDIA2_APIS) {
    513             return;
    514         }
    515         if (DEBUG) {
    516             Log.d(TAG, "buildMediaSessionService2List");
    517         }
    518         // TODO(jaewan): Also query for managed profile users. (b/73597722)
    519         PackageManager manager = getContext().getPackageManager();
    520         List<ResolveInfo> services = new ArrayList<>();
    521         // If multiple actions are declared for a service, browser gets higher priority.
    522         List<ResolveInfo> libraryServices = manager.queryIntentServices(
    523                 new Intent(MediaLibraryService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
    524         if (libraryServices != null) {
    525             services.addAll(libraryServices);
    526         }
    527         List<ResolveInfo> sessionServices = manager.queryIntentServices(
    528                 new Intent(MediaSessionService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
    529         if (sessionServices != null) {
    530             services.addAll(sessionServices);
    531         }
    532         synchronized (mLock) {
    533             // List to keep the session services that need be removed because they don't exist
    534             // in the 'services' above.
    535             boolean notifySessionTokensUpdated = false;
    536             Set<SessionToken2> sessionTokensToRemove = new HashSet<>();
    537             for (SessionToken2 token : mSessionRecords.keySet()) {
    538                 if (token.getType() != TYPE_SESSION) {
    539                     sessionTokensToRemove.add(token);
    540                 }
    541             }
    542 
    543             for (int i = 0; i < services.size(); i++) {
    544                 if (services.get(i) == null || services.get(i).serviceInfo == null) {
    545                     continue;
    546                 }
    547                 ServiceInfo serviceInfo = services.get(i).serviceInfo;
    548                 int uid;
    549                 try {
    550                     // TODO(jaewan): Do this per user. (b/73597722)
    551                     uid = manager.getPackageUid(serviceInfo.packageName,
    552                             PackageManager.GET_META_DATA);
    553                 } catch (NameNotFoundException e) {
    554                     continue;
    555                 }
    556                 SessionToken2 token;
    557                 try {
    558                     token = new SessionToken2(getContext(),
    559                             serviceInfo.packageName, serviceInfo.name, uid);
    560                 } catch (IllegalArgumentException e) {
    561                     Log.w(TAG, "Invalid session service", e);
    562                     continue;
    563                 }
    564                 // If the token already exists, keep it in the mSessions by removing from
    565                 // sessionTokensToRemove.
    566                 if (!sessionTokensToRemove.remove(token)) {
    567                     // New session service is found.
    568                     notifySessionTokensUpdated |= addSessionRecordLocked(token);
    569                 }
    570             }
    571             for (SessionToken2 token : sessionTokensToRemove) {
    572                 mSessionRecords.remove(token);
    573                 notifySessionTokensUpdated |= removeSessionRecordLocked(token);
    574             }
    575 
    576             if (notifySessionTokensUpdated) {
    577                 // TODO(jaewan): Pass proper user id to postSessionTokensUpdated(...)
    578                 postSessionTokensUpdated(UserHandle.USER_ALL);
    579             }
    580         }
    581         if (DEBUG) {
    582             Log.d(TAG, "Found " + mSessionRecords.size() + " session services");
    583             for (SessionToken2 token : mSessionRecords.keySet()) {
    584                 Log.d(TAG, "   " + token);
    585             }
    586         }
    587     }
    588 
    589     private void enforcePackageName(String packageName, int uid) {
    590         if (TextUtils.isEmpty(packageName)) {
    591             throw new IllegalArgumentException("packageName may not be empty");
    592         }
    593         String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
    594         final int packageCount = packages.length;
    595         for (int i = 0; i < packageCount; i++) {
    596             if (packageName.equals(packages[i])) {
    597                 return;
    598             }
    599         }
    600         throw new IllegalArgumentException("packageName is not owned by the calling process");
    601     }
    602 
    603     /**
    604      * Checks a caller's authorization to register an IRemoteControlDisplay.
    605      * Authorization is granted if one of the following is true:
    606      * <ul>
    607      * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
    608      * permission</li>
    609      * <li>the caller's listener is one of the enabled notification listeners
    610      * for the caller's user</li>
    611      * </ul>
    612      */
    613     private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
    614             int resolvedUserId) {
    615         if (isCurrentVolumeController(pid, uid)) return;
    616         if (getContext()
    617                 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
    618                     != PackageManager.PERMISSION_GRANTED
    619                 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
    620                         resolvedUserId)) {
    621             throw new SecurityException("Missing permission to control media.");
    622         }
    623     }
    624 
    625     private boolean isCurrentVolumeController(int pid, int uid) {
    626         return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
    627                 pid, uid) == PackageManager.PERMISSION_GRANTED;
    628     }
    629 
    630     private void enforceSystemUiPermission(String action, int pid, int uid) {
    631         if (!isCurrentVolumeController(pid, uid)) {
    632             throw new SecurityException("Only system ui may " + action);
    633         }
    634     }
    635 
    636     /**
    637      * This checks if the component is an enabled notification listener for the
    638      * specified user. Enabled components may only operate on behalf of the user
    639      * they're running as.
    640      *
    641      * @param compName The component that is enabled.
    642      * @param userId The user id of the caller.
    643      * @param forUserId The user id they're making the request on behalf of.
    644      * @return True if the component is enabled, false otherwise
    645      */
    646     private boolean isEnabledNotificationListener(ComponentName compName, int userId,
    647             int forUserId) {
    648         if (userId != forUserId) {
    649             // You may not access another user's content as an enabled listener.
    650             return false;
    651         }
    652         if (DEBUG) {
    653             Log.d(TAG, "Checking if enabled notification listener " + compName);
    654         }
    655         if (compName != null) {
    656             try {
    657                 return mNotificationManager.isNotificationListenerAccessGrantedForUser(
    658                         compName, userId);
    659             } catch(RemoteException e) {
    660                 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
    661             }
    662         }
    663         return false;
    664     }
    665 
    666     private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
    667             String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
    668         synchronized (mLock) {
    669             return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
    670         }
    671     }
    672 
    673     /*
    674      * When a session is created the following things need to happen.
    675      * 1. Its callback binder needs a link to death
    676      * 2. It needs to be added to all sessions.
    677      * 3. It needs to be added to the priority stack.
    678      * 4. It needs to be added to the relevant user record.
    679      */
    680     private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
    681             String callerPackageName, ISessionCallback cb, String tag) {
    682         FullUserRecord user = getFullUserRecordLocked(userId);
    683         if (user == null) {
    684             Log.wtf(TAG, "Request from invalid user: " +  userId);
    685             throw new RuntimeException("Session request from invalid user.");
    686         }
    687 
    688         final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
    689                 callerPackageName, cb, tag, this, mHandler.getLooper());
    690         try {
    691             cb.asBinder().linkToDeath(session, 0);
    692         } catch (RemoteException e) {
    693             throw new RuntimeException("Media Session owner died prematurely.", e);
    694         }
    695 
    696         user.mPriorityStack.addSession(session);
    697         mHandler.postSessionsChanged(userId);
    698 
    699         if (DEBUG) {
    700             Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
    701         }
    702         return session;
    703     }
    704 
    705     private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
    706         for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
    707             if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
    708                 return i;
    709             }
    710         }
    711         return -1;
    712     }
    713 
    714     private void pushSessionsChanged(int userId) {
    715         synchronized (mLock) {
    716             FullUserRecord user = getFullUserRecordLocked(userId);
    717             if (user == null) {
    718                 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
    719                 return;
    720             }
    721             List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
    722             int size = records.size();
    723             ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
    724             for (int i = 0; i < size; i++) {
    725                 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
    726             }
    727             pushRemoteVolumeUpdateLocked(userId);
    728             for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
    729                 SessionsListenerRecord record = mSessionsListeners.get(i);
    730                 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
    731                     try {
    732                         record.mListener.onActiveSessionsChanged(tokens);
    733                     } catch (RemoteException e) {
    734                         Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
    735                                 e);
    736                         mSessionsListeners.remove(i);
    737                     }
    738                 }
    739             }
    740         }
    741     }
    742 
    743     private void pushRemoteVolumeUpdateLocked(int userId) {
    744         if (mRvc != null) {
    745             try {
    746                 FullUserRecord user = getFullUserRecordLocked(userId);
    747                 if (user == null) {
    748                     Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
    749                     return;
    750                 }
    751                 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
    752                 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
    753             } catch (RemoteException e) {
    754                 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
    755             }
    756         }
    757     }
    758 
    759     /**
    760      * Called when the media button receiver for the {@param record} is changed.
    761      *
    762      * @param record the media session whose media button receiver is updated.
    763      */
    764     public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
    765         synchronized (mLock) {
    766             FullUserRecord user = getFullUserRecordLocked(record.getUserId());
    767             MediaSessionRecord mediaButtonSession =
    768                     user.mPriorityStack.getMediaButtonSession();
    769             if (record == mediaButtonSession) {
    770                 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
    771             }
    772         }
    773     }
    774 
    775     private String getCallingPackageName(int uid) {
    776         String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
    777         if (packages != null && packages.length > 0) {
    778             return packages[0];
    779         }
    780         return "";
    781     }
    782 
    783     private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
    784         if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
    785             return;
    786         }
    787         try {
    788             mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
    789         } catch (RemoteException e) {
    790             Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
    791         }
    792     }
    793 
    794     private FullUserRecord getFullUserRecordLocked(int userId) {
    795         int fullUserId = mFullUserIds.get(userId, -1);
    796         if (fullUserId < 0) {
    797             return null;
    798         }
    799         return mUserRecords.get(fullUserId);
    800     }
    801 
    802     void destroySession2Internal(SessionToken2 token) {
    803         synchronized (mLock) {
    804             boolean notifySessionTokensUpdated = false;
    805             if (token.getType() == SessionToken2.TYPE_SESSION) {
    806                 notifySessionTokensUpdated |= removeSessionRecordLocked(token);
    807             } else {
    808                 notifySessionTokensUpdated |= addSessionRecordLocked(token);
    809             }
    810             if (notifySessionTokensUpdated) {
    811                 postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
    812             }
    813         }
    814     }
    815 
    816     /**
    817      * Information about a full user and its corresponding managed profiles.
    818      *
    819      * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
    820      * them when he/she presses a media/volume button. So keeping media sessions for them in one
    821      * place makes more sense and increases the readability.</p>
    822      * <p>The contents of this object is guarded by {@link #mLock}.
    823      */
    824     final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
    825         private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
    826         private final int mFullUserId;
    827         private final MediaSessionStack mPriorityStack;
    828         private PendingIntent mLastMediaButtonReceiver;
    829         private ComponentName mRestoredMediaButtonReceiver;
    830         private int mRestoredMediaButtonReceiverUserId;
    831 
    832         private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
    833         private int mOnVolumeKeyLongPressListenerUid;
    834         private KeyEvent mInitialDownVolumeKeyEvent;
    835         private int mInitialDownVolumeStream;
    836         private boolean mInitialDownMusicOnly;
    837 
    838         private IOnMediaKeyListener mOnMediaKeyListener;
    839         private int mOnMediaKeyListenerUid;
    840         private ICallback mCallback;
    841 
    842         public FullUserRecord(int fullUserId) {
    843             mFullUserId = fullUserId;
    844             mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
    845             // Restore the remembered media button receiver before the boot.
    846             String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
    847                     Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
    848             if (mediaButtonReceiver == null) {
    849                 return;
    850             }
    851             String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
    852             if (tokens == null || tokens.length != 2) {
    853                 return;
    854             }
    855             mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
    856             mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
    857         }
    858 
    859         public void destroySessionsForUserLocked(int userId) {
    860             List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
    861             for (MediaSessionRecord session : sessions) {
    862                 MediaSessionService.this.destroySessionLocked(session);
    863             }
    864         }
    865 
    866         public void dumpLocked(PrintWriter pw, String prefix) {
    867             pw.print(prefix + "Record for full_user=" + mFullUserId);
    868             // Dump managed profile user ids associated with this user.
    869             int size = mFullUserIds.size();
    870             for (int i = 0; i < size; i++) {
    871                 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
    872                         && mFullUserIds.valueAt(i) == mFullUserId) {
    873                     pw.print(", profile_user=" + mFullUserIds.keyAt(i));
    874                 }
    875             }
    876             pw.println();
    877             String indent = prefix + "  ";
    878             pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
    879             pw.println(indent + "Volume key long-press listener package: " +
    880                     getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
    881             pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
    882             pw.println(indent + "Media key listener package: " +
    883                     getCallingPackageName(mOnMediaKeyListenerUid));
    884             pw.println(indent + "Callback: " + mCallback);
    885             pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
    886             pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
    887             mPriorityStack.dump(pw, indent);
    888         }
    889 
    890         @Override
    891         public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
    892                 MediaSessionRecord newMediaButtonSession) {
    893             if (DEBUG_KEY_EVENT) {
    894                 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
    895             }
    896             synchronized (mLock) {
    897                 if (oldMediaButtonSession != null) {
    898                     mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
    899                 }
    900                 if (newMediaButtonSession != null) {
    901                     rememberMediaButtonReceiverLocked(newMediaButtonSession);
    902                     mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
    903                 }
    904                 pushAddressedPlayerChangedLocked();
    905             }
    906         }
    907 
    908         // Remember media button receiver and keep it in the persistent storage.
    909         public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
    910             PendingIntent receiver = record.getMediaButtonReceiver();
    911             mLastMediaButtonReceiver = receiver;
    912             mRestoredMediaButtonReceiver = null;
    913             String componentName = "";
    914             if (receiver != null) {
    915                 ComponentName component = receiver.getIntent().getComponent();
    916                 if (component != null
    917                         && record.getPackageName().equals(component.getPackageName())) {
    918                     componentName = component.flattenToString();
    919                 }
    920             }
    921             Settings.Secure.putStringForUser(mContentResolver,
    922                     Settings.System.MEDIA_BUTTON_RECEIVER,
    923                     componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
    924                     mFullUserId);
    925         }
    926 
    927         private void pushAddressedPlayerChangedLocked() {
    928             if (mCallback == null) {
    929                 return;
    930             }
    931             try {
    932                 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
    933                 if (mediaButtonSession != null) {
    934                     mCallback.onAddressedPlayerChangedToMediaSession(
    935                             new MediaSession.Token(mediaButtonSession.getControllerBinder()));
    936                 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
    937                     mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
    938                             mCurrentFullUserRecord.mLastMediaButtonReceiver
    939                                     .getIntent().getComponent());
    940                 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
    941                     mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
    942                             mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
    943                 }
    944             } catch (RemoteException e) {
    945                 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
    946             }
    947         }
    948 
    949         private MediaSessionRecord getMediaButtonSessionLocked() {
    950             return isGlobalPriorityActiveLocked()
    951                     ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
    952         }
    953     }
    954 
    955     final class SessionsListenerRecord implements IBinder.DeathRecipient {
    956         private final IActiveSessionsListener mListener;
    957         private final ComponentName mComponentName;
    958         private final int mUserId;
    959         private final int mPid;
    960         private final int mUid;
    961 
    962         public SessionsListenerRecord(IActiveSessionsListener listener,
    963                 ComponentName componentName,
    964                 int userId, int pid, int uid) {
    965             mListener = listener;
    966             mComponentName = componentName;
    967             mUserId = userId;
    968             mPid = pid;
    969             mUid = uid;
    970         }
    971 
    972         @Override
    973         public void binderDied() {
    974             synchronized (mLock) {
    975                 mSessionsListeners.remove(this);
    976             }
    977         }
    978     }
    979 
    980     final class SettingsObserver extends ContentObserver {
    981         private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
    982                 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
    983 
    984         private SettingsObserver() {
    985             super(null);
    986         }
    987 
    988         private void observe() {
    989             mContentResolver.registerContentObserver(mSecureSettingsUri,
    990                     false, this, UserHandle.USER_ALL);
    991         }
    992 
    993         @Override
    994         public void onChange(boolean selfChange, Uri uri) {
    995             updateActiveSessionListeners();
    996         }
    997     }
    998 
    999     class SessionManagerImpl extends ISessionManager.Stub {
   1000         private static final String EXTRA_WAKELOCK_ACQUIRED =
   1001                 "android.media.AudioService.WAKELOCK_ACQUIRED";
   1002         private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
   1003 
   1004         private boolean mVoiceButtonDown = false;
   1005         private boolean mVoiceButtonHandled = false;
   1006 
   1007         @Override
   1008         public ISession createSession(String packageName, ISessionCallback cb, String tag,
   1009                 int userId) throws RemoteException {
   1010             final int pid = Binder.getCallingPid();
   1011             final int uid = Binder.getCallingUid();
   1012             final long token = Binder.clearCallingIdentity();
   1013             try {
   1014                 enforcePackageName(packageName, uid);
   1015                 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
   1016                         false /* allowAll */, true /* requireFull */, "createSession", packageName);
   1017                 if (cb == null) {
   1018                     throw new IllegalArgumentException("Controller callback cannot be null");
   1019                 }
   1020                 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
   1021                         .getSessionBinder();
   1022             } finally {
   1023                 Binder.restoreCallingIdentity(token);
   1024             }
   1025         }
   1026 
   1027         @Override
   1028         public List<IBinder> getSessions(ComponentName componentName, int userId) {
   1029             final int pid = Binder.getCallingPid();
   1030             final int uid = Binder.getCallingUid();
   1031             final long token = Binder.clearCallingIdentity();
   1032 
   1033             try {
   1034                 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
   1035                 ArrayList<IBinder> binders = new ArrayList<IBinder>();
   1036                 synchronized (mLock) {
   1037                     List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
   1038                     for (MediaSessionRecord record : records) {
   1039                         binders.add(record.getControllerBinder().asBinder());
   1040                     }
   1041                 }
   1042                 return binders;
   1043             } finally {
   1044                 Binder.restoreCallingIdentity(token);
   1045             }
   1046         }
   1047 
   1048         @Override
   1049         public void addSessionsListener(IActiveSessionsListener listener,
   1050                 ComponentName componentName, int userId) throws RemoteException {
   1051             final int pid = Binder.getCallingPid();
   1052             final int uid = Binder.getCallingUid();
   1053             final long token = Binder.clearCallingIdentity();
   1054 
   1055             try {
   1056                 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
   1057                 synchronized (mLock) {
   1058                     int index = findIndexOfSessionsListenerLocked(listener);
   1059                     if (index != -1) {
   1060                         Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
   1061                         return;
   1062                     }
   1063                     SessionsListenerRecord record = new SessionsListenerRecord(listener,
   1064                             componentName, resolvedUserId, pid, uid);
   1065                     try {
   1066                         listener.asBinder().linkToDeath(record, 0);
   1067                     } catch (RemoteException e) {
   1068                         Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
   1069                         return;
   1070                     }
   1071                     mSessionsListeners.add(record);
   1072                 }
   1073             } finally {
   1074                 Binder.restoreCallingIdentity(token);
   1075             }
   1076         }
   1077 
   1078         @Override
   1079         public void removeSessionsListener(IActiveSessionsListener listener)
   1080                 throws RemoteException {
   1081             synchronized (mLock) {
   1082                 int index = findIndexOfSessionsListenerLocked(listener);
   1083                 if (index != -1) {
   1084                     SessionsListenerRecord record = mSessionsListeners.remove(index);
   1085                     try {
   1086                         record.mListener.asBinder().unlinkToDeath(record, 0);
   1087                     } catch (Exception e) {
   1088                         // ignore exceptions, the record is being removed
   1089                     }
   1090                 }
   1091             }
   1092         }
   1093 
   1094         /**
   1095          * Handles the dispatching of the media button events to one of the
   1096          * registered listeners, or if there was none, broadcast an
   1097          * ACTION_MEDIA_BUTTON intent to the rest of the system.
   1098          *
   1099          * @param packageName The caller package
   1100          * @param asSystemService {@code true} if the event sent to the session as if it was come
   1101          *          from the system service instead of the app process. This helps sessions to
   1102          *          distinguish between the key injection by the app and key events from the
   1103          *          hardware devices. Should be used only when the volume key events aren't handled
   1104          *          by foreground activity. {@code false} otherwise to tell session about the real
   1105          *          caller.
   1106          * @param keyEvent a non-null KeyEvent whose key code is one of the
   1107          *            supported media buttons
   1108          * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
   1109          *            while this key event is dispatched.
   1110          */
   1111         @Override
   1112         public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
   1113                 KeyEvent keyEvent, boolean needWakeLock) {
   1114             if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
   1115                 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
   1116                 return;
   1117             }
   1118 
   1119             final int pid = Binder.getCallingPid();
   1120             final int uid = Binder.getCallingUid();
   1121             final long token = Binder.clearCallingIdentity();
   1122             try {
   1123                 if (DEBUG) {
   1124                     Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid
   1125                             + ", uid=" + uid + ", asSystem=" + asSystemService + ", event="
   1126                             + keyEvent);
   1127                 }
   1128                 if (!isUserSetupComplete()) {
   1129                     // Global media key handling can have the side-effect of starting new
   1130                     // activities which is undesirable while setup is in progress.
   1131                     Slog.i(TAG, "Not dispatching media key event because user "
   1132                             + "setup is in progress.");
   1133                     return;
   1134                 }
   1135 
   1136                 synchronized (mLock) {
   1137                     boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
   1138                     if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
   1139                         // Prevent dispatching key event through reflection while the global
   1140                         // priority session is active.
   1141                         Slog.i(TAG, "Only the system can dispatch media key event "
   1142                                 + "to the global priority session.");
   1143                         return;
   1144                     }
   1145                     if (!isGlobalPriorityActive) {
   1146                         if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
   1147                             if (DEBUG_KEY_EVENT) {
   1148                                 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
   1149                             }
   1150                             try {
   1151                                 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
   1152                                         new MediaKeyListenerResultReceiver(packageName, pid, uid,
   1153                                                 asSystemService, keyEvent, needWakeLock));
   1154                                 return;
   1155                             } catch (RemoteException e) {
   1156                                 Log.w(TAG, "Failed to send " + keyEvent
   1157                                         + " to the media key listener");
   1158                             }
   1159                         }
   1160                     }
   1161                     if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
   1162                         handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
   1163                                 needWakeLock);
   1164                     } else {
   1165                         dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
   1166                                 keyEvent, needWakeLock);
   1167                     }
   1168                 }
   1169             } finally {
   1170                 Binder.restoreCallingIdentity(token);
   1171             }
   1172         }
   1173 
   1174         @Override
   1175         public void setCallback(ICallback callback) {
   1176             final int pid = Binder.getCallingPid();
   1177             final int uid = Binder.getCallingUid();
   1178             final long token = Binder.clearCallingIdentity();
   1179             try {
   1180                 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
   1181                     throw new SecurityException("Only Bluetooth service processes can set"
   1182                             + " Callback");
   1183                 }
   1184                 synchronized (mLock) {
   1185                     int userId = UserHandle.getUserId(uid);
   1186                     FullUserRecord user = getFullUserRecordLocked(userId);
   1187                     if (user == null || user.mFullUserId != userId) {
   1188                         Log.w(TAG, "Only the full user can set the callback"
   1189                                 + ", userId=" + userId);
   1190                         return;
   1191                     }
   1192                     user.mCallback = callback;
   1193                     Log.d(TAG, "The callback " + user.mCallback
   1194                             + " is set by " + getCallingPackageName(uid));
   1195                     if (user.mCallback == null) {
   1196                         return;
   1197                     }
   1198                     try {
   1199                         user.mCallback.asBinder().linkToDeath(
   1200                                 new IBinder.DeathRecipient() {
   1201                                     @Override
   1202                                     public void binderDied() {
   1203                                         synchronized (mLock) {
   1204                                             user.mCallback = null;
   1205                                         }
   1206                                     }
   1207                                 }, 0);
   1208                         user.pushAddressedPlayerChangedLocked();
   1209                     } catch (RemoteException e) {
   1210                         Log.w(TAG, "Failed to set callback", e);
   1211                         user.mCallback = null;
   1212                     }
   1213                 }
   1214             } finally {
   1215                 Binder.restoreCallingIdentity(token);
   1216             }
   1217         }
   1218 
   1219         @Override
   1220         public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
   1221             final int pid = Binder.getCallingPid();
   1222             final int uid = Binder.getCallingUid();
   1223             final long token = Binder.clearCallingIdentity();
   1224             try {
   1225                 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
   1226                 if (getContext().checkPermission(
   1227                         android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
   1228                             != PackageManager.PERMISSION_GRANTED) {
   1229                     throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
   1230                             " permission.");
   1231                 }
   1232 
   1233                 synchronized (mLock) {
   1234                     int userId = UserHandle.getUserId(uid);
   1235                     FullUserRecord user = getFullUserRecordLocked(userId);
   1236                     if (user == null || user.mFullUserId != userId) {
   1237                         Log.w(TAG, "Only the full user can set the volume key long-press listener"
   1238                                 + ", userId=" + userId);
   1239                         return;
   1240                     }
   1241                     if (user.mOnVolumeKeyLongPressListener != null &&
   1242                             user.mOnVolumeKeyLongPressListenerUid != uid) {
   1243                         Log.w(TAG, "The volume key long-press listener cannot be reset"
   1244                                 + " by another app , mOnVolumeKeyLongPressListener="
   1245                                 + user.mOnVolumeKeyLongPressListenerUid
   1246                                 + ", uid=" + uid);
   1247                         return;
   1248                     }
   1249 
   1250                     user.mOnVolumeKeyLongPressListener = listener;
   1251                     user.mOnVolumeKeyLongPressListenerUid = uid;
   1252 
   1253                     Log.d(TAG, "The volume key long-press listener "
   1254                             + listener + " is set by " + getCallingPackageName(uid));
   1255 
   1256                     if (user.mOnVolumeKeyLongPressListener != null) {
   1257                         try {
   1258                             user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
   1259                                     new IBinder.DeathRecipient() {
   1260                                         @Override
   1261                                         public void binderDied() {
   1262                                             synchronized (mLock) {
   1263                                                 user.mOnVolumeKeyLongPressListener = null;
   1264                                             }
   1265                                         }
   1266                                     }, 0);
   1267                         } catch (RemoteException e) {
   1268                             Log.w(TAG, "Failed to set death recipient "
   1269                                     + user.mOnVolumeKeyLongPressListener);
   1270                             user.mOnVolumeKeyLongPressListener = null;
   1271                         }
   1272                     }
   1273                 }
   1274             } finally {
   1275                 Binder.restoreCallingIdentity(token);
   1276             }
   1277         }
   1278 
   1279         @Override
   1280         public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
   1281             final int pid = Binder.getCallingPid();
   1282             final int uid = Binder.getCallingUid();
   1283             final long token = Binder.clearCallingIdentity();
   1284             try {
   1285                 // Enforce SET_MEDIA_KEY_LISTENER permission.
   1286                 if (getContext().checkPermission(
   1287                         android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
   1288                             != PackageManager.PERMISSION_GRANTED) {
   1289                     throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
   1290                             " permission.");
   1291                 }
   1292 
   1293                 synchronized (mLock) {
   1294                     int userId = UserHandle.getUserId(uid);
   1295                     FullUserRecord user = getFullUserRecordLocked(userId);
   1296                     if (user == null || user.mFullUserId != userId) {
   1297                         Log.w(TAG, "Only the full user can set the media key listener"
   1298                                 + ", userId=" + userId);
   1299                         return;
   1300                     }
   1301                     if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
   1302                         Log.w(TAG, "The media key listener cannot be reset by another app. "
   1303                                 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
   1304                                 + ", uid=" + uid);
   1305                         return;
   1306                     }
   1307 
   1308                     user.mOnMediaKeyListener = listener;
   1309                     user.mOnMediaKeyListenerUid = uid;
   1310 
   1311                     Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
   1312                             + " is set by " + getCallingPackageName(uid));
   1313 
   1314                     if (user.mOnMediaKeyListener != null) {
   1315                         try {
   1316                             user.mOnMediaKeyListener.asBinder().linkToDeath(
   1317                                     new IBinder.DeathRecipient() {
   1318                                         @Override
   1319                                         public void binderDied() {
   1320                                             synchronized (mLock) {
   1321                                                 user.mOnMediaKeyListener = null;
   1322                                             }
   1323                                         }
   1324                                     }, 0);
   1325                         } catch (RemoteException e) {
   1326                             Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
   1327                             user.mOnMediaKeyListener = null;
   1328                         }
   1329                     }
   1330                 }
   1331             } finally {
   1332                 Binder.restoreCallingIdentity(token);
   1333             }
   1334         }
   1335 
   1336         /**
   1337          * Handles the dispatching of the volume button events to one of the
   1338          * registered listeners. If there's a volume key long-press listener and
   1339          * there's no active global priority session, long-pressess will be sent to the
   1340          * long-press listener instead of adjusting volume.
   1341          *
   1342          * @param packageName The caller package.
   1343          * @param asSystemService {@code true} if the event sent to the session as if it was come
   1344          *          from the system service instead of the app process. This helps sessions to
   1345          *          distinguish between the key injection by the app and key events from the
   1346          *          hardware devices. Should be used only when the volume key events aren't handled
   1347          *          by foreground activity. {@code false} otherwise to tell session about the real
   1348          *          caller.
   1349          * @param keyEvent a non-null KeyEvent whose key code is one of the
   1350          *            {@link KeyEvent#KEYCODE_VOLUME_UP},
   1351          *            {@link KeyEvent#KEYCODE_VOLUME_DOWN},
   1352          *            or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
   1353          * @param stream stream type to adjust volume.
   1354          * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
   1355          */
   1356         @Override
   1357         public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService,
   1358                 KeyEvent keyEvent, int stream, boolean musicOnly) {
   1359             if (keyEvent == null ||
   1360                     (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
   1361                              && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
   1362                              && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
   1363                 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
   1364                 return;
   1365             }
   1366 
   1367             final int pid = Binder.getCallingPid();
   1368             final int uid = Binder.getCallingUid();
   1369             final long token = Binder.clearCallingIdentity();
   1370 
   1371             if (DEBUG_KEY_EVENT) {
   1372                 Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid="
   1373                         + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent);
   1374             }
   1375 
   1376             try {
   1377                 synchronized (mLock) {
   1378                     if (isGlobalPriorityActiveLocked()
   1379                             || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
   1380                         dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
   1381                                 keyEvent, stream, musicOnly);
   1382                     } else {
   1383                         // TODO: Consider the case when both volume up and down keys are pressed
   1384                         //       at the same time.
   1385                         if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
   1386                             if (keyEvent.getRepeatCount() == 0) {
   1387                                 // Keeps the copy of the KeyEvent because it can be reused.
   1388                                 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
   1389                                         KeyEvent.obtain(keyEvent);
   1390                                 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
   1391                                 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
   1392                                 mHandler.sendMessageDelayed(
   1393                                         mHandler.obtainMessage(
   1394                                                 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
   1395                                                 mCurrentFullUserRecord.mFullUserId, 0),
   1396                                         mLongPressTimeout);
   1397                             }
   1398                             if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
   1399                                 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
   1400                                 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
   1401                                     dispatchVolumeKeyLongPressLocked(
   1402                                             mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
   1403                                     // Mark that the key is already handled.
   1404                                     mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
   1405                                 }
   1406                                 dispatchVolumeKeyLongPressLocked(keyEvent);
   1407                             }
   1408                         } else { // if up
   1409                             mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
   1410                             if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
   1411                                     && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
   1412                                             .getDownTime() == keyEvent.getDownTime()) {
   1413                                 // Short-press. Should change volume.
   1414                                 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
   1415                                         mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
   1416                                         mCurrentFullUserRecord.mInitialDownVolumeStream,
   1417                                         mCurrentFullUserRecord.mInitialDownMusicOnly);
   1418                                 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
   1419                                         keyEvent, stream, musicOnly);
   1420                             } else {
   1421                                 dispatchVolumeKeyLongPressLocked(keyEvent);
   1422                             }
   1423                         }
   1424                     }
   1425                 }
   1426             } finally {
   1427                 Binder.restoreCallingIdentity(token);
   1428             }
   1429         }
   1430 
   1431         private void dispatchVolumeKeyEventLocked(String packageName, int pid, int uid,
   1432                 boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
   1433             boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
   1434             boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
   1435             int direction = 0;
   1436             boolean isMute = false;
   1437             switch (keyEvent.getKeyCode()) {
   1438                 case KeyEvent.KEYCODE_VOLUME_UP:
   1439                     direction = AudioManager.ADJUST_RAISE;
   1440                     break;
   1441                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   1442                     direction = AudioManager.ADJUST_LOWER;
   1443                     break;
   1444                 case KeyEvent.KEYCODE_VOLUME_MUTE:
   1445                     isMute = true;
   1446                     break;
   1447             }
   1448             if (down || up) {
   1449                 int flags = AudioManager.FLAG_FROM_KEY;
   1450                 if (musicOnly) {
   1451                     // This flag is used when the screen is off to only affect active media.
   1452                     flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
   1453                 } else {
   1454                     // These flags are consistent with the home screen
   1455                     if (up) {
   1456                         flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
   1457                     } else {
   1458                         flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
   1459                     }
   1460                 }
   1461                 if (direction != 0) {
   1462                     // If this is action up we want to send a beep for non-music events
   1463                     if (up) {
   1464                         direction = 0;
   1465                     }
   1466                     dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
   1467                             direction, flags);
   1468                 } else if (isMute) {
   1469                     if (down && keyEvent.getRepeatCount() == 0) {
   1470                         dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
   1471                                 AudioManager.ADJUST_TOGGLE_MUTE, flags);
   1472                     }
   1473                 }
   1474             }
   1475         }
   1476 
   1477         @Override
   1478         public void dispatchAdjustVolume(String packageName, int suggestedStream, int delta,
   1479                 int flags) {
   1480             final int pid = Binder.getCallingPid();
   1481             final int uid = Binder.getCallingUid();
   1482             final long token = Binder.clearCallingIdentity();
   1483             try {
   1484                 synchronized (mLock) {
   1485                     dispatchAdjustVolumeLocked(packageName, pid, uid, false,
   1486                             suggestedStream, delta, flags);
   1487                 }
   1488             } finally {
   1489                 Binder.restoreCallingIdentity(token);
   1490             }
   1491         }
   1492 
   1493         @Override
   1494         public void setRemoteVolumeController(IRemoteVolumeController rvc) {
   1495             final int pid = Binder.getCallingPid();
   1496             final int uid = Binder.getCallingUid();
   1497             final long token = Binder.clearCallingIdentity();
   1498             try {
   1499                 enforceSystemUiPermission("listen for volume changes", pid, uid);
   1500                 mRvc = rvc;
   1501             } finally {
   1502                 Binder.restoreCallingIdentity(token);
   1503             }
   1504         }
   1505 
   1506         @Override
   1507         public boolean isGlobalPriorityActive() {
   1508             synchronized (mLock) {
   1509                 return isGlobalPriorityActiveLocked();
   1510             }
   1511         }
   1512 
   1513         @Override
   1514         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
   1515             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
   1516 
   1517             pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
   1518             pw.println();
   1519 
   1520             synchronized (mLock) {
   1521                 pw.println(mSessionsListeners.size() + " sessions listeners.");
   1522                 pw.println("Global priority session is " + mGlobalPrioritySession);
   1523                 if (mGlobalPrioritySession != null) {
   1524                     mGlobalPrioritySession.dump(pw, "  ");
   1525                 }
   1526                 pw.println("User Records:");
   1527                 int count = mUserRecords.size();
   1528                 for (int i = 0; i < count; i++) {
   1529                     mUserRecords.valueAt(i).dumpLocked(pw, "");
   1530                 }
   1531                 mAudioPlayerStateMonitor.dump(getContext(), pw, "");
   1532             }
   1533         }
   1534 
   1535         /**
   1536          * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
   1537          * permission or an enabled notification listener)
   1538          *
   1539          * @param controllerPackageName package name of the controller app
   1540          * @param controllerPid pid of the controller app
   1541          * @param controllerUid uid of the controller app
   1542          */
   1543         @Override
   1544         public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
   1545                 throws RemoteException {
   1546             final int uid = Binder.getCallingUid();
   1547             final long token = Binder.clearCallingIdentity();
   1548             try {
   1549                 int controllerUserId = UserHandle.getUserId(controllerUid);
   1550                 int controllerUidFromPackageName;
   1551                 try {
   1552                     controllerUidFromPackageName = getContext().getPackageManager()
   1553                             .getPackageUidAsUser(controllerPackageName, controllerUserId);
   1554                 } catch (NameNotFoundException e) {
   1555                     if (DEBUG) {
   1556                         Log.d(TAG, "Package " + controllerPackageName + " doesn't exist");
   1557                     }
   1558                     return false;
   1559                 }
   1560                 if (controllerUidFromPackageName != controllerUid) {
   1561                     if (DEBUG) {
   1562                         Log.d(TAG, "Package name " + controllerPackageName
   1563                                 + " doesn't match with the uid " + controllerUid);
   1564                     }
   1565                     return false;
   1566                 }
   1567                 return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
   1568                         controllerPid, controllerUid);
   1569             } finally {
   1570                 Binder.restoreCallingIdentity(token);
   1571             }
   1572         }
   1573 
   1574         /**
   1575          * Called when a {@link android.media.MediaSession2} instance is created.
   1576          * <p>
   1577          * This does two things.
   1578          *   1. Keep the newly created session in the service
   1579          *   2. Do sanity check to ensure unique id per package, and return result
   1580          *
   1581          * @param sessionToken SessionToken2 object in bundled form
   1582          * @return {@code true} if the session's id isn't used by the package now. {@code false}
   1583          *     otherwise.
   1584          */
   1585         @Override
   1586         public boolean createSession2(Bundle sessionToken) {
   1587             if (!USE_MEDIA2_APIS) {
   1588                 return false;
   1589             }
   1590             final int uid = Binder.getCallingUid();
   1591             final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
   1592             if (token == null || token.getUid() != uid) {
   1593                 Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid()
   1594                         + " but from uid=" + uid);
   1595             }
   1596             if (DEBUG) {
   1597                 Log.d(TAG, "createSession2: " + token);
   1598             }
   1599             synchronized (mLock) {
   1600                 MediaController2 controller = mSessionRecords.get(token);
   1601                 if (controller != null && controller.isConnected()) {
   1602                     return false;
   1603                 }
   1604                 Context context = getContext();
   1605                 controller = new MediaController2(context, token, context.getMainExecutor(),
   1606                         new ControllerCallback(token));
   1607                 if (addSessionRecordLocked(token, controller)) {
   1608                     postSessionTokensUpdated(UserHandle.getUserId(token.getUid()));
   1609                 }
   1610                 return true;
   1611             }
   1612         }
   1613 
   1614         /**
   1615          * Called when a {@link android.media.MediaSession2} instance is closed. (i.e. destroyed)
   1616          * <p>
   1617          * Ideally service should know that a session is destroyed through the
   1618          * {@link android.media.MediaController2.ControllerCallback#onDisconnected()}, which is
   1619          * asynchronous call. However, we also need synchronous way together to address timing
   1620          * issue. If the package recreates the session almost immediately, which happens commonly
   1621          * for tests, service will reject the creation through {@link #onSessionCreated(Bundle)}
   1622          * if the service hasn't notified previous destroy yet. This synchronous API will address
   1623          * the issue.
   1624          *
   1625          * @param sessionToken SessionToken2 object in bundled form
   1626          */
   1627         @Override
   1628         public void destroySession2(Bundle sessionToken) {
   1629             if (!USE_MEDIA2_APIS) {
   1630                 return;
   1631             }
   1632             final int uid = Binder.getCallingUid();
   1633             final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
   1634             if (token == null || token.getUid() != uid) {
   1635                 Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid()
   1636                         + " but from uid=" + uid);
   1637             }
   1638             if (DEBUG) {
   1639                 Log.d(TAG, "destroySession2 " + token);
   1640             }
   1641             destroySession2Internal(token);
   1642         }
   1643 
   1644         // TODO(jaewan): Make this API take userId as an argument (b/73597722)
   1645         @Override
   1646         public List<Bundle> getSessionTokens(boolean activeSessionOnly,
   1647                 boolean sessionServiceOnly, String packageName) throws RemoteException {
   1648             if (!USE_MEDIA2_APIS) {
   1649                 return null;
   1650             }
   1651             final int pid = Binder.getCallingPid();
   1652             final int uid = Binder.getCallingUid();
   1653             final long token = Binder.clearCallingIdentity();
   1654 
   1655             List<Bundle> tokens = new ArrayList<>();
   1656             try {
   1657                 verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
   1658                 synchronized (mLock) {
   1659                     for (Map.Entry<SessionToken2, MediaController2> record
   1660                             : mSessionRecords.entrySet()) {
   1661                         boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
   1662                         boolean isActive = record.getValue() != null;
   1663                         if ((activeSessionOnly && !isActive)
   1664                                 || (sessionServiceOnly && !isSessionService)) {
   1665                             continue;
   1666                         }
   1667                         tokens.add(record.getKey().toBundle());
   1668                     }
   1669                 }
   1670             } finally {
   1671                 Binder.restoreCallingIdentity(token);
   1672             }
   1673             return tokens;
   1674         }
   1675 
   1676         @Override
   1677         public void addSessionTokensListener(ISessionTokensListener listener, int userId,
   1678                 String packageName) throws RemoteException {
   1679             if (!USE_MEDIA2_APIS) {
   1680                 return;
   1681             }
   1682             final int pid = Binder.getCallingPid();
   1683             final int uid = Binder.getCallingUid();
   1684             final long token = Binder.clearCallingIdentity();
   1685             try {
   1686                 int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid);
   1687                 synchronized (mLock) {
   1688                     final SessionTokensListenerRecord record =
   1689                             new SessionTokensListenerRecord(listener, resolvedUserId);
   1690                     try {
   1691                         listener.asBinder().linkToDeath(record, 0);
   1692                     } catch (RemoteException e) {
   1693                     }
   1694                     mSessionTokensListeners.add(record);
   1695                 }
   1696             } finally {
   1697                 Binder.restoreCallingIdentity(token);
   1698             }
   1699         }
   1700 
   1701         // TODO(jaewan): Make this API take userId as an argument (b/73597722)
   1702         @Override
   1703         public void removeSessionTokensListener(ISessionTokensListener listener,
   1704                 String packageName) throws RemoteException {
   1705             if (!USE_MEDIA2_APIS) {
   1706                 return;
   1707             }
   1708             final int pid = Binder.getCallingPid();
   1709             final int uid = Binder.getCallingUid();
   1710             final long token = Binder.clearCallingIdentity();
   1711             try {
   1712                 verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
   1713                 synchronized (mLock) {
   1714                     IBinder listenerBinder = listener.asBinder();
   1715                     for (SessionTokensListenerRecord record : mSessionTokensListeners) {
   1716                         if (listenerBinder.equals(record.mListener.asBinder())) {
   1717                             try {
   1718                                 listenerBinder.unlinkToDeath(record, 0);
   1719                             } catch (NoSuchElementException e) {
   1720                             }
   1721                             mSessionTokensListeners.remove(record);
   1722                             break;
   1723                         }
   1724                     }
   1725                 }
   1726             } finally {
   1727                 Binder.restoreCallingIdentity(token);
   1728             }
   1729         }
   1730 
   1731         // For MediaSession
   1732         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
   1733                 final int uid) {
   1734             String packageName = null;
   1735             if (componentName != null) {
   1736                 // If they gave us a component name verify they own the
   1737                 // package
   1738                 packageName = componentName.getPackageName();
   1739                 enforcePackageName(packageName, uid);
   1740             }
   1741             // Check that they can make calls on behalf of the user and
   1742             // get the final user id
   1743             int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
   1744                     true /* allowAll */, true /* requireFull */, "getSessions", packageName);
   1745             // Check if they have the permissions or their component is
   1746             // enabled for the user they're calling from.
   1747             enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
   1748             return resolvedUserId;
   1749         }
   1750 
   1751         // For MediaSession2
   1752         private int verifySessionsRequest2(int targetUserId, String callerPackageName,
   1753                 int callerPid, int callerUid) throws RemoteException {
   1754             // Check that they can make calls on behalf of the user and get the final user id.
   1755             int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid,
   1756                     targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens",
   1757                     callerPackageName);
   1758             // Check if they have the permissions or their component is
   1759             // enabled for the user they're calling from.
   1760             if (!hasMediaControlPermission(
   1761                     resolvedUserId, callerPackageName, callerPid, callerUid)) {
   1762                 throw new SecurityException("Missing permission to control media.");
   1763             }
   1764             return resolvedUserId;
   1765         }
   1766 
   1767         // For MediaSession2
   1768         private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
   1769                 int pid, int uid) throws RemoteException {
   1770             // Allow API calls from the System UI
   1771             if (isCurrentVolumeController(pid, uid)) {
   1772                 return true;
   1773             }
   1774 
   1775             // Check if it's system server or has MEDIA_CONTENT_CONTROL.
   1776             // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
   1777             // check here.
   1778             if (uid == Process.SYSTEM_UID || getContext().checkPermission(
   1779                     android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
   1780                     == PackageManager.PERMISSION_GRANTED) {
   1781                 return true;
   1782             } else if (DEBUG) {
   1783                 Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
   1784             }
   1785 
   1786             // You may not access another user's content as an enabled listener.
   1787             final int userId = UserHandle.getUserId(uid);
   1788             if (resolvedUserId != userId) {
   1789                 return false;
   1790             }
   1791 
   1792             // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
   1793             //               String pkgName) to notification team for optimization
   1794             final List<ComponentName> enabledNotificationListeners =
   1795                     mNotificationManager.getEnabledNotificationListeners(userId);
   1796             if (enabledNotificationListeners != null) {
   1797                 for (int i = 0; i < enabledNotificationListeners.size(); i++) {
   1798                     if (TextUtils.equals(packageName,
   1799                             enabledNotificationListeners.get(i).getPackageName())) {
   1800                         return true;
   1801                     }
   1802                 }
   1803             }
   1804             if (DEBUG) {
   1805                 Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
   1806                         + "notification listener");
   1807             }
   1808             return false;
   1809         }
   1810 
   1811         private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid,
   1812                 boolean asSystemService, int suggestedStream, int direction, int flags) {
   1813             MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
   1814                     : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
   1815 
   1816             boolean preferSuggestedStream = false;
   1817             if (isValidLocalStreamType(suggestedStream)
   1818                     && AudioSystem.isStreamActive(suggestedStream, 0)) {
   1819                 preferSuggestedStream = true;
   1820             }
   1821             if (DEBUG_KEY_EVENT) {
   1822                 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
   1823                         + flags + ", suggestedStream=" + suggestedStream
   1824                         + ", preferSuggestedStream=" + preferSuggestedStream);
   1825             }
   1826             if (session == null || preferSuggestedStream) {
   1827                 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
   1828                         && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
   1829                     if (DEBUG) {
   1830                         Log.d(TAG, "No active session to adjust, skipping media only volume event");
   1831                     }
   1832                     return;
   1833                 }
   1834 
   1835                 // Execute mAudioService.adjustSuggestedStreamVolume() on
   1836                 // handler thread of MediaSessionService.
   1837                 // This will release the MediaSessionService.mLock sooner and avoid
   1838                 // a potential deadlock between MediaSessionService.mLock and
   1839                 // ActivityManagerService lock.
   1840                 mHandler.post(new Runnable() {
   1841                     @Override
   1842                     public void run() {
   1843                         try {
   1844                             String packageName = getContext().getOpPackageName();
   1845                             mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
   1846                                     flags, packageName, TAG);
   1847                         } catch (RemoteException e) {
   1848                             Log.e(TAG, "Error adjusting default volume.", e);
   1849                         } catch (IllegalArgumentException e) {
   1850                             Log.e(TAG, "Cannot adjust volume: direction=" + direction
   1851                                     + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
   1852                                     e);
   1853                         }
   1854                     }
   1855                 });
   1856             } else {
   1857                 session.adjustVolume(packageName, pid, uid, null, asSystemService,
   1858                         direction, flags, true);
   1859             }
   1860         }
   1861 
   1862         private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
   1863                 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
   1864             int action = keyEvent.getAction();
   1865             boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
   1866             if (action == KeyEvent.ACTION_DOWN) {
   1867                 if (keyEvent.getRepeatCount() == 0) {
   1868                     mVoiceButtonDown = true;
   1869                     mVoiceButtonHandled = false;
   1870                 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
   1871                     mVoiceButtonHandled = true;
   1872                     startVoiceInput(needWakeLock);
   1873                 }
   1874             } else if (action == KeyEvent.ACTION_UP) {
   1875                 if (mVoiceButtonDown) {
   1876                     mVoiceButtonDown = false;
   1877                     if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
   1878                         // Resend the down then send this event through
   1879                         KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
   1880                         dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
   1881                                 downEvent, needWakeLock);
   1882                         dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
   1883                                 keyEvent, needWakeLock);
   1884                     }
   1885                 }
   1886             }
   1887         }
   1888 
   1889         private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
   1890                 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
   1891             MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
   1892             if (session != null) {
   1893                 if (DEBUG_KEY_EVENT) {
   1894                     Log.d(TAG, "Sending " + keyEvent + " to " + session);
   1895                 }
   1896                 if (needWakeLock) {
   1897                     mKeyEventReceiver.aquireWakeLockLocked();
   1898                 }
   1899                 // If we don't need a wakelock use -1 as the id so we won't release it later.
   1900                 session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
   1901                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
   1902                         mKeyEventReceiver);
   1903                 if (mCurrentFullUserRecord.mCallback != null) {
   1904                     try {
   1905                         mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
   1906                                 keyEvent, new MediaSession.Token(session.getControllerBinder()));
   1907                     } catch (RemoteException e) {
   1908                         Log.w(TAG, "Failed to send callback", e);
   1909                     }
   1910                 }
   1911             } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
   1912                     || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
   1913                 if (needWakeLock) {
   1914                     mKeyEventReceiver.aquireWakeLockLocked();
   1915                 }
   1916                 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
   1917                 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
   1918                 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
   1919                 // TODO: Find a way to also send PID/UID in secure way.
   1920                 String callerPackageName =
   1921                         (asSystemService) ? getContext().getPackageName() : packageName;
   1922                 mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
   1923                 try {
   1924                     if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
   1925                         PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
   1926                         if (DEBUG_KEY_EVENT) {
   1927                             Log.d(TAG, "Sending " + keyEvent
   1928                                     + " to the last known PendingIntent " + receiver);
   1929                         }
   1930                         receiver.send(getContext(),
   1931                                 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
   1932                                 mediaButtonIntent, mKeyEventReceiver, mHandler);
   1933                         if (mCurrentFullUserRecord.mCallback != null) {
   1934                             ComponentName componentName = mCurrentFullUserRecord
   1935                                     .mLastMediaButtonReceiver.getIntent().getComponent();
   1936                             if (componentName != null) {
   1937                                 mCurrentFullUserRecord.mCallback
   1938                                         .onMediaKeyEventDispatchedToMediaButtonReceiver(
   1939                                                 keyEvent, componentName);
   1940                             }
   1941                         }
   1942                     } else {
   1943                         ComponentName receiver =
   1944                                 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
   1945                         if (DEBUG_KEY_EVENT) {
   1946                             Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
   1947                                     + receiver);
   1948                         }
   1949                         mediaButtonIntent.setComponent(receiver);
   1950                         getContext().sendBroadcastAsUser(mediaButtonIntent,
   1951                                 UserHandle.of(mCurrentFullUserRecord
   1952                                       .mRestoredMediaButtonReceiverUserId));
   1953                         if (mCurrentFullUserRecord.mCallback != null) {
   1954                             mCurrentFullUserRecord.mCallback
   1955                                     .onMediaKeyEventDispatchedToMediaButtonReceiver(
   1956                                             keyEvent, receiver);
   1957                         }
   1958                     }
   1959                 } catch (CanceledException e) {
   1960                     Log.i(TAG, "Error sending key event to media button receiver "
   1961                             + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
   1962                 } catch (RemoteException e) {
   1963                     Log.w(TAG, "Failed to send callback", e);
   1964                 }
   1965             }
   1966         }
   1967 
   1968         private void startVoiceInput(boolean needWakeLock) {
   1969             Intent voiceIntent = null;
   1970             // select which type of search to launch:
   1971             // - screen on and device unlocked: action is ACTION_WEB_SEARCH
   1972             // - device locked or screen off: action is
   1973             // ACTION_VOICE_SEARCH_HANDS_FREE
   1974             // with EXTRA_SECURE set to true if the device is securely locked
   1975             PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
   1976             boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
   1977             if (!isLocked && pm.isScreenOn()) {
   1978                 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
   1979                 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
   1980             } else {
   1981                 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
   1982                 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
   1983                         isLocked && mKeyguardManager.isKeyguardSecure());
   1984                 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
   1985             }
   1986             // start the search activity
   1987             if (needWakeLock) {
   1988                 mMediaEventWakeLock.acquire();
   1989             }
   1990             try {
   1991                 if (voiceIntent != null) {
   1992                     voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1993                             | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   1994                     if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
   1995                     getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
   1996                 }
   1997             } catch (ActivityNotFoundException e) {
   1998                 Log.w(TAG, "No activity for search: " + e);
   1999             } finally {
   2000                 if (needWakeLock) {
   2001                     mMediaEventWakeLock.release();
   2002                 }
   2003             }
   2004         }
   2005 
   2006         private boolean isVoiceKey(int keyCode) {
   2007             return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
   2008                     || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
   2009         }
   2010 
   2011         private boolean isUserSetupComplete() {
   2012             return Settings.Secure.getIntForUser(getContext().getContentResolver(),
   2013                     Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
   2014         }
   2015 
   2016         // we only handle public stream types, which are 0-5
   2017         private boolean isValidLocalStreamType(int streamType) {
   2018             return streamType >= AudioManager.STREAM_VOICE_CALL
   2019                     && streamType <= AudioManager.STREAM_NOTIFICATION;
   2020         }
   2021 
   2022         private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
   2023             private final String mPackageName;
   2024             private final int mPid;
   2025             private final int mUid;
   2026             private final boolean mAsSystemService;
   2027             private final KeyEvent mKeyEvent;
   2028             private final boolean mNeedWakeLock;
   2029             private boolean mHandled;
   2030 
   2031             private MediaKeyListenerResultReceiver(String packageName, int pid, int uid,
   2032                     boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
   2033                 super(mHandler);
   2034                 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
   2035                 mPackageName = packageName;
   2036                 mPid = pid;
   2037                 mUid = uid;
   2038                 mAsSystemService = asSystemService;
   2039                 mKeyEvent = keyEvent;
   2040                 mNeedWakeLock = needWakeLock;
   2041             }
   2042 
   2043             @Override
   2044             public void run() {
   2045                 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
   2046                 dispatchMediaKeyEvent();
   2047             }
   2048 
   2049             @Override
   2050             protected void onReceiveResult(int resultCode, Bundle resultData) {
   2051                 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
   2052                     mHandled = true;
   2053                     mHandler.removeCallbacks(this);
   2054                     return;
   2055                 }
   2056                 dispatchMediaKeyEvent();
   2057             }
   2058 
   2059             private void dispatchMediaKeyEvent() {
   2060                 if (mHandled) {
   2061                     return;
   2062                 }
   2063                 mHandled = true;
   2064                 mHandler.removeCallbacks(this);
   2065                 synchronized (mLock) {
   2066                     if (!isGlobalPriorityActiveLocked()
   2067                             && isVoiceKey(mKeyEvent.getKeyCode())) {
   2068                         handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
   2069                                 mKeyEvent, mNeedWakeLock);
   2070                     } else {
   2071                         dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
   2072                                 mKeyEvent, mNeedWakeLock);
   2073                     }
   2074                 }
   2075             }
   2076         }
   2077 
   2078         private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
   2079 
   2080         class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
   2081                 PendingIntent.OnFinished {
   2082             private final Handler mHandler;
   2083             private int mRefCount = 0;
   2084             private int mLastTimeoutId = 0;
   2085 
   2086             public KeyEventWakeLockReceiver(Handler handler) {
   2087                 super(handler);
   2088                 mHandler = handler;
   2089             }
   2090 
   2091             public void onTimeout() {
   2092                 synchronized (mLock) {
   2093                     if (mRefCount == 0) {
   2094                         // We've already released it, so just return
   2095                         return;
   2096                     }
   2097                     mLastTimeoutId++;
   2098                     mRefCount = 0;
   2099                     releaseWakeLockLocked();
   2100                 }
   2101             }
   2102 
   2103             public void aquireWakeLockLocked() {
   2104                 if (mRefCount == 0) {
   2105                     mMediaEventWakeLock.acquire();
   2106                 }
   2107                 mRefCount++;
   2108                 mHandler.removeCallbacks(this);
   2109                 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
   2110 
   2111             }
   2112 
   2113             @Override
   2114             public void run() {
   2115                 onTimeout();
   2116             }
   2117 
   2118             @Override
   2119             protected void onReceiveResult(int resultCode, Bundle resultData) {
   2120                 if (resultCode < mLastTimeoutId) {
   2121                     // Ignore results from calls that were before the last
   2122                     // timeout, just in case.
   2123                     return;
   2124                 } else {
   2125                     synchronized (mLock) {
   2126                         if (mRefCount > 0) {
   2127                             mRefCount--;
   2128                             if (mRefCount == 0) {
   2129                                 releaseWakeLockLocked();
   2130                             }
   2131                         }
   2132                     }
   2133                 }
   2134             }
   2135 
   2136             private void releaseWakeLockLocked() {
   2137                 mMediaEventWakeLock.release();
   2138                 mHandler.removeCallbacks(this);
   2139             }
   2140 
   2141             @Override
   2142             public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
   2143                     String resultData, Bundle resultExtras) {
   2144                 onReceiveResult(resultCode, null);
   2145             }
   2146         };
   2147 
   2148         BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
   2149             @Override
   2150             public void onReceive(Context context, Intent intent) {
   2151                 if (intent == null) {
   2152                     return;
   2153                 }
   2154                 Bundle extras = intent.getExtras();
   2155                 if (extras == null) {
   2156                     return;
   2157                 }
   2158                 synchronized (mLock) {
   2159                     if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
   2160                             && mMediaEventWakeLock.isHeld()) {
   2161                         mMediaEventWakeLock.release();
   2162                     }
   2163                 }
   2164             }
   2165         };
   2166     }
   2167 
   2168     final class MessageHandler extends Handler {
   2169         private static final int MSG_SESSIONS_CHANGED = 1;
   2170         private static final int MSG_VOLUME_INITIAL_DOWN = 2;
   2171         private static final int MSG_SESSIONS_TOKENS_CHANGED = 3;
   2172         private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
   2173 
   2174         @Override
   2175         public void handleMessage(Message msg) {
   2176             switch (msg.what) {
   2177                 case MSG_SESSIONS_CHANGED:
   2178                     pushSessionsChanged((int) msg.obj);
   2179                     break;
   2180                 case MSG_VOLUME_INITIAL_DOWN:
   2181                     synchronized (mLock) {
   2182                         FullUserRecord user = mUserRecords.get((int) msg.arg1);
   2183                         if (user != null && user.mInitialDownVolumeKeyEvent != null) {
   2184                             dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
   2185                             // Mark that the key is already handled.
   2186                             user.mInitialDownVolumeKeyEvent = null;
   2187                         }
   2188                     }
   2189                     break;
   2190                 case MSG_SESSIONS_TOKENS_CHANGED:
   2191                     pushSessionTokensChanged((int) msg.obj);
   2192                     break;
   2193             }
   2194         }
   2195 
   2196         public void postSessionsChanged(int userId) {
   2197             // Use object instead of the arguments when posting message to remove pending requests.
   2198             Integer userIdInteger = mIntegerCache.get(userId);
   2199             if (userIdInteger == null) {
   2200                 userIdInteger = Integer.valueOf(userId);
   2201                 mIntegerCache.put(userId, userIdInteger);
   2202             }
   2203             removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
   2204             obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
   2205         }
   2206     }
   2207 
   2208     private class ControllerCallback extends MediaController2.ControllerCallback {
   2209 
   2210         private final SessionToken2 mToken;
   2211 
   2212         ControllerCallback(SessionToken2 token) {
   2213             mToken = token;
   2214         }
   2215 
   2216         @Override
   2217         public void onDisconnected(MediaController2 controller) {
   2218             destroySession2Internal(mToken);
   2219         }
   2220     };
   2221 
   2222     private final class SessionTokensListenerRecord implements IBinder.DeathRecipient {
   2223         private final ISessionTokensListener mListener;
   2224         private final int mUserId;
   2225 
   2226         public SessionTokensListenerRecord(ISessionTokensListener listener, int userId) {
   2227             mListener = listener;
   2228             // TODO(jaewan): should userId be mapped through mFullUserIds? (b/73597722)
   2229             mUserId = userId;
   2230         }
   2231 
   2232         @Override
   2233         public void binderDied() {
   2234             synchronized (mLock) {
   2235                 mSessionTokensListeners.remove(this);
   2236             }
   2237         }
   2238     }
   2239 
   2240     private void postSessionTokensUpdated(int userId) {
   2241         mHandler.obtainMessage(MessageHandler.MSG_SESSIONS_TOKENS_CHANGED, userId).sendToTarget();
   2242     }
   2243 
   2244     private void pushSessionTokensChanged(int userId) {
   2245         synchronized (mLock) {
   2246             List<Bundle> tokens = new ArrayList<>();
   2247             for (SessionToken2 token : mSessionRecords.keySet()) {
   2248                 // TODO(jaewan): Remove the check for UserHandle.USER_ALL (shouldn't happen).
   2249                 //               This happens when called form buildMediaSessionService2List(...).
   2250                 //               (b/73760382)
   2251                 if (UserHandle.getUserId(token.getUid()) == userId
   2252                         || UserHandle.USER_ALL == userId) {
   2253                     tokens.add(token.toBundle());
   2254                 }
   2255             }
   2256 
   2257             for (SessionTokensListenerRecord record : mSessionTokensListeners) {
   2258                 // TODO(jaewan): Should userId be mapped through mFullUserIds? (b/73760382)
   2259                 if (record.mUserId == userId || record.mUserId == UserHandle.USER_ALL) {
   2260                     try {
   2261                         record.mListener.onSessionTokensChanged(tokens);
   2262                     } catch (RemoteException e) {
   2263                         Log.w(TAG, "Failed to notify session tokens changed", e);
   2264                     }
   2265                 }
   2266             }
   2267         }
   2268     }
   2269 
   2270     private boolean addSessionRecordLocked(SessionToken2 token) {
   2271         return addSessionRecordLocked(token, null);
   2272     }
   2273 
   2274     private boolean addSessionRecordLocked(SessionToken2 token, MediaController2 controller) {
   2275         if (mSessionRecords.containsKey(token) && mSessionRecords.get(token) == controller) {
   2276             // The key/value pair already exists, no need to update.
   2277             return false;
   2278         }
   2279 
   2280         mSessionRecords.put(token, controller);
   2281         return true;
   2282     }
   2283 
   2284     private boolean removeSessionRecordLocked(SessionToken2 token) {
   2285         if (!mSessionRecords.containsKey(token)) {
   2286             // The key is already removed, no need to remove.
   2287             return false;
   2288         }
   2289 
   2290         mSessionRecords.remove(token);
   2291         return true;
   2292     }
   2293 }
   2294