blob: f6a81d07277a552aa98880f271aaff1ac6f2ccdf [file] [log] [blame]
RoboErik01fe6612014-02-13 14:19:04 -08001/*
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
17package com.android.server.media;
18
Sungsoo Lim875e6972017-11-03 02:22:35 +000019import android.annotation.Nullable;
RoboErike7880d82014-04-30 12:48:25 -070020import android.app.ActivityManager;
Julia Reynoldsb852e562017-06-06 16:14:18 -040021import android.app.INotificationManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070022import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070023import android.app.PendingIntent;
24import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070025import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070026import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070027import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070028import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080029import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070030import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070031import android.content.pm.PackageManager;
Jaewan Kima7dce192017-02-16 17:10:54 +090032import android.content.pm.UserInfo;
RoboErik7aef77b2014-08-08 15:56:54 -070033import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070034import android.media.AudioManager;
Sungsoo Lim875e6972017-11-03 02:22:35 +000035import android.media.AudioPlaybackConfiguration;
RoboErik94c716e2014-09-14 13:54:31 -070036import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070037import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070038import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070039import android.media.session.IActiveSessionsListener;
Jaewan Kimbd16f452017-02-03 16:21:38 +090040import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080041import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090042import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070043import android.media.session.ISession;
44import android.media.session.ISessionCallback;
45import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070046import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080047import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070048import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080049import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070050import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080051import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070052import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070053import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070054import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090055import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080056import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070057import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070058import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070059import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090060import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070061import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070062import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080063import android.text.TextUtils;
64import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070065import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070066import android.util.SparseArray;
Jaewan Kima7dce192017-02-16 17:10:54 +090067import android.util.SparseIntArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070068import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090069import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080070
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060071import com.android.internal.util.DumpUtils;
RoboErik01fe6612014-02-13 14:19:04 -080072import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070073import com.android.server.Watchdog;
74import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080075
RoboErika278ea72014-04-24 14:49:01 -070076import java.io.FileDescriptor;
77import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080078import java.util.ArrayList;
RoboErike7880d82014-04-30 12:48:25 -070079import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080080
81/**
82 * System implementation of MediaSessionManager
83 */
RoboErika278ea72014-04-24 14:49:01 -070084public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080085 private static final String TAG = "MediaSessionService";
Jaewan Kim92dea332017-02-02 11:52:08 +090086 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090087 // Leave log for key event always.
88 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080089
RoboErik418c10c2014-05-19 09:25:25 -070090 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080091 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070092
RoboErik01fe6612014-02-13 14:19:04 -080093 private final SessionManagerImpl mSessionManagerImpl;
94
Jaewan Kima7dce192017-02-16 17:10:54 +090095 // Keeps the full user id for each user.
96 private final SparseIntArray mFullUserIds = new SparseIntArray();
97 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070098 private final ArrayList<SessionsListenerRecord> mSessionsListeners
99 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800100 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700101 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700102 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900103 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800104
RoboErik9a9d0b52014-05-20 14:53:39 -0700105 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700106 private IAudioService mAudioService;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700107 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700108 private SettingsObserver mSettingsObserver;
Julia Reynoldsb852e562017-06-06 16:14:18 -0400109 private INotificationManager mNotificationManager;
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900110 private boolean mHasFeatureLeanback;
RoboErik9a9d0b52014-05-20 14:53:39 -0700111
Jaewan Kima7dce192017-02-16 17:10:54 +0900112 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
113 // It's always not null after the MediaSessionService is started.
114 private FullUserRecord mCurrentFullUserRecord;
115 private MediaSessionRecord mGlobalPrioritySession;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000116 private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
RoboErike7880d82014-04-30 12:48:25 -0700117
RoboErik19c95182014-06-23 15:38:48 -0700118 // Used to notify system UI when remote volume was changed. TODO find a
119 // better way to handle this.
120 private IRemoteVolumeController mRvc;
121
RoboErik01fe6612014-02-13 14:19:04 -0800122 public MediaSessionService(Context context) {
123 super(context);
124 mSessionManagerImpl = new SessionManagerImpl();
RoboErik8a2cfc32014-05-16 11:19:38 -0700125 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
126 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900127 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
Julia Reynoldsb852e562017-06-06 16:14:18 -0400128 mNotificationManager = INotificationManager.Stub.asInterface(
129 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
RoboErik01fe6612014-02-13 14:19:04 -0800130 }
131
132 @Override
133 public void onStart() {
134 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700135 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700136 mKeyguardManager =
137 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700138 mAudioService = getAudioService();
Sungsoo Lim875e6972017-11-03 02:22:35 +0000139 mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
140 mAudioPlayerStateMonitor.registerListener(
141 new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
Sungsoob3658562017-05-22 17:10:44 +0900142 @Override
Sungsoo Lim875e6972017-11-03 02:22:35 +0000143 public void onAudioPlayerStateChanged(
144 int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
145 if (config == null || !config.isActive() || config.getPlayerType()
146 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
147 return;
148 }
Sungsoob3658562017-05-22 17:10:44 +0900149 synchronized (mLock) {
150 FullUserRecord user =
151 getFullUserRecordLocked(UserHandle.getUserId(uid));
152 if (user != null) {
153 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
Jaewan Kim92dea332017-02-02 11:52:08 +0900154 }
Sungsoob3658562017-05-22 17:10:44 +0900155 }
156 }
Sungsoo Lim875e6972017-11-03 02:22:35 +0000157 }, null /* handler */);
158 mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700159 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700160 mSettingsObserver = new SettingsObserver();
161 mSettingsObserver.observe();
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900162 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
163 PackageManager.FEATURE_LEANBACK);
RoboErikc8f92d12015-01-05 16:48:07 -0800164
165 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700166 }
167
168 private IAudioService getAudioService() {
169 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
170 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700171 }
172
Jaewan Kima7dce192017-02-16 17:10:54 +0900173 private boolean isGlobalPriorityActiveLocked() {
174 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
175 }
176
RoboErika8f95142014-05-05 14:23:49 -0700177 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700178 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900179 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
Jaewan Kim101b4d52017-05-18 13:23:11 +0900180 if (user == null) {
181 Log.w(TAG, "Unknown session updated. Ignoring.");
RoboErik4646d282014-05-13 10:13:04 -0700182 return;
183 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900184 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900185 if (DEBUG_KEY_EVENT) {
186 Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
187 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900188 user.pushAddressedPlayerChangedLocked();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900189 } else {
190 if (!user.mPriorityStack.contains(record)) {
191 Log.w(TAG, "Unknown session updated. Ignoring.");
192 return;
193 }
194 user.mPriorityStack.onSessionStateChange(record);
Jaewan Kima7dce192017-02-16 17:10:54 +0900195 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900196 mHandler.postSessionsChanged(record.getUserId());
RoboErike7880d82014-04-30 12:48:25 -0700197 }
198 }
199
Jaewan Kimfa85b602017-10-10 16:49:58 +0900200 public void setGlobalPrioritySession(MediaSessionRecord record) {
201 synchronized (mLock) {
202 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
203 if (mGlobalPrioritySession != record) {
204 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
205 + " to " + record);
206 mGlobalPrioritySession = record;
207 if (user != null && user.mPriorityStack.contains(record)) {
208 // Handle the global priority session separately.
209 // Otherwise, it can be the media button session regardless of the active state
210 // because it or other system components might have been the lastly played media
211 // app.
212 user.mPriorityStack.removeSession(record);
213 }
214 }
215 }
216 }
217
Jaewan Kim101b4d52017-05-18 13:23:11 +0900218 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
Jaewan Kimda74a152017-10-03 23:58:11 +0900219 List<MediaSessionRecord> records = new ArrayList<>();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900220 if (userId == UserHandle.USER_ALL) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900221 int size = mUserRecords.size();
222 for (int i = 0; i < size; i++) {
223 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
224 }
225 } else {
226 FullUserRecord user = getFullUserRecordLocked(userId);
227 if (user == null) {
228 Log.w(TAG, "getSessions failed. Unknown user " + userId);
Jaewan Kimda74a152017-10-03 23:58:11 +0900229 return records;
Jaewan Kim101b4d52017-05-18 13:23:11 +0900230 }
Jaewan Kimda74a152017-10-03 23:58:11 +0900231 records.addAll(user.mPriorityStack.getActiveSessions(userId));
Jaewan Kim101b4d52017-05-18 13:23:11 +0900232 }
233
234 // Return global priority session at the first whenever it's asked.
235 if (isGlobalPriorityActiveLocked()
236 && (userId == UserHandle.USER_ALL
237 || userId == mGlobalPrioritySession.getUserId())) {
238 records.add(0, mGlobalPrioritySession);
239 }
240 return records;
241 }
242
RoboErik9c5b7cb2015-01-15 15:09:09 -0800243 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900244 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800245 */
246 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900247 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800248 return;
249 }
250 try {
251 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
252 } catch (Exception e) {
253 Log.wtf(TAG, "Error sending volume change to system UI.", e);
254 }
255 }
256
Jaewan Kim92dea332017-02-02 11:52:08 +0900257 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
RoboErika8f95142014-05-05 14:23:49 -0700258 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900259 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
260 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700261 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
262 return;
263 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900264 user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
RoboErika8f95142014-05-05 14:23:49 -0700265 }
266 }
267
RoboErik19c95182014-06-23 15:38:48 -0700268 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
269 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900270 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
271 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700272 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
273 return;
274 }
275 pushRemoteVolumeUpdateLocked(record.getUserId());
276 }
277 }
278
RoboErika278ea72014-04-24 14:49:01 -0700279 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900280 public void onStartUser(int userId) {
281 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700282 updateUser();
283 }
284
285 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900286 public void onSwitchUser(int userId) {
287 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700288 updateUser();
289 }
290
291 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900292 public void onStopUser(int userId) {
293 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700294 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900295 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700296 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900297 if (user.mFullUserId == userId) {
298 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
299 mUserRecords.remove(userId);
300 } else {
301 user.destroySessionsForUserLocked(userId);
302 }
RoboErik4646d282014-05-13 10:13:04 -0700303 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900304 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700305 }
306 }
307
308 @Override
RoboErika278ea72014-04-24 14:49:01 -0700309 public void monitor() {
310 synchronized (mLock) {
311 // Check for deadlock
312 }
313 }
314
RoboErik4646d282014-05-13 10:13:04 -0700315 protected void enforcePhoneStatePermission(int pid, int uid) {
316 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
317 != PackageManager.PERMISSION_GRANTED) {
318 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
319 }
320 }
321
RoboErik01fe6612014-02-13 14:19:04 -0800322 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700323 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800324 destroySessionLocked(session);
325 }
326 }
327
328 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700329 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800330 destroySessionLocked(session);
331 }
332 }
333
RoboErik4646d282014-05-13 10:13:04 -0700334 private void updateUser() {
335 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900336 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900337 mFullUserIds.clear();
338 List<UserInfo> allUsers = manager.getUsers();
339 if (allUsers != null) {
340 for (UserInfo userInfo : allUsers) {
341 if (userInfo.isManagedProfile()) {
342 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
343 } else {
344 mFullUserIds.put(userInfo.id, userInfo.id);
345 if (mUserRecords.get(userInfo.id) == null) {
346 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
347 }
348 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900349 }
RoboErik4646d282014-05-13 10:13:04 -0700350 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900351 // Ensure that the current full user exists.
352 int currentFullUserId = ActivityManager.getCurrentUser();
353 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
354 if (mCurrentFullUserRecord == null) {
355 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
356 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
357 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
358 }
359 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700360 }
361 }
362
RoboErik7aef77b2014-08-08 15:56:54 -0700363 private void updateActiveSessionListeners() {
364 synchronized (mLock) {
365 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
366 SessionsListenerRecord listener = mSessionsListeners.get(i);
367 try {
368 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
369 listener.mUserId);
370 } catch (SecurityException e) {
371 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
372 + " is no longer authorized. Disconnecting.");
373 mSessionsListeners.remove(i);
374 try {
375 listener.mListener
376 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
377 } catch (Exception e1) {
378 // ignore
379 }
380 }
381 }
382 }
383 }
384
RoboErik4646d282014-05-13 10:13:04 -0700385 /*
386 * When a session is removed several things need to happen.
387 * 1. We need to remove it from the relevant user.
388 * 2. We need to remove it from the priority stack.
389 * 3. We need to remove it from all sessions.
390 * 4. If this is the system priority session we need to clear it.
391 * 5. We need to unlink to death from the cb binder
392 * 6. We need to tell the session to do any final cleanup (onDestroy)
393 */
RoboErik01fe6612014-02-13 14:19:04 -0800394 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900395 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900396 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900397 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900398 FullUserRecord user = getFullUserRecordLocked(session.getUserId());
Jaewan Kima7dce192017-02-16 17:10:54 +0900399 if (mGlobalPrioritySession == session) {
400 mGlobalPrioritySession = null;
Jaewan Kim92dea332017-02-02 11:52:08 +0900401 if (session.isActive() && user != null) {
402 user.pushAddressedPlayerChangedLocked();
403 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900404 } else {
405 if (user != null) {
406 user.mPriorityStack.removeSession(session);
407 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900408 }
RoboErik4646d282014-05-13 10:13:04 -0700409
410 try {
411 session.getCallback().asBinder().unlinkToDeath(session, 0);
412 } catch (Exception e) {
413 // ignore exceptions while destroying a session.
414 }
415 session.onDestroy();
Jaewan Kim92dea332017-02-02 11:52:08 +0900416 mHandler.postSessionsChanged(session.getUserId());
RoboErik01fe6612014-02-13 14:19:04 -0800417 }
418
419 private void enforcePackageName(String packageName, int uid) {
420 if (TextUtils.isEmpty(packageName)) {
421 throw new IllegalArgumentException("packageName may not be empty");
422 }
423 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
424 final int packageCount = packages.length;
425 for (int i = 0; i < packageCount; i++) {
426 if (packageName.equals(packages[i])) {
427 return;
428 }
429 }
430 throw new IllegalArgumentException("packageName is not owned by the calling process");
431 }
432
RoboErike7880d82014-04-30 12:48:25 -0700433 /**
434 * Checks a caller's authorization to register an IRemoteControlDisplay.
435 * Authorization is granted if one of the following is true:
436 * <ul>
437 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
438 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700439 * <li>the caller's listener is one of the enabled notification listeners
440 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700441 * </ul>
442 */
RoboErika5b02322014-05-07 17:05:49 -0700443 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
444 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500445 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700446 if (getContext()
447 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
448 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700449 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
450 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700451 throw new SecurityException("Missing permission to control media.");
452 }
453 }
454
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500455 private boolean isCurrentVolumeController(int uid, int pid) {
456 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
457 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500458 }
459
460 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500461 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700462 throw new SecurityException("Only system ui may " + action);
463 }
464 }
465
RoboErika5b02322014-05-07 17:05:49 -0700466 /**
467 * This checks if the component is an enabled notification listener for the
468 * specified user. Enabled components may only operate on behalf of the user
469 * they're running as.
470 *
471 * @param compName The component that is enabled.
472 * @param userId The user id of the caller.
473 * @param forUserId The user id they're making the request on behalf of.
474 * @return True if the component is enabled, false otherwise
475 */
476 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
477 int forUserId) {
478 if (userId != forUserId) {
479 // You may not access another user's content as an enabled listener.
480 return false;
481 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700482 if (DEBUG) {
483 Log.d(TAG, "Checking if enabled notification listener " + compName);
484 }
RoboErike7880d82014-04-30 12:48:25 -0700485 if (compName != null) {
Julia Reynoldsb852e562017-06-06 16:14:18 -0400486 try {
487 return mNotificationManager.isNotificationListenerAccessGrantedForUser(
488 compName, userId);
489 } catch(RemoteException e) {
490 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
RoboErike7880d82014-04-30 12:48:25 -0700491 }
492 }
493 return false;
494 }
495
RoboErika5b02322014-05-07 17:05:49 -0700496 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700497 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800498 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700499 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800500 }
501 }
502
RoboErik4646d282014-05-13 10:13:04 -0700503 /*
504 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700505 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700506 * 2. It needs to be added to all sessions.
507 * 3. It needs to be added to the priority stack.
508 * 4. It needs to be added to the relevant user record.
509 */
RoboErika5b02322014-05-07 17:05:49 -0700510 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
511 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900512 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700513 if (user == null) {
514 Log.wtf(TAG, "Request from invalid user: " + userId);
515 throw new RuntimeException("Session request from invalid user.");
516 }
517
RoboErika5b02322014-05-07 17:05:49 -0700518 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
Jaewan Kim92dea332017-02-02 11:52:08 +0900519 callerPackageName, cb, tag, this, mHandler.getLooper());
RoboErik01fe6612014-02-13 14:19:04 -0800520 try {
521 cb.asBinder().linkToDeath(session, 0);
522 } catch (RemoteException e) {
523 throw new RuntimeException("Media Session owner died prematurely.", e);
524 }
RoboErik4646d282014-05-13 10:13:04 -0700525
Jaewan Kim101b4d52017-05-18 13:23:11 +0900526 user.mPriorityStack.addSession(session);
Jaewan Kim92dea332017-02-02 11:52:08 +0900527 mHandler.postSessionsChanged(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700528
RoboErik01fe6612014-02-13 14:19:04 -0800529 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900530 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800531 }
532 return session;
533 }
534
RoboErik2e7a9162014-06-04 16:53:45 -0700535 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
536 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800537 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700538 return i;
539 }
540 }
541 return -1;
542 }
543
RoboErik2e7a9162014-06-04 16:53:45 -0700544 private void pushSessionsChanged(int userId) {
545 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900546 FullUserRecord user = getFullUserRecordLocked(userId);
547 if (user == null) {
548 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
549 return;
550 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900551 List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700552 int size = records.size();
Jeff Browndba34ba2014-06-24 20:46:03 -0700553 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700554 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700555 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700556 }
RoboErik19c95182014-06-23 15:38:48 -0700557 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700558 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
559 SessionsListenerRecord record = mSessionsListeners.get(i);
560 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
561 try {
562 record.mListener.onActiveSessionsChanged(tokens);
563 } catch (RemoteException e) {
564 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
565 e);
566 mSessionsListeners.remove(i);
567 }
568 }
569 }
570 }
571 }
572
RoboErik19c95182014-06-23 15:38:48 -0700573 private void pushRemoteVolumeUpdateLocked(int userId) {
574 if (mRvc != null) {
575 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900576 FullUserRecord user = getFullUserRecordLocked(userId);
577 if (user == null) {
578 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
579 return;
580 }
581 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700582 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
583 } catch (RemoteException e) {
584 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
585 }
586 }
587 }
588
Jaewan Kim92dea332017-02-02 11:52:08 +0900589 /**
590 * Called when the media button receiver for the {@param record} is changed.
591 *
592 * @param record the media session whose media button receiver is updated.
593 */
594 public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
595 synchronized (mLock) {
596 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
597 MediaSessionRecord mediaButtonSession =
598 user.mPriorityStack.getMediaButtonSession();
599 if (record == mediaButtonSession) {
600 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
601 }
602 }
603 }
604
Jaewan Kim50269362016-12-23 11:22:02 +0900605 private String getCallingPackageName(int uid) {
606 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
607 if (packages != null && packages.length > 0) {
608 return packages[0];
609 }
610 return "";
611 }
612
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900613 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900614 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900615 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900616 } catch (RemoteException e) {
617 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
618 }
619 }
620
Jaewan Kima7dce192017-02-16 17:10:54 +0900621 private FullUserRecord getFullUserRecordLocked(int userId) {
622 int fullUserId = mFullUserIds.get(userId, -1);
623 if (fullUserId < 0) {
624 return null;
625 }
626 return mUserRecords.get(fullUserId);
627 }
628
RoboErik4646d282014-05-13 10:13:04 -0700629 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900630 * Information about a full user and its corresponding managed profiles.
631 *
632 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
633 * them when he/she presses a media/volume button. So keeping media sessions for them in one
634 * place makes more sense and increases the readability.</p>
635 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700636 */
Jaewan Kim92dea332017-02-02 11:52:08 +0900637 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
Jaewan Kima7dce192017-02-16 17:10:54 +0900638 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
639 private final int mFullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900640 private final MediaSessionStack mPriorityStack;
RoboErikb214efb2014-07-24 13:20:30 -0700641 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800642 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900643 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700644
Jaewan Kim50269362016-12-23 11:22:02 +0900645 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
646 private int mOnVolumeKeyLongPressListenerUid;
647 private KeyEvent mInitialDownVolumeKeyEvent;
648 private int mInitialDownVolumeStream;
649 private boolean mInitialDownMusicOnly;
650
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800651 private IOnMediaKeyListener mOnMediaKeyListener;
652 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900653 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800654
Jaewan Kima7dce192017-02-16 17:10:54 +0900655 public FullUserRecord(int fullUserId) {
656 mFullUserId = fullUserId;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000657 mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
Jaewan Kima7dce192017-02-16 17:10:54 +0900658 // Restore the remembered media button receiver before the boot.
659 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
660 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
661 if (mediaButtonReceiver == null) {
662 return;
663 }
664 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
665 if (tokens == null || tokens.length != 2) {
666 return;
667 }
668 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
669 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700670 }
671
Jaewan Kima7dce192017-02-16 17:10:54 +0900672 public void destroySessionsForUserLocked(int userId) {
Jaewan Kim92dea332017-02-02 11:52:08 +0900673 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
Jaewan Kima7dce192017-02-16 17:10:54 +0900674 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700675 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700676 }
677 }
678
RoboErik4646d282014-05-13 10:13:04 -0700679 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900680 pw.print(prefix + "Record for full_user=" + mFullUserId);
681 // Dump managed profile user ids associated with this user.
682 int size = mFullUserIds.size();
683 for (int i = 0; i < size; i++) {
684 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
685 && mFullUserIds.valueAt(i) == mFullUserId) {
686 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
687 }
688 }
689 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700690 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900691 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
692 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900693 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800694 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
695 pw.println(indent + "Media key listener package: " +
696 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900697 pw.println(indent + "Callback: " + mCallback);
698 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
699 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
700 mPriorityStack.dump(pw, indent);
701 }
702
Jaewan Kim92dea332017-02-02 11:52:08 +0900703 @Override
704 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
705 MediaSessionRecord newMediaButtonSession) {
706 if (DEBUG_KEY_EVENT) {
Jaewan Kim98e4aaf2017-05-12 17:06:47 +0900707 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
Jaewan Kim92dea332017-02-02 11:52:08 +0900708 }
709 synchronized (mLock) {
710 if (oldMediaButtonSession != null) {
711 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
712 }
713 if (newMediaButtonSession != null) {
714 rememberMediaButtonReceiverLocked(newMediaButtonSession);
715 mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
716 }
717 pushAddressedPlayerChangedLocked();
718 }
719 }
720
721 // Remember media button receiver and keep it in the persistent storage.
722 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900723 PendingIntent receiver = record.getMediaButtonReceiver();
Jaewan Kima7dce192017-02-16 17:10:54 +0900724 mLastMediaButtonReceiver = receiver;
Jaewan Kim92dea332017-02-02 11:52:08 +0900725 mRestoredMediaButtonReceiver = null;
726 String componentName = "";
727 if (receiver != null) {
728 ComponentName component = receiver.getIntent().getComponent();
729 if (component != null
730 && record.getPackageName().equals(component.getPackageName())) {
731 componentName = component.flattenToString();
732 }
RoboErik4646d282014-05-13 10:13:04 -0700733 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900734 Settings.Secure.putStringForUser(mContentResolver,
735 Settings.System.MEDIA_BUTTON_RECEIVER,
736 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
737 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700738 }
RoboErikc8f92d12015-01-05 16:48:07 -0800739
Jaewan Kima7dce192017-02-16 17:10:54 +0900740 private void pushAddressedPlayerChangedLocked() {
741 if (mCallback == null) {
742 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800743 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900744 try {
745 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
746 if (mediaButtonSession != null) {
747 mCallback.onAddressedPlayerChangedToMediaSession(
748 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
749 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
750 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
751 mCurrentFullUserRecord.mLastMediaButtonReceiver
752 .getIntent().getComponent());
753 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
754 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
755 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
756 }
757 } catch (RemoteException e) {
758 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
759 }
760 }
761
762 private MediaSessionRecord getMediaButtonSessionLocked() {
Jaewan Kim92dea332017-02-02 11:52:08 +0900763 return isGlobalPriorityActiveLocked()
764 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
RoboErikc8f92d12015-01-05 16:48:07 -0800765 }
RoboErik4646d282014-05-13 10:13:04 -0700766 }
767
RoboErik2e7a9162014-06-04 16:53:45 -0700768 final class SessionsListenerRecord implements IBinder.DeathRecipient {
769 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700770 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700771 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700772 private final int mPid;
773 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700774
RoboErik7aef77b2014-08-08 15:56:54 -0700775 public SessionsListenerRecord(IActiveSessionsListener listener,
776 ComponentName componentName,
777 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700778 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700779 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700780 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700781 mPid = pid;
782 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700783 }
784
785 @Override
786 public void binderDied() {
787 synchronized (mLock) {
788 mSessionsListeners.remove(this);
789 }
790 }
791 }
792
RoboErik7aef77b2014-08-08 15:56:54 -0700793 final class SettingsObserver extends ContentObserver {
794 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
795 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
796
797 private SettingsObserver() {
798 super(null);
799 }
800
801 private void observe() {
802 mContentResolver.registerContentObserver(mSecureSettingsUri,
803 false, this, UserHandle.USER_ALL);
804 }
805
806 @Override
807 public void onChange(boolean selfChange, Uri uri) {
808 updateActiveSessionListeners();
809 }
810 }
811
RoboErik07c70772014-03-20 13:33:52 -0700812 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700813 private static final String EXTRA_WAKELOCK_ACQUIRED =
814 "android.media.AudioService.WAKELOCK_ACQUIRED";
815 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
816
RoboErik9a9d0b52014-05-20 14:53:39 -0700817 private boolean mVoiceButtonDown = false;
818 private boolean mVoiceButtonHandled = false;
819
RoboErik07c70772014-03-20 13:33:52 -0700820 @Override
RoboErika5b02322014-05-07 17:05:49 -0700821 public ISession createSession(String packageName, ISessionCallback cb, String tag,
822 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800823 final int pid = Binder.getCallingPid();
824 final int uid = Binder.getCallingUid();
825 final long token = Binder.clearCallingIdentity();
826 try {
827 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700828 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
829 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800830 if (cb == null) {
831 throw new IllegalArgumentException("Controller callback cannot be null");
832 }
RoboErika5b02322014-05-07 17:05:49 -0700833 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
834 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700835 } finally {
836 Binder.restoreCallingIdentity(token);
837 }
838 }
839
840 @Override
RoboErika5b02322014-05-07 17:05:49 -0700841 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700842 final int pid = Binder.getCallingPid();
843 final int uid = Binder.getCallingUid();
844 final long token = Binder.clearCallingIdentity();
845
846 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700847 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700848 ArrayList<IBinder> binders = new ArrayList<IBinder>();
849 synchronized (mLock) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900850 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
851 for (MediaSessionRecord record : records) {
852 binders.add(record.getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700853 }
854 }
855 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800856 } finally {
857 Binder.restoreCallingIdentity(token);
858 }
859 }
RoboErika278ea72014-04-24 14:49:01 -0700860
RoboErik2e7a9162014-06-04 16:53:45 -0700861 @Override
862 public void addSessionsListener(IActiveSessionsListener listener,
863 ComponentName componentName, int userId) throws RemoteException {
864 final int pid = Binder.getCallingPid();
865 final int uid = Binder.getCallingUid();
866 final long token = Binder.clearCallingIdentity();
867
868 try {
869 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
870 synchronized (mLock) {
871 int index = findIndexOfSessionsListenerLocked(listener);
872 if (index != -1) {
873 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
874 return;
875 }
876 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700877 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700878 try {
879 listener.asBinder().linkToDeath(record, 0);
880 } catch (RemoteException e) {
881 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
882 return;
883 }
884 mSessionsListeners.add(record);
885 }
886 } finally {
887 Binder.restoreCallingIdentity(token);
888 }
889 }
890
891 @Override
892 public void removeSessionsListener(IActiveSessionsListener listener)
893 throws RemoteException {
894 synchronized (mLock) {
895 int index = findIndexOfSessionsListenerLocked(listener);
896 if (index != -1) {
897 SessionsListenerRecord record = mSessionsListeners.remove(index);
898 try {
899 record.mListener.asBinder().unlinkToDeath(record, 0);
900 } catch (Exception e) {
901 // ignore exceptions, the record is being removed
902 }
903 }
904 }
905 }
906
RoboErik8a2cfc32014-05-16 11:19:38 -0700907 /**
908 * Handles the dispatching of the media button events to one of the
909 * registered listeners, or if there was none, broadcast an
910 * ACTION_MEDIA_BUTTON intent to the rest of the system.
911 *
912 * @param keyEvent a non-null KeyEvent whose key code is one of the
913 * supported media buttons
914 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
915 * while this key event is dispatched.
916 */
917 @Override
918 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
919 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
920 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
921 return;
922 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700923
RoboErik8a2cfc32014-05-16 11:19:38 -0700924 final int pid = Binder.getCallingPid();
925 final int uid = Binder.getCallingUid();
926 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700927 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700928 if (DEBUG) {
929 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
930 + keyEvent);
931 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700932 if (!isUserSetupComplete()) {
933 // Global media key handling can have the side-effect of starting new
934 // activities which is undesirable while setup is in progress.
935 Slog.i(TAG, "Not dispatching media key event because user "
936 + "setup is in progress.");
937 return;
938 }
939
RoboErik8a2cfc32014-05-16 11:19:38 -0700940 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900941 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +0900942 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
943 // Prevent dispatching key event through reflection while the global
944 // priority session is active.
945 Slog.i(TAG, "Only the system can dispatch media key event "
946 + "to the global priority session.");
947 return;
948 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900949 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900950 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +0900951 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900952 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900953 }
954 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900955 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim98003d32017-02-24 18:33:04 +0900956 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
957 return;
958 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900959 Log.w(TAG, "Failed to send " + keyEvent
960 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900961 }
962 }
963 }
Jaewan Kim51255012017-02-24 16:19:14 +0900964 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800965 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700966 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +0900967 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700968 }
969 }
970 } finally {
971 Binder.restoreCallingIdentity(token);
972 }
973 }
974
RoboErika278ea72014-04-24 14:49:01 -0700975 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +0900976 public void setCallback(ICallback callback) {
977 final int pid = Binder.getCallingPid();
978 final int uid = Binder.getCallingUid();
979 final long token = Binder.clearCallingIdentity();
980 try {
Amith Yamasanie259ad22017-04-24 11:30:19 -0700981 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900982 throw new SecurityException("Only Bluetooth service processes can set"
983 + " Callback");
984 }
985 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900986 int userId = UserHandle.getUserId(uid);
987 FullUserRecord user = getFullUserRecordLocked(userId);
988 if (user == null || user.mFullUserId != userId) {
989 Log.w(TAG, "Only the full user can set the callback"
990 + ", userId=" + userId);
991 return;
992 }
993 user.mCallback = callback;
994 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +0900995 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900996 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900997 return;
998 }
999 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001000 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +09001001 new IBinder.DeathRecipient() {
1002 @Override
1003 public void binderDied() {
1004 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001005 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001006 }
1007 }
1008 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +09001009 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +09001010 } catch (RemoteException e) {
1011 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +09001012 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001013 }
1014 }
1015 } finally {
1016 Binder.restoreCallingIdentity(token);
1017 }
1018 }
1019
1020 @Override
Jaewan Kim50269362016-12-23 11:22:02 +09001021 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
1022 final int pid = Binder.getCallingPid();
1023 final int uid = Binder.getCallingUid();
1024 final long token = Binder.clearCallingIdentity();
1025 try {
1026 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
1027 if (getContext().checkPermission(
1028 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
1029 != PackageManager.PERMISSION_GRANTED) {
1030 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
1031 " permission.");
1032 }
1033
1034 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001035 int userId = UserHandle.getUserId(uid);
1036 FullUserRecord user = getFullUserRecordLocked(userId);
1037 if (user == null || user.mFullUserId != userId) {
1038 Log.w(TAG, "Only the full user can set the volume key long-press listener"
1039 + ", userId=" + userId);
1040 return;
1041 }
Jaewan Kim50269362016-12-23 11:22:02 +09001042 if (user.mOnVolumeKeyLongPressListener != null &&
1043 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001044 Log.w(TAG, "The volume key long-press listener cannot be reset"
1045 + " by another app , mOnVolumeKeyLongPressListener="
1046 + user.mOnVolumeKeyLongPressListenerUid
1047 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +09001048 return;
1049 }
1050
1051 user.mOnVolumeKeyLongPressListener = listener;
1052 user.mOnVolumeKeyLongPressListenerUid = uid;
1053
Jaewan Kima7dce192017-02-16 17:10:54 +09001054 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001055 + listener + " is set by " + getCallingPackageName(uid));
1056
1057 if (user.mOnVolumeKeyLongPressListener != null) {
1058 try {
1059 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1060 new IBinder.DeathRecipient() {
1061 @Override
1062 public void binderDied() {
1063 synchronized (mLock) {
1064 user.mOnVolumeKeyLongPressListener = null;
1065 }
1066 }
1067 }, 0);
1068 } catch (RemoteException e) {
1069 Log.w(TAG, "Failed to set death recipient "
1070 + user.mOnVolumeKeyLongPressListener);
1071 user.mOnVolumeKeyLongPressListener = null;
1072 }
1073 }
1074 }
1075 } finally {
1076 Binder.restoreCallingIdentity(token);
1077 }
1078 }
1079
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001080 @Override
1081 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1082 final int pid = Binder.getCallingPid();
1083 final int uid = Binder.getCallingUid();
1084 final long token = Binder.clearCallingIdentity();
1085 try {
1086 // Enforce SET_MEDIA_KEY_LISTENER permission.
1087 if (getContext().checkPermission(
1088 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1089 != PackageManager.PERMISSION_GRANTED) {
1090 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1091 " permission.");
1092 }
1093
1094 synchronized (mLock) {
1095 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001096 FullUserRecord user = getFullUserRecordLocked(userId);
1097 if (user == null || user.mFullUserId != userId) {
1098 Log.w(TAG, "Only the full user can set the media key listener"
1099 + ", userId=" + userId);
1100 return;
1101 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001102 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001103 Log.w(TAG, "The media key listener cannot be reset by another app. "
1104 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1105 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001106 return;
1107 }
1108
1109 user.mOnMediaKeyListener = listener;
1110 user.mOnMediaKeyListenerUid = uid;
1111
Jaewan Kima7dce192017-02-16 17:10:54 +09001112 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001113 + " is set by " + getCallingPackageName(uid));
1114
1115 if (user.mOnMediaKeyListener != null) {
1116 try {
1117 user.mOnMediaKeyListener.asBinder().linkToDeath(
1118 new IBinder.DeathRecipient() {
1119 @Override
1120 public void binderDied() {
1121 synchronized (mLock) {
1122 user.mOnMediaKeyListener = null;
1123 }
1124 }
1125 }, 0);
1126 } catch (RemoteException e) {
1127 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1128 user.mOnMediaKeyListener = null;
1129 }
1130 }
1131 }
1132 } finally {
1133 Binder.restoreCallingIdentity(token);
1134 }
1135 }
1136
Jaewan Kim50269362016-12-23 11:22:02 +09001137 /**
1138 * Handles the dispatching of the volume button events to one of the
1139 * registered listeners. If there's a volume key long-press listener and
1140 * there's no active global priority session, long-pressess will be sent to the
1141 * long-press listener instead of adjusting volume.
1142 *
1143 * @param keyEvent a non-null KeyEvent whose key code is one of the
1144 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1145 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1146 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1147 * @param stream stream type to adjust volume.
1148 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1149 */
1150 @Override
1151 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1152 if (keyEvent == null ||
1153 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1154 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1155 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1156 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1157 return;
1158 }
1159
1160 final int pid = Binder.getCallingPid();
1161 final int uid = Binder.getCallingUid();
1162 final long token = Binder.clearCallingIdentity();
1163
Jaewan Kimb2781e72017-03-02 09:57:09 +09001164 if (DEBUG_KEY_EVENT) {
Jaewan Kim50269362016-12-23 11:22:02 +09001165 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1166 + keyEvent);
1167 }
1168
1169 try {
1170 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001171 if (isGlobalPriorityActiveLocked()
1172 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001173 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1174 } else {
1175 // TODO: Consider the case when both volume up and down keys are pressed
1176 // at the same time.
1177 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1178 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001179 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001180 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1181 KeyEvent.obtain(keyEvent);
1182 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1183 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001184 mHandler.sendMessageDelayed(
1185 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001186 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1187 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001188 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001189 }
1190 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001191 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001192 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001193 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001194 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001195 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001196 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001197 }
1198 dispatchVolumeKeyLongPressLocked(keyEvent);
1199 }
1200 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001201 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001202 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
1203 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1204 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001205 // Short-press. Should change volume.
1206 dispatchVolumeKeyEventLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001207 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1208 mCurrentFullUserRecord.mInitialDownVolumeStream,
1209 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001210 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1211 } else {
1212 dispatchVolumeKeyLongPressLocked(keyEvent);
1213 }
1214 }
1215 }
1216 }
1217 } finally {
1218 Binder.restoreCallingIdentity(token);
1219 }
1220 }
1221
Jaewan Kim50269362016-12-23 11:22:02 +09001222 private void dispatchVolumeKeyEventLocked(
1223 KeyEvent keyEvent, int stream, boolean musicOnly) {
1224 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1225 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1226 int direction = 0;
1227 boolean isMute = false;
1228 switch (keyEvent.getKeyCode()) {
1229 case KeyEvent.KEYCODE_VOLUME_UP:
1230 direction = AudioManager.ADJUST_RAISE;
1231 break;
1232 case KeyEvent.KEYCODE_VOLUME_DOWN:
1233 direction = AudioManager.ADJUST_LOWER;
1234 break;
1235 case KeyEvent.KEYCODE_VOLUME_MUTE:
1236 isMute = true;
1237 break;
1238 }
1239 if (down || up) {
1240 int flags = AudioManager.FLAG_FROM_KEY;
1241 if (musicOnly) {
1242 // This flag is used when the screen is off to only affect active media.
1243 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1244 } else {
1245 // These flags are consistent with the home screen
1246 if (up) {
1247 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1248 } else {
1249 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1250 }
1251 }
1252 if (direction != 0) {
1253 // If this is action up we want to send a beep for non-music events
1254 if (up) {
1255 direction = 0;
1256 }
1257 dispatchAdjustVolumeLocked(stream, direction, flags);
1258 } else if (isMute) {
1259 if (down && keyEvent.getRepeatCount() == 0) {
1260 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1261 }
1262 }
1263 }
1264 }
1265
1266 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001267 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001268 final long token = Binder.clearCallingIdentity();
1269 try {
1270 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001271 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001272 }
1273 } finally {
1274 Binder.restoreCallingIdentity(token);
1275 }
1276 }
1277
1278 @Override
RoboErik19c95182014-06-23 15:38:48 -07001279 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1280 final int pid = Binder.getCallingPid();
1281 final int uid = Binder.getCallingUid();
1282 final long token = Binder.clearCallingIdentity();
1283 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001284 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001285 mRvc = rvc;
1286 } finally {
1287 Binder.restoreCallingIdentity(token);
1288 }
1289 }
1290
1291 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001292 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001293 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001294 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001295 }
RoboErikde9ba392014-09-26 12:51:01 -07001296 }
1297
1298 @Override
RoboErika278ea72014-04-24 14:49:01 -07001299 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001300 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
RoboErika278ea72014-04-24 14:49:01 -07001301
1302 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1303 pw.println();
1304
1305 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001306 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001307 pw.println("Global priority session is " + mGlobalPrioritySession);
Jaewan Kim101b4d52017-05-18 13:23:11 +09001308 if (mGlobalPrioritySession != null) {
1309 mGlobalPrioritySession.dump(pw, " ");
1310 }
RoboErik4646d282014-05-13 10:13:04 -07001311 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001312 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001313 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001314 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001315 }
Sungsoo Lim875e6972017-11-03 02:22:35 +00001316 mAudioPlayerStateMonitor.dump(getContext(), pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001317 }
1318 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001319
RoboErik2e7a9162014-06-04 16:53:45 -07001320 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1321 final int uid) {
1322 String packageName = null;
1323 if (componentName != null) {
1324 // If they gave us a component name verify they own the
1325 // package
1326 packageName = componentName.getPackageName();
1327 enforcePackageName(packageName, uid);
1328 }
1329 // Check that they can make calls on behalf of the user and
1330 // get the final user id
1331 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1332 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1333 // Check if they have the permissions or their component is
1334 // enabled for the user they're calling from.
1335 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1336 return resolvedUserId;
1337 }
1338
Jaewan Kim50269362016-12-23 11:22:02 +09001339 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001340 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1341 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001342
RoboErik9c785402014-11-11 16:52:26 -08001343 boolean preferSuggestedStream = false;
1344 if (isValidLocalStreamType(suggestedStream)
1345 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1346 preferSuggestedStream = true;
1347 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001348 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001349 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1350 + flags + ", suggestedStream=" + suggestedStream
1351 + ", preferSuggestedStream=" + preferSuggestedStream);
1352 }
RoboErik9c785402014-11-11 16:52:26 -08001353 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001354 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1355 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001356 if (DEBUG) {
1357 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001358 }
RoboErikb7c014c2014-07-22 15:58:22 -07001359 return;
RoboErik3c45c292014-07-08 16:47:31 -07001360 }
Shibin George19e84042016-06-14 20:42:13 +05301361
1362 // Execute mAudioService.adjustSuggestedStreamVolume() on
1363 // handler thread of MediaSessionService.
1364 // This will release the MediaSessionService.mLock sooner and avoid
1365 // a potential deadlock between MediaSessionService.mLock and
1366 // ActivityManagerService lock.
1367 mHandler.post(new Runnable() {
1368 @Override
1369 public void run() {
1370 try {
1371 String packageName = getContext().getOpPackageName();
1372 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1373 flags, packageName, TAG);
1374 } catch (RemoteException e) {
1375 Log.e(TAG, "Error adjusting default volume.", e);
Hyundo Moon739d6c22017-09-18 17:01:48 +09001376 } catch (IllegalArgumentException e) {
1377 Log.e(TAG, "Cannot adjust volume: direction=" + direction
1378 + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
1379 e);
Shibin George19e84042016-06-14 20:42:13 +05301380 }
1381 }
1382 });
RoboErikb69ffd42014-05-30 14:57:59 -07001383 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001384 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001385 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001386 }
1387 }
1388
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001389 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001390 int action = keyEvent.getAction();
1391 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1392 if (action == KeyEvent.ACTION_DOWN) {
1393 if (keyEvent.getRepeatCount() == 0) {
1394 mVoiceButtonDown = true;
1395 mVoiceButtonHandled = false;
1396 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1397 mVoiceButtonHandled = true;
1398 startVoiceInput(needWakeLock);
1399 }
1400 } else if (action == KeyEvent.ACTION_UP) {
1401 if (mVoiceButtonDown) {
1402 mVoiceButtonDown = false;
1403 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1404 // Resend the down then send this event through
1405 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001406 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1407 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001408 }
1409 }
1410 }
1411 }
1412
Jaewan Kim98003d32017-02-24 18:33:04 +09001413 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001414 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001415 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001416 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001417 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001418 }
1419 if (needWakeLock) {
1420 mKeyEventReceiver.aquireWakeLockLocked();
1421 }
Jaewan Kim50269362016-12-23 11:22:02 +09001422 // If we don't need a wakelock use -1 as the id so we won't release it later.
RoboErik9a9d0b52014-05-20 14:53:39 -07001423 session.sendMediaButton(keyEvent,
1424 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001425 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001426 getContext().getPackageName());
Jaewan Kima7dce192017-02-16 17:10:54 +09001427 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001428 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001429 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
1430 keyEvent,
Jaewan Kimbd16f452017-02-03 16:21:38 +09001431 new MediaSession.Token(session.getControllerBinder()));
1432 } catch (RemoteException e) {
1433 Log.w(TAG, "Failed to send callback", e);
1434 }
1435 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001436 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1437 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1438 if (needWakeLock) {
1439 mKeyEventReceiver.aquireWakeLockLocked();
1440 }
1441 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1442 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1443 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1444 try {
1445 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1446 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1447 if (DEBUG_KEY_EVENT) {
1448 Log.d(TAG, "Sending " + keyEvent
Jaewan Kim92dea332017-02-02 11:52:08 +09001449 + " to the last known PendingIntent " + receiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001450 }
1451 receiver.send(getContext(),
1452 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1453 mediaButtonIntent, mKeyEventReceiver, mHandler);
1454 if (mCurrentFullUserRecord.mCallback != null) {
1455 ComponentName componentName = mCurrentFullUserRecord
1456 .mLastMediaButtonReceiver.getIntent().getComponent();
1457 if (componentName != null) {
1458 mCurrentFullUserRecord.mCallback
1459 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1460 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001461 }
RoboErikc8f92d12015-01-05 16:48:07 -08001462 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001463 } else {
1464 ComponentName receiver =
1465 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1466 if (DEBUG_KEY_EVENT) {
1467 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1468 + receiver);
1469 }
1470 mediaButtonIntent.setComponent(receiver);
1471 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim92dea332017-02-02 11:52:08 +09001472 UserHandle.of(mCurrentFullUserRecord
1473 .mRestoredMediaButtonReceiverUserId));
Jaewan Kima7dce192017-02-16 17:10:54 +09001474 if (mCurrentFullUserRecord.mCallback != null) {
1475 mCurrentFullUserRecord.mCallback
1476 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1477 keyEvent, receiver);
1478 }
RoboErikb214efb2014-07-24 13:20:30 -07001479 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001480 } catch (CanceledException e) {
1481 Log.i(TAG, "Error sending key event to media button receiver "
1482 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1483 } catch (RemoteException e) {
1484 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001485 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001486 }
1487 }
1488
1489 private void startVoiceInput(boolean needWakeLock) {
1490 Intent voiceIntent = null;
1491 // select which type of search to launch:
1492 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1493 // - device locked or screen off: action is
1494 // ACTION_VOICE_SEARCH_HANDS_FREE
1495 // with EXTRA_SECURE set to true if the device is securely locked
1496 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1497 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1498 if (!isLocked && pm.isScreenOn()) {
1499 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1500 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1501 } else {
1502 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1503 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1504 isLocked && mKeyguardManager.isKeyguardSecure());
1505 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1506 }
1507 // start the search activity
1508 if (needWakeLock) {
1509 mMediaEventWakeLock.acquire();
1510 }
1511 try {
1512 if (voiceIntent != null) {
1513 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1514 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001515 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001516 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1517 }
1518 } catch (ActivityNotFoundException e) {
1519 Log.w(TAG, "No activity for search: " + e);
1520 } finally {
1521 if (needWakeLock) {
1522 mMediaEventWakeLock.release();
1523 }
1524 }
1525 }
1526
1527 private boolean isVoiceKey(int keyCode) {
Jaewan Kimba18d8e2017-05-12 17:37:57 +09001528 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
Jaewan Kimfdb612e2017-07-01 09:23:41 +09001529 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
RoboErik9a9d0b52014-05-20 14:53:39 -07001530 }
1531
Jeff Brown38d3feb2015-03-19 18:26:30 -07001532 private boolean isUserSetupComplete() {
1533 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1534 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1535 }
1536
RoboErik9c785402014-11-11 16:52:26 -08001537 // we only handle public stream types, which are 0-5
1538 private boolean isValidLocalStreamType(int streamType) {
1539 return streamType >= AudioManager.STREAM_VOICE_CALL
1540 && streamType <= AudioManager.STREAM_NOTIFICATION;
1541 }
1542
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001543 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1544 private KeyEvent mKeyEvent;
1545 private boolean mNeedWakeLock;
1546 private boolean mHandled;
1547
1548 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1549 super(mHandler);
1550 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1551 mKeyEvent = keyEvent;
1552 mNeedWakeLock = needWakeLock;
1553 }
1554
1555 @Override
1556 public void run() {
1557 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1558 dispatchMediaKeyEvent();
1559 }
1560
1561 @Override
1562 protected void onReceiveResult(int resultCode, Bundle resultData) {
1563 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1564 mHandled = true;
1565 mHandler.removeCallbacks(this);
1566 return;
1567 }
1568 dispatchMediaKeyEvent();
1569 }
1570
1571 private void dispatchMediaKeyEvent() {
1572 if (mHandled) {
1573 return;
1574 }
1575 mHandled = true;
1576 mHandler.removeCallbacks(this);
1577 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001578 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001579 && isVoiceKey(mKeyEvent.getKeyCode())) {
1580 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1581 } else {
1582 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1583 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001584 }
1585 }
1586 }
1587
RoboErik418c10c2014-05-19 09:25:25 -07001588 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1589
RoboErikb214efb2014-07-24 13:20:30 -07001590 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1591 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001592 private final Handler mHandler;
1593 private int mRefCount = 0;
1594 private int mLastTimeoutId = 0;
1595
1596 public KeyEventWakeLockReceiver(Handler handler) {
1597 super(handler);
1598 mHandler = handler;
1599 }
1600
1601 public void onTimeout() {
1602 synchronized (mLock) {
1603 if (mRefCount == 0) {
1604 // We've already released it, so just return
1605 return;
1606 }
1607 mLastTimeoutId++;
1608 mRefCount = 0;
1609 releaseWakeLockLocked();
1610 }
1611 }
1612
1613 public void aquireWakeLockLocked() {
1614 if (mRefCount == 0) {
1615 mMediaEventWakeLock.acquire();
1616 }
1617 mRefCount++;
1618 mHandler.removeCallbacks(this);
1619 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1620
1621 }
1622
1623 @Override
1624 public void run() {
1625 onTimeout();
1626 }
1627
RoboErik8a2cfc32014-05-16 11:19:38 -07001628 @Override
1629 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001630 if (resultCode < mLastTimeoutId) {
1631 // Ignore results from calls that were before the last
1632 // timeout, just in case.
1633 return;
1634 } else {
1635 synchronized (mLock) {
1636 if (mRefCount > 0) {
1637 mRefCount--;
1638 if (mRefCount == 0) {
1639 releaseWakeLockLocked();
1640 }
1641 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001642 }
1643 }
1644 }
RoboErik418c10c2014-05-19 09:25:25 -07001645
1646 private void releaseWakeLockLocked() {
1647 mMediaEventWakeLock.release();
1648 mHandler.removeCallbacks(this);
1649 }
RoboErikb214efb2014-07-24 13:20:30 -07001650
1651 @Override
1652 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1653 String resultData, Bundle resultExtras) {
1654 onReceiveResult(resultCode, null);
1655 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001656 };
1657
1658 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1659 @Override
1660 public void onReceive(Context context, Intent intent) {
1661 if (intent == null) {
1662 return;
1663 }
1664 Bundle extras = intent.getExtras();
1665 if (extras == null) {
1666 return;
1667 }
1668 synchronized (mLock) {
1669 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1670 && mMediaEventWakeLock.isHeld()) {
1671 mMediaEventWakeLock.release();
1672 }
1673 }
1674 }
1675 };
RoboErik01fe6612014-02-13 14:19:04 -08001676 }
1677
RoboErik2e7a9162014-06-04 16:53:45 -07001678 final class MessageHandler extends Handler {
1679 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001680 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
Jaewan Kim92dea332017-02-02 11:52:08 +09001681 private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
RoboErik2e7a9162014-06-04 16:53:45 -07001682
1683 @Override
1684 public void handleMessage(Message msg) {
1685 switch (msg.what) {
1686 case MSG_SESSIONS_CHANGED:
Jaewan Kim92dea332017-02-02 11:52:08 +09001687 pushSessionsChanged((int) msg.obj);
RoboErik2e7a9162014-06-04 16:53:45 -07001688 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001689 case MSG_VOLUME_INITIAL_DOWN:
1690 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001691 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001692 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1693 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1694 // Mark that the key is already handled.
1695 user.mInitialDownVolumeKeyEvent = null;
1696 }
1697 }
1698 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001699 }
1700 }
1701
Jaewan Kim92dea332017-02-02 11:52:08 +09001702 public void postSessionsChanged(int userId) {
1703 // Use object instead of the arguments when posting message to remove pending requests.
1704 Integer userIdInteger = mIntegerCache.get(userId);
1705 if (userIdInteger == null) {
1706 userIdInteger = Integer.valueOf(userId);
1707 mIntegerCache.put(userId, userIdInteger);
1708 }
1709 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
1710 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
RoboErik2e7a9162014-06-04 16:53:45 -07001711 }
1712 }
RoboErik01fe6612014-02-13 14:19:04 -08001713}