blob: 06f4f5e8b7c4d3defd5ab2108316b619793bf8ee [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
RoboErike7880d82014-04-30 12:48:25 -070019import android.app.ActivityManager;
Julia Reynoldsb852e562017-06-06 16:14:18 -040020import android.app.INotificationManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070021import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070022import android.app.PendingIntent;
23import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070024import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070025import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070026import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070027import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080028import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070029import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070030import android.content.pm.PackageManager;
Jaewan Kima7dce192017-02-16 17:10:54 +090031import android.content.pm.UserInfo;
RoboErik7aef77b2014-08-08 15:56:54 -070032import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070033import android.media.AudioManager;
Sungsoo Lim875e6972017-11-03 02:22:35 +000034import android.media.AudioPlaybackConfiguration;
RoboErik94c716e2014-09-14 13:54:31 -070035import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070036import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070037import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070038import android.media.session.IActiveSessionsListener;
Jaewan Kimbd16f452017-02-03 16:21:38 +090039import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080040import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090041import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070042import android.media.session.ISession;
43import android.media.session.ISessionCallback;
44import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070045import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080046import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070047import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080048import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070049import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080050import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070051import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070052import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070053import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090054import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080055import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070056import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070057import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070058import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090059import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070060import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070061import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080062import android.text.TextUtils;
63import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070064import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070065import android.util.SparseArray;
Jaewan Kima7dce192017-02-16 17:10:54 +090066import android.util.SparseIntArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070067import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090068import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080069
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060070import com.android.internal.util.DumpUtils;
RoboErik01fe6612014-02-13 14:19:04 -080071import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070072import com.android.server.Watchdog;
73import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080074
RoboErika278ea72014-04-24 14:49:01 -070075import java.io.FileDescriptor;
76import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080077import java.util.ArrayList;
RoboErike7880d82014-04-30 12:48:25 -070078import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080079
80/**
81 * System implementation of MediaSessionManager
82 */
RoboErika278ea72014-04-24 14:49:01 -070083public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080084 private static final String TAG = "MediaSessionService";
Jaewan Kim92dea332017-02-02 11:52:08 +090085 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090086 // Leave log for key event always.
87 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080088
RoboErik418c10c2014-05-19 09:25:25 -070089 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080090 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070091
RoboErik01fe6612014-02-13 14:19:04 -080092 private final SessionManagerImpl mSessionManagerImpl;
93
Jaewan Kima7dce192017-02-16 17:10:54 +090094 // Keeps the full user id for each user.
95 private final SparseIntArray mFullUserIds = new SparseIntArray();
96 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070097 private final ArrayList<SessionsListenerRecord> mSessionsListeners
98 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -080099 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700100 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700101 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900102 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800103
RoboErik9a9d0b52014-05-20 14:53:39 -0700104 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700105 private IAudioService mAudioService;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700106 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700107 private SettingsObserver mSettingsObserver;
Julia Reynoldsb852e562017-06-06 16:14:18 -0400108 private INotificationManager mNotificationManager;
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900109 private boolean mHasFeatureLeanback;
RoboErik9a9d0b52014-05-20 14:53:39 -0700110
Jaewan Kima7dce192017-02-16 17:10:54 +0900111 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
112 // It's always not null after the MediaSessionService is started.
113 private FullUserRecord mCurrentFullUserRecord;
114 private MediaSessionRecord mGlobalPrioritySession;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000115 private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
RoboErike7880d82014-04-30 12:48:25 -0700116
RoboErik19c95182014-06-23 15:38:48 -0700117 // Used to notify system UI when remote volume was changed. TODO find a
118 // better way to handle this.
119 private IRemoteVolumeController mRvc;
120
RoboErik01fe6612014-02-13 14:19:04 -0800121 public MediaSessionService(Context context) {
122 super(context);
123 mSessionManagerImpl = new SessionManagerImpl();
RoboErik8a2cfc32014-05-16 11:19:38 -0700124 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
125 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900126 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
Julia Reynoldsb852e562017-06-06 16:14:18 -0400127 mNotificationManager = INotificationManager.Stub.asInterface(
128 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
RoboErik01fe6612014-02-13 14:19:04 -0800129 }
130
131 @Override
132 public void onStart() {
133 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700134 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700135 mKeyguardManager =
136 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700137 mAudioService = getAudioService();
Sungsoo Lim875e6972017-11-03 02:22:35 +0000138 mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
139 mAudioPlayerStateMonitor.registerListener(
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900140 (config, isRemoved) -> {
141 if (isRemoved || !config.isActive() || config.getPlayerType()
142 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
143 return;
Jaewan Kim92dea332017-02-02 11:52:08 +0900144 }
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900145 synchronized (mLock) {
146 FullUserRecord user = getFullUserRecordLocked(
147 UserHandle.getUserId(config.getClientUid()));
148 if (user != null) {
149 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
150 }
151 }
152 }, null /* handler */);
Sungsoo Lim875e6972017-11-03 02:22:35 +0000153 mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700154 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700155 mSettingsObserver = new SettingsObserver();
156 mSettingsObserver.observe();
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900157 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
158 PackageManager.FEATURE_LEANBACK);
RoboErikc8f92d12015-01-05 16:48:07 -0800159
160 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700161 }
162
163 private IAudioService getAudioService() {
164 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
165 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700166 }
167
Jaewan Kima7dce192017-02-16 17:10:54 +0900168 private boolean isGlobalPriorityActiveLocked() {
169 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
170 }
171
RoboErika8f95142014-05-05 14:23:49 -0700172 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700173 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900174 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
Jaewan Kim101b4d52017-05-18 13:23:11 +0900175 if (user == null) {
176 Log.w(TAG, "Unknown session updated. Ignoring.");
RoboErik4646d282014-05-13 10:13:04 -0700177 return;
178 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900179 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900180 if (DEBUG_KEY_EVENT) {
181 Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
182 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900183 user.pushAddressedPlayerChangedLocked();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900184 } else {
185 if (!user.mPriorityStack.contains(record)) {
186 Log.w(TAG, "Unknown session updated. Ignoring.");
187 return;
188 }
189 user.mPriorityStack.onSessionStateChange(record);
Jaewan Kima7dce192017-02-16 17:10:54 +0900190 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900191 mHandler.postSessionsChanged(record.getUserId());
RoboErike7880d82014-04-30 12:48:25 -0700192 }
193 }
194
Jaewan Kimfa85b602017-10-10 16:49:58 +0900195 public void setGlobalPrioritySession(MediaSessionRecord record) {
196 synchronized (mLock) {
197 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
198 if (mGlobalPrioritySession != record) {
199 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
200 + " to " + record);
201 mGlobalPrioritySession = record;
202 if (user != null && user.mPriorityStack.contains(record)) {
203 // Handle the global priority session separately.
204 // Otherwise, it can be the media button session regardless of the active state
205 // because it or other system components might have been the lastly played media
206 // app.
207 user.mPriorityStack.removeSession(record);
208 }
209 }
210 }
211 }
212
Jaewan Kim101b4d52017-05-18 13:23:11 +0900213 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
Jaewan Kimda74a152017-10-03 23:58:11 +0900214 List<MediaSessionRecord> records = new ArrayList<>();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900215 if (userId == UserHandle.USER_ALL) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900216 int size = mUserRecords.size();
217 for (int i = 0; i < size; i++) {
218 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
219 }
220 } else {
221 FullUserRecord user = getFullUserRecordLocked(userId);
222 if (user == null) {
223 Log.w(TAG, "getSessions failed. Unknown user " + userId);
Jaewan Kimda74a152017-10-03 23:58:11 +0900224 return records;
Jaewan Kim101b4d52017-05-18 13:23:11 +0900225 }
Jaewan Kimda74a152017-10-03 23:58:11 +0900226 records.addAll(user.mPriorityStack.getActiveSessions(userId));
Jaewan Kim101b4d52017-05-18 13:23:11 +0900227 }
228
229 // Return global priority session at the first whenever it's asked.
230 if (isGlobalPriorityActiveLocked()
231 && (userId == UserHandle.USER_ALL
232 || userId == mGlobalPrioritySession.getUserId())) {
233 records.add(0, mGlobalPrioritySession);
234 }
235 return records;
236 }
237
RoboErik9c5b7cb2015-01-15 15:09:09 -0800238 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900239 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800240 */
241 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900242 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800243 return;
244 }
245 try {
246 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
247 } catch (Exception e) {
248 Log.wtf(TAG, "Error sending volume change to system UI.", e);
249 }
250 }
251
Jaewan Kim92dea332017-02-02 11:52:08 +0900252 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
RoboErika8f95142014-05-05 14:23:49 -0700253 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900254 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
255 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700256 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
257 return;
258 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900259 user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
RoboErika8f95142014-05-05 14:23:49 -0700260 }
261 }
262
RoboErik19c95182014-06-23 15:38:48 -0700263 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
264 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900265 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
266 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700267 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
268 return;
269 }
270 pushRemoteVolumeUpdateLocked(record.getUserId());
271 }
272 }
273
RoboErika278ea72014-04-24 14:49:01 -0700274 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900275 public void onStartUser(int userId) {
276 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700277 updateUser();
278 }
279
280 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900281 public void onSwitchUser(int userId) {
282 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700283 updateUser();
284 }
285
286 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900287 public void onStopUser(int userId) {
288 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700289 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900290 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700291 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900292 if (user.mFullUserId == userId) {
293 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
294 mUserRecords.remove(userId);
295 } else {
296 user.destroySessionsForUserLocked(userId);
297 }
RoboErik4646d282014-05-13 10:13:04 -0700298 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900299 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700300 }
301 }
302
303 @Override
RoboErika278ea72014-04-24 14:49:01 -0700304 public void monitor() {
305 synchronized (mLock) {
306 // Check for deadlock
307 }
308 }
309
RoboErik4646d282014-05-13 10:13:04 -0700310 protected void enforcePhoneStatePermission(int pid, int uid) {
311 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
312 != PackageManager.PERMISSION_GRANTED) {
313 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
314 }
315 }
316
RoboErik01fe6612014-02-13 14:19:04 -0800317 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700318 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800319 destroySessionLocked(session);
320 }
321 }
322
323 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700324 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800325 destroySessionLocked(session);
326 }
327 }
328
RoboErik4646d282014-05-13 10:13:04 -0700329 private void updateUser() {
330 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900331 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900332 mFullUserIds.clear();
333 List<UserInfo> allUsers = manager.getUsers();
334 if (allUsers != null) {
335 for (UserInfo userInfo : allUsers) {
336 if (userInfo.isManagedProfile()) {
337 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
338 } else {
339 mFullUserIds.put(userInfo.id, userInfo.id);
340 if (mUserRecords.get(userInfo.id) == null) {
341 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
342 }
343 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900344 }
RoboErik4646d282014-05-13 10:13:04 -0700345 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900346 // Ensure that the current full user exists.
347 int currentFullUserId = ActivityManager.getCurrentUser();
348 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
349 if (mCurrentFullUserRecord == null) {
350 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
351 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
352 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
353 }
354 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700355 }
356 }
357
RoboErik7aef77b2014-08-08 15:56:54 -0700358 private void updateActiveSessionListeners() {
359 synchronized (mLock) {
360 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
361 SessionsListenerRecord listener = mSessionsListeners.get(i);
362 try {
363 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
364 listener.mUserId);
365 } catch (SecurityException e) {
366 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
367 + " is no longer authorized. Disconnecting.");
368 mSessionsListeners.remove(i);
369 try {
370 listener.mListener
371 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
372 } catch (Exception e1) {
373 // ignore
374 }
375 }
376 }
377 }
378 }
379
RoboErik4646d282014-05-13 10:13:04 -0700380 /*
381 * When a session is removed several things need to happen.
382 * 1. We need to remove it from the relevant user.
383 * 2. We need to remove it from the priority stack.
384 * 3. We need to remove it from all sessions.
385 * 4. If this is the system priority session we need to clear it.
386 * 5. We need to unlink to death from the cb binder
387 * 6. We need to tell the session to do any final cleanup (onDestroy)
388 */
RoboErik01fe6612014-02-13 14:19:04 -0800389 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900390 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900391 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900392 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900393 FullUserRecord user = getFullUserRecordLocked(session.getUserId());
Jaewan Kima7dce192017-02-16 17:10:54 +0900394 if (mGlobalPrioritySession == session) {
395 mGlobalPrioritySession = null;
Jaewan Kim92dea332017-02-02 11:52:08 +0900396 if (session.isActive() && user != null) {
397 user.pushAddressedPlayerChangedLocked();
398 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900399 } else {
400 if (user != null) {
401 user.mPriorityStack.removeSession(session);
402 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900403 }
RoboErik4646d282014-05-13 10:13:04 -0700404
405 try {
406 session.getCallback().asBinder().unlinkToDeath(session, 0);
407 } catch (Exception e) {
408 // ignore exceptions while destroying a session.
409 }
410 session.onDestroy();
Jaewan Kim92dea332017-02-02 11:52:08 +0900411 mHandler.postSessionsChanged(session.getUserId());
RoboErik01fe6612014-02-13 14:19:04 -0800412 }
413
414 private void enforcePackageName(String packageName, int uid) {
415 if (TextUtils.isEmpty(packageName)) {
416 throw new IllegalArgumentException("packageName may not be empty");
417 }
418 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
419 final int packageCount = packages.length;
420 for (int i = 0; i < packageCount; i++) {
421 if (packageName.equals(packages[i])) {
422 return;
423 }
424 }
425 throw new IllegalArgumentException("packageName is not owned by the calling process");
426 }
427
RoboErike7880d82014-04-30 12:48:25 -0700428 /**
429 * Checks a caller's authorization to register an IRemoteControlDisplay.
430 * Authorization is granted if one of the following is true:
431 * <ul>
432 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
433 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700434 * <li>the caller's listener is one of the enabled notification listeners
435 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700436 * </ul>
437 */
RoboErika5b02322014-05-07 17:05:49 -0700438 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
439 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500440 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700441 if (getContext()
442 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
443 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700444 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
445 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700446 throw new SecurityException("Missing permission to control media.");
447 }
448 }
449
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500450 private boolean isCurrentVolumeController(int uid, int pid) {
451 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
452 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500453 }
454
455 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500456 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700457 throw new SecurityException("Only system ui may " + action);
458 }
459 }
460
RoboErika5b02322014-05-07 17:05:49 -0700461 /**
462 * This checks if the component is an enabled notification listener for the
463 * specified user. Enabled components may only operate on behalf of the user
464 * they're running as.
465 *
466 * @param compName The component that is enabled.
467 * @param userId The user id of the caller.
468 * @param forUserId The user id they're making the request on behalf of.
469 * @return True if the component is enabled, false otherwise
470 */
471 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
472 int forUserId) {
473 if (userId != forUserId) {
474 // You may not access another user's content as an enabled listener.
475 return false;
476 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700477 if (DEBUG) {
478 Log.d(TAG, "Checking if enabled notification listener " + compName);
479 }
RoboErike7880d82014-04-30 12:48:25 -0700480 if (compName != null) {
Julia Reynoldsb852e562017-06-06 16:14:18 -0400481 try {
482 return mNotificationManager.isNotificationListenerAccessGrantedForUser(
483 compName, userId);
484 } catch(RemoteException e) {
485 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
RoboErike7880d82014-04-30 12:48:25 -0700486 }
487 }
488 return false;
489 }
490
RoboErika5b02322014-05-07 17:05:49 -0700491 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700492 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800493 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700494 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800495 }
496 }
497
RoboErik4646d282014-05-13 10:13:04 -0700498 /*
499 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700500 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700501 * 2. It needs to be added to all sessions.
502 * 3. It needs to be added to the priority stack.
503 * 4. It needs to be added to the relevant user record.
504 */
RoboErika5b02322014-05-07 17:05:49 -0700505 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
506 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900507 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700508 if (user == null) {
509 Log.wtf(TAG, "Request from invalid user: " + userId);
510 throw new RuntimeException("Session request from invalid user.");
511 }
512
RoboErika5b02322014-05-07 17:05:49 -0700513 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
Jaewan Kim92dea332017-02-02 11:52:08 +0900514 callerPackageName, cb, tag, this, mHandler.getLooper());
RoboErik01fe6612014-02-13 14:19:04 -0800515 try {
516 cb.asBinder().linkToDeath(session, 0);
517 } catch (RemoteException e) {
518 throw new RuntimeException("Media Session owner died prematurely.", e);
519 }
RoboErik4646d282014-05-13 10:13:04 -0700520
Jaewan Kim101b4d52017-05-18 13:23:11 +0900521 user.mPriorityStack.addSession(session);
Jaewan Kim92dea332017-02-02 11:52:08 +0900522 mHandler.postSessionsChanged(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700523
RoboErik01fe6612014-02-13 14:19:04 -0800524 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900525 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800526 }
527 return session;
528 }
529
RoboErik2e7a9162014-06-04 16:53:45 -0700530 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
531 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800532 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700533 return i;
534 }
535 }
536 return -1;
537 }
538
RoboErik2e7a9162014-06-04 16:53:45 -0700539 private void pushSessionsChanged(int userId) {
540 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900541 FullUserRecord user = getFullUserRecordLocked(userId);
542 if (user == null) {
543 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
544 return;
545 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900546 List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700547 int size = records.size();
Jeff Browndba34ba2014-06-24 20:46:03 -0700548 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700549 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700550 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700551 }
RoboErik19c95182014-06-23 15:38:48 -0700552 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700553 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
554 SessionsListenerRecord record = mSessionsListeners.get(i);
555 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
556 try {
557 record.mListener.onActiveSessionsChanged(tokens);
558 } catch (RemoteException e) {
559 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
560 e);
561 mSessionsListeners.remove(i);
562 }
563 }
564 }
565 }
566 }
567
RoboErik19c95182014-06-23 15:38:48 -0700568 private void pushRemoteVolumeUpdateLocked(int userId) {
569 if (mRvc != null) {
570 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900571 FullUserRecord user = getFullUserRecordLocked(userId);
572 if (user == null) {
573 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
574 return;
575 }
576 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700577 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
578 } catch (RemoteException e) {
579 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
580 }
581 }
582 }
583
Jaewan Kim92dea332017-02-02 11:52:08 +0900584 /**
585 * Called when the media button receiver for the {@param record} is changed.
586 *
587 * @param record the media session whose media button receiver is updated.
588 */
589 public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
590 synchronized (mLock) {
591 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
592 MediaSessionRecord mediaButtonSession =
593 user.mPriorityStack.getMediaButtonSession();
594 if (record == mediaButtonSession) {
595 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
596 }
597 }
598 }
599
Jaewan Kim50269362016-12-23 11:22:02 +0900600 private String getCallingPackageName(int uid) {
601 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
602 if (packages != null && packages.length > 0) {
603 return packages[0];
604 }
605 return "";
606 }
607
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900608 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900609 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900610 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900611 } catch (RemoteException e) {
612 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
613 }
614 }
615
Jaewan Kima7dce192017-02-16 17:10:54 +0900616 private FullUserRecord getFullUserRecordLocked(int userId) {
617 int fullUserId = mFullUserIds.get(userId, -1);
618 if (fullUserId < 0) {
619 return null;
620 }
621 return mUserRecords.get(fullUserId);
622 }
623
RoboErik4646d282014-05-13 10:13:04 -0700624 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900625 * Information about a full user and its corresponding managed profiles.
626 *
627 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
628 * them when he/she presses a media/volume button. So keeping media sessions for them in one
629 * place makes more sense and increases the readability.</p>
630 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700631 */
Jaewan Kim92dea332017-02-02 11:52:08 +0900632 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
Jaewan Kima7dce192017-02-16 17:10:54 +0900633 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
634 private final int mFullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900635 private final MediaSessionStack mPriorityStack;
RoboErikb214efb2014-07-24 13:20:30 -0700636 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800637 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900638 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700639
Jaewan Kim50269362016-12-23 11:22:02 +0900640 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
641 private int mOnVolumeKeyLongPressListenerUid;
642 private KeyEvent mInitialDownVolumeKeyEvent;
643 private int mInitialDownVolumeStream;
644 private boolean mInitialDownMusicOnly;
645
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800646 private IOnMediaKeyListener mOnMediaKeyListener;
647 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900648 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800649
Jaewan Kima7dce192017-02-16 17:10:54 +0900650 public FullUserRecord(int fullUserId) {
651 mFullUserId = fullUserId;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000652 mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
Jaewan Kima7dce192017-02-16 17:10:54 +0900653 // Restore the remembered media button receiver before the boot.
654 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
655 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
656 if (mediaButtonReceiver == null) {
657 return;
658 }
659 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
660 if (tokens == null || tokens.length != 2) {
661 return;
662 }
663 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
664 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700665 }
666
Jaewan Kima7dce192017-02-16 17:10:54 +0900667 public void destroySessionsForUserLocked(int userId) {
Jaewan Kim92dea332017-02-02 11:52:08 +0900668 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
Jaewan Kima7dce192017-02-16 17:10:54 +0900669 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700670 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700671 }
672 }
673
RoboErik4646d282014-05-13 10:13:04 -0700674 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900675 pw.print(prefix + "Record for full_user=" + mFullUserId);
676 // Dump managed profile user ids associated with this user.
677 int size = mFullUserIds.size();
678 for (int i = 0; i < size; i++) {
679 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
680 && mFullUserIds.valueAt(i) == mFullUserId) {
681 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
682 }
683 }
684 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700685 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900686 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
687 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900688 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800689 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
690 pw.println(indent + "Media key listener package: " +
691 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900692 pw.println(indent + "Callback: " + mCallback);
693 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
694 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
695 mPriorityStack.dump(pw, indent);
696 }
697
Jaewan Kim92dea332017-02-02 11:52:08 +0900698 @Override
699 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
700 MediaSessionRecord newMediaButtonSession) {
701 if (DEBUG_KEY_EVENT) {
Jaewan Kim98e4aaf2017-05-12 17:06:47 +0900702 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
Jaewan Kim92dea332017-02-02 11:52:08 +0900703 }
704 synchronized (mLock) {
705 if (oldMediaButtonSession != null) {
706 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
707 }
708 if (newMediaButtonSession != null) {
709 rememberMediaButtonReceiverLocked(newMediaButtonSession);
710 mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
711 }
712 pushAddressedPlayerChangedLocked();
713 }
714 }
715
716 // Remember media button receiver and keep it in the persistent storage.
717 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900718 PendingIntent receiver = record.getMediaButtonReceiver();
Jaewan Kima7dce192017-02-16 17:10:54 +0900719 mLastMediaButtonReceiver = receiver;
Jaewan Kim92dea332017-02-02 11:52:08 +0900720 mRestoredMediaButtonReceiver = null;
721 String componentName = "";
722 if (receiver != null) {
723 ComponentName component = receiver.getIntent().getComponent();
724 if (component != null
725 && record.getPackageName().equals(component.getPackageName())) {
726 componentName = component.flattenToString();
727 }
RoboErik4646d282014-05-13 10:13:04 -0700728 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900729 Settings.Secure.putStringForUser(mContentResolver,
730 Settings.System.MEDIA_BUTTON_RECEIVER,
731 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
732 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700733 }
RoboErikc8f92d12015-01-05 16:48:07 -0800734
Jaewan Kima7dce192017-02-16 17:10:54 +0900735 private void pushAddressedPlayerChangedLocked() {
736 if (mCallback == null) {
737 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800738 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900739 try {
740 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
741 if (mediaButtonSession != null) {
742 mCallback.onAddressedPlayerChangedToMediaSession(
743 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
744 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
745 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
746 mCurrentFullUserRecord.mLastMediaButtonReceiver
747 .getIntent().getComponent());
748 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
749 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
750 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
751 }
752 } catch (RemoteException e) {
753 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
754 }
755 }
756
757 private MediaSessionRecord getMediaButtonSessionLocked() {
Jaewan Kim92dea332017-02-02 11:52:08 +0900758 return isGlobalPriorityActiveLocked()
759 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
RoboErikc8f92d12015-01-05 16:48:07 -0800760 }
RoboErik4646d282014-05-13 10:13:04 -0700761 }
762
RoboErik2e7a9162014-06-04 16:53:45 -0700763 final class SessionsListenerRecord implements IBinder.DeathRecipient {
764 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700765 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700766 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700767 private final int mPid;
768 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700769
RoboErik7aef77b2014-08-08 15:56:54 -0700770 public SessionsListenerRecord(IActiveSessionsListener listener,
771 ComponentName componentName,
772 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700773 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700774 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700775 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700776 mPid = pid;
777 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700778 }
779
780 @Override
781 public void binderDied() {
782 synchronized (mLock) {
783 mSessionsListeners.remove(this);
784 }
785 }
786 }
787
RoboErik7aef77b2014-08-08 15:56:54 -0700788 final class SettingsObserver extends ContentObserver {
789 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
790 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
791
792 private SettingsObserver() {
793 super(null);
794 }
795
796 private void observe() {
797 mContentResolver.registerContentObserver(mSecureSettingsUri,
798 false, this, UserHandle.USER_ALL);
799 }
800
801 @Override
802 public void onChange(boolean selfChange, Uri uri) {
803 updateActiveSessionListeners();
804 }
805 }
806
RoboErik07c70772014-03-20 13:33:52 -0700807 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700808 private static final String EXTRA_WAKELOCK_ACQUIRED =
809 "android.media.AudioService.WAKELOCK_ACQUIRED";
810 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
811
RoboErik9a9d0b52014-05-20 14:53:39 -0700812 private boolean mVoiceButtonDown = false;
813 private boolean mVoiceButtonHandled = false;
814
RoboErik07c70772014-03-20 13:33:52 -0700815 @Override
RoboErika5b02322014-05-07 17:05:49 -0700816 public ISession createSession(String packageName, ISessionCallback cb, String tag,
817 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800818 final int pid = Binder.getCallingPid();
819 final int uid = Binder.getCallingUid();
820 final long token = Binder.clearCallingIdentity();
821 try {
822 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700823 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
824 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800825 if (cb == null) {
826 throw new IllegalArgumentException("Controller callback cannot be null");
827 }
RoboErika5b02322014-05-07 17:05:49 -0700828 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
829 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700830 } finally {
831 Binder.restoreCallingIdentity(token);
832 }
833 }
834
835 @Override
RoboErika5b02322014-05-07 17:05:49 -0700836 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700837 final int pid = Binder.getCallingPid();
838 final int uid = Binder.getCallingUid();
839 final long token = Binder.clearCallingIdentity();
840
841 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700842 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700843 ArrayList<IBinder> binders = new ArrayList<IBinder>();
844 synchronized (mLock) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900845 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
846 for (MediaSessionRecord record : records) {
847 binders.add(record.getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700848 }
849 }
850 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800851 } finally {
852 Binder.restoreCallingIdentity(token);
853 }
854 }
RoboErika278ea72014-04-24 14:49:01 -0700855
RoboErik2e7a9162014-06-04 16:53:45 -0700856 @Override
857 public void addSessionsListener(IActiveSessionsListener listener,
858 ComponentName componentName, int userId) throws RemoteException {
859 final int pid = Binder.getCallingPid();
860 final int uid = Binder.getCallingUid();
861 final long token = Binder.clearCallingIdentity();
862
863 try {
864 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
865 synchronized (mLock) {
866 int index = findIndexOfSessionsListenerLocked(listener);
867 if (index != -1) {
868 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
869 return;
870 }
871 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700872 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700873 try {
874 listener.asBinder().linkToDeath(record, 0);
875 } catch (RemoteException e) {
876 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
877 return;
878 }
879 mSessionsListeners.add(record);
880 }
881 } finally {
882 Binder.restoreCallingIdentity(token);
883 }
884 }
885
886 @Override
887 public void removeSessionsListener(IActiveSessionsListener listener)
888 throws RemoteException {
889 synchronized (mLock) {
890 int index = findIndexOfSessionsListenerLocked(listener);
891 if (index != -1) {
892 SessionsListenerRecord record = mSessionsListeners.remove(index);
893 try {
894 record.mListener.asBinder().unlinkToDeath(record, 0);
895 } catch (Exception e) {
896 // ignore exceptions, the record is being removed
897 }
898 }
899 }
900 }
901
RoboErik8a2cfc32014-05-16 11:19:38 -0700902 /**
903 * Handles the dispatching of the media button events to one of the
904 * registered listeners, or if there was none, broadcast an
905 * ACTION_MEDIA_BUTTON intent to the rest of the system.
906 *
907 * @param keyEvent a non-null KeyEvent whose key code is one of the
908 * supported media buttons
909 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
910 * while this key event is dispatched.
911 */
912 @Override
913 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
914 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
915 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
916 return;
917 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700918
RoboErik8a2cfc32014-05-16 11:19:38 -0700919 final int pid = Binder.getCallingPid();
920 final int uid = Binder.getCallingUid();
921 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700922 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700923 if (DEBUG) {
924 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
925 + keyEvent);
926 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700927 if (!isUserSetupComplete()) {
928 // Global media key handling can have the side-effect of starting new
929 // activities which is undesirable while setup is in progress.
930 Slog.i(TAG, "Not dispatching media key event because user "
931 + "setup is in progress.");
932 return;
933 }
934
RoboErik8a2cfc32014-05-16 11:19:38 -0700935 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900936 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +0900937 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
938 // Prevent dispatching key event through reflection while the global
939 // priority session is active.
940 Slog.i(TAG, "Only the system can dispatch media key event "
941 + "to the global priority session.");
942 return;
943 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900944 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900945 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +0900946 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900947 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900948 }
949 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900950 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim98003d32017-02-24 18:33:04 +0900951 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
952 return;
953 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900954 Log.w(TAG, "Failed to send " + keyEvent
955 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900956 }
957 }
958 }
Jaewan Kim51255012017-02-24 16:19:14 +0900959 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800960 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700961 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +0900962 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700963 }
964 }
965 } finally {
966 Binder.restoreCallingIdentity(token);
967 }
968 }
969
RoboErika278ea72014-04-24 14:49:01 -0700970 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +0900971 public void setCallback(ICallback callback) {
972 final int pid = Binder.getCallingPid();
973 final int uid = Binder.getCallingUid();
974 final long token = Binder.clearCallingIdentity();
975 try {
Amith Yamasanie259ad22017-04-24 11:30:19 -0700976 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900977 throw new SecurityException("Only Bluetooth service processes can set"
978 + " Callback");
979 }
980 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900981 int userId = UserHandle.getUserId(uid);
982 FullUserRecord user = getFullUserRecordLocked(userId);
983 if (user == null || user.mFullUserId != userId) {
984 Log.w(TAG, "Only the full user can set the callback"
985 + ", userId=" + userId);
986 return;
987 }
988 user.mCallback = callback;
989 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +0900990 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900991 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900992 return;
993 }
994 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900995 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +0900996 new IBinder.DeathRecipient() {
997 @Override
998 public void binderDied() {
999 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001000 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001001 }
1002 }
1003 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +09001004 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +09001005 } catch (RemoteException e) {
1006 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +09001007 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001008 }
1009 }
1010 } finally {
1011 Binder.restoreCallingIdentity(token);
1012 }
1013 }
1014
1015 @Override
Jaewan Kim50269362016-12-23 11:22:02 +09001016 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
1017 final int pid = Binder.getCallingPid();
1018 final int uid = Binder.getCallingUid();
1019 final long token = Binder.clearCallingIdentity();
1020 try {
1021 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
1022 if (getContext().checkPermission(
1023 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
1024 != PackageManager.PERMISSION_GRANTED) {
1025 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
1026 " permission.");
1027 }
1028
1029 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001030 int userId = UserHandle.getUserId(uid);
1031 FullUserRecord user = getFullUserRecordLocked(userId);
1032 if (user == null || user.mFullUserId != userId) {
1033 Log.w(TAG, "Only the full user can set the volume key long-press listener"
1034 + ", userId=" + userId);
1035 return;
1036 }
Jaewan Kim50269362016-12-23 11:22:02 +09001037 if (user.mOnVolumeKeyLongPressListener != null &&
1038 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001039 Log.w(TAG, "The volume key long-press listener cannot be reset"
1040 + " by another app , mOnVolumeKeyLongPressListener="
1041 + user.mOnVolumeKeyLongPressListenerUid
1042 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +09001043 return;
1044 }
1045
1046 user.mOnVolumeKeyLongPressListener = listener;
1047 user.mOnVolumeKeyLongPressListenerUid = uid;
1048
Jaewan Kima7dce192017-02-16 17:10:54 +09001049 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001050 + listener + " is set by " + getCallingPackageName(uid));
1051
1052 if (user.mOnVolumeKeyLongPressListener != null) {
1053 try {
1054 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1055 new IBinder.DeathRecipient() {
1056 @Override
1057 public void binderDied() {
1058 synchronized (mLock) {
1059 user.mOnVolumeKeyLongPressListener = null;
1060 }
1061 }
1062 }, 0);
1063 } catch (RemoteException e) {
1064 Log.w(TAG, "Failed to set death recipient "
1065 + user.mOnVolumeKeyLongPressListener);
1066 user.mOnVolumeKeyLongPressListener = null;
1067 }
1068 }
1069 }
1070 } finally {
1071 Binder.restoreCallingIdentity(token);
1072 }
1073 }
1074
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001075 @Override
1076 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1077 final int pid = Binder.getCallingPid();
1078 final int uid = Binder.getCallingUid();
1079 final long token = Binder.clearCallingIdentity();
1080 try {
1081 // Enforce SET_MEDIA_KEY_LISTENER permission.
1082 if (getContext().checkPermission(
1083 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1084 != PackageManager.PERMISSION_GRANTED) {
1085 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1086 " permission.");
1087 }
1088
1089 synchronized (mLock) {
1090 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001091 FullUserRecord user = getFullUserRecordLocked(userId);
1092 if (user == null || user.mFullUserId != userId) {
1093 Log.w(TAG, "Only the full user can set the media key listener"
1094 + ", userId=" + userId);
1095 return;
1096 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001097 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001098 Log.w(TAG, "The media key listener cannot be reset by another app. "
1099 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1100 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001101 return;
1102 }
1103
1104 user.mOnMediaKeyListener = listener;
1105 user.mOnMediaKeyListenerUid = uid;
1106
Jaewan Kima7dce192017-02-16 17:10:54 +09001107 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001108 + " is set by " + getCallingPackageName(uid));
1109
1110 if (user.mOnMediaKeyListener != null) {
1111 try {
1112 user.mOnMediaKeyListener.asBinder().linkToDeath(
1113 new IBinder.DeathRecipient() {
1114 @Override
1115 public void binderDied() {
1116 synchronized (mLock) {
1117 user.mOnMediaKeyListener = null;
1118 }
1119 }
1120 }, 0);
1121 } catch (RemoteException e) {
1122 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1123 user.mOnMediaKeyListener = null;
1124 }
1125 }
1126 }
1127 } finally {
1128 Binder.restoreCallingIdentity(token);
1129 }
1130 }
1131
Jaewan Kim50269362016-12-23 11:22:02 +09001132 /**
1133 * Handles the dispatching of the volume button events to one of the
1134 * registered listeners. If there's a volume key long-press listener and
1135 * there's no active global priority session, long-pressess will be sent to the
1136 * long-press listener instead of adjusting volume.
1137 *
1138 * @param keyEvent a non-null KeyEvent whose key code is one of the
1139 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1140 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1141 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1142 * @param stream stream type to adjust volume.
1143 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1144 */
1145 @Override
1146 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1147 if (keyEvent == null ||
1148 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1149 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1150 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1151 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1152 return;
1153 }
1154
1155 final int pid = Binder.getCallingPid();
1156 final int uid = Binder.getCallingUid();
1157 final long token = Binder.clearCallingIdentity();
1158
Jaewan Kimb2781e72017-03-02 09:57:09 +09001159 if (DEBUG_KEY_EVENT) {
Jaewan Kim50269362016-12-23 11:22:02 +09001160 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1161 + keyEvent);
1162 }
1163
1164 try {
1165 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001166 if (isGlobalPriorityActiveLocked()
1167 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001168 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1169 } else {
1170 // TODO: Consider the case when both volume up and down keys are pressed
1171 // at the same time.
1172 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1173 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001174 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001175 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1176 KeyEvent.obtain(keyEvent);
1177 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1178 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001179 mHandler.sendMessageDelayed(
1180 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001181 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1182 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001183 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001184 }
1185 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001186 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001187 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001188 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001189 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001190 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001191 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001192 }
1193 dispatchVolumeKeyLongPressLocked(keyEvent);
1194 }
1195 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001196 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001197 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
1198 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1199 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001200 // Short-press. Should change volume.
1201 dispatchVolumeKeyEventLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001202 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1203 mCurrentFullUserRecord.mInitialDownVolumeStream,
1204 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001205 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1206 } else {
1207 dispatchVolumeKeyLongPressLocked(keyEvent);
1208 }
1209 }
1210 }
1211 }
1212 } finally {
1213 Binder.restoreCallingIdentity(token);
1214 }
1215 }
1216
Jaewan Kim50269362016-12-23 11:22:02 +09001217 private void dispatchVolumeKeyEventLocked(
1218 KeyEvent keyEvent, int stream, boolean musicOnly) {
1219 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1220 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1221 int direction = 0;
1222 boolean isMute = false;
1223 switch (keyEvent.getKeyCode()) {
1224 case KeyEvent.KEYCODE_VOLUME_UP:
1225 direction = AudioManager.ADJUST_RAISE;
1226 break;
1227 case KeyEvent.KEYCODE_VOLUME_DOWN:
1228 direction = AudioManager.ADJUST_LOWER;
1229 break;
1230 case KeyEvent.KEYCODE_VOLUME_MUTE:
1231 isMute = true;
1232 break;
1233 }
1234 if (down || up) {
1235 int flags = AudioManager.FLAG_FROM_KEY;
1236 if (musicOnly) {
1237 // This flag is used when the screen is off to only affect active media.
1238 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1239 } else {
1240 // These flags are consistent with the home screen
1241 if (up) {
1242 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1243 } else {
1244 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1245 }
1246 }
1247 if (direction != 0) {
1248 // If this is action up we want to send a beep for non-music events
1249 if (up) {
1250 direction = 0;
1251 }
1252 dispatchAdjustVolumeLocked(stream, direction, flags);
1253 } else if (isMute) {
1254 if (down && keyEvent.getRepeatCount() == 0) {
1255 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1256 }
1257 }
1258 }
1259 }
1260
1261 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001262 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001263 final long token = Binder.clearCallingIdentity();
1264 try {
1265 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001266 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001267 }
1268 } finally {
1269 Binder.restoreCallingIdentity(token);
1270 }
1271 }
1272
1273 @Override
RoboErik19c95182014-06-23 15:38:48 -07001274 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1275 final int pid = Binder.getCallingPid();
1276 final int uid = Binder.getCallingUid();
1277 final long token = Binder.clearCallingIdentity();
1278 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001279 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001280 mRvc = rvc;
1281 } finally {
1282 Binder.restoreCallingIdentity(token);
1283 }
1284 }
1285
1286 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001287 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001288 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001289 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001290 }
RoboErikde9ba392014-09-26 12:51:01 -07001291 }
1292
1293 @Override
RoboErika278ea72014-04-24 14:49:01 -07001294 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001295 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
RoboErika278ea72014-04-24 14:49:01 -07001296
1297 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1298 pw.println();
1299
1300 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001301 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001302 pw.println("Global priority session is " + mGlobalPrioritySession);
Jaewan Kim101b4d52017-05-18 13:23:11 +09001303 if (mGlobalPrioritySession != null) {
1304 mGlobalPrioritySession.dump(pw, " ");
1305 }
RoboErik4646d282014-05-13 10:13:04 -07001306 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001307 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001308 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001309 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001310 }
Sungsoo Lim875e6972017-11-03 02:22:35 +00001311 mAudioPlayerStateMonitor.dump(getContext(), pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001312 }
1313 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001314
RoboErik2e7a9162014-06-04 16:53:45 -07001315 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1316 final int uid) {
1317 String packageName = null;
1318 if (componentName != null) {
1319 // If they gave us a component name verify they own the
1320 // package
1321 packageName = componentName.getPackageName();
1322 enforcePackageName(packageName, uid);
1323 }
1324 // Check that they can make calls on behalf of the user and
1325 // get the final user id
1326 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1327 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1328 // Check if they have the permissions or their component is
1329 // enabled for the user they're calling from.
1330 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1331 return resolvedUserId;
1332 }
1333
Jaewan Kim50269362016-12-23 11:22:02 +09001334 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001335 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1336 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001337
RoboErik9c785402014-11-11 16:52:26 -08001338 boolean preferSuggestedStream = false;
1339 if (isValidLocalStreamType(suggestedStream)
1340 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1341 preferSuggestedStream = true;
1342 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001343 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001344 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1345 + flags + ", suggestedStream=" + suggestedStream
1346 + ", preferSuggestedStream=" + preferSuggestedStream);
1347 }
RoboErik9c785402014-11-11 16:52:26 -08001348 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001349 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1350 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001351 if (DEBUG) {
1352 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001353 }
RoboErikb7c014c2014-07-22 15:58:22 -07001354 return;
RoboErik3c45c292014-07-08 16:47:31 -07001355 }
Shibin George19e84042016-06-14 20:42:13 +05301356
1357 // Execute mAudioService.adjustSuggestedStreamVolume() on
1358 // handler thread of MediaSessionService.
1359 // This will release the MediaSessionService.mLock sooner and avoid
1360 // a potential deadlock between MediaSessionService.mLock and
1361 // ActivityManagerService lock.
1362 mHandler.post(new Runnable() {
1363 @Override
1364 public void run() {
1365 try {
1366 String packageName = getContext().getOpPackageName();
1367 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1368 flags, packageName, TAG);
1369 } catch (RemoteException e) {
1370 Log.e(TAG, "Error adjusting default volume.", e);
Hyundo Moon739d6c22017-09-18 17:01:48 +09001371 } catch (IllegalArgumentException e) {
1372 Log.e(TAG, "Cannot adjust volume: direction=" + direction
1373 + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
1374 e);
Shibin George19e84042016-06-14 20:42:13 +05301375 }
1376 }
1377 });
RoboErikb69ffd42014-05-30 14:57:59 -07001378 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001379 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001380 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001381 }
1382 }
1383
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001384 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001385 int action = keyEvent.getAction();
1386 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1387 if (action == KeyEvent.ACTION_DOWN) {
1388 if (keyEvent.getRepeatCount() == 0) {
1389 mVoiceButtonDown = true;
1390 mVoiceButtonHandled = false;
1391 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1392 mVoiceButtonHandled = true;
1393 startVoiceInput(needWakeLock);
1394 }
1395 } else if (action == KeyEvent.ACTION_UP) {
1396 if (mVoiceButtonDown) {
1397 mVoiceButtonDown = false;
1398 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1399 // Resend the down then send this event through
1400 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001401 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1402 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001403 }
1404 }
1405 }
1406 }
1407
Jaewan Kim98003d32017-02-24 18:33:04 +09001408 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001409 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001410 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001411 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001412 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001413 }
1414 if (needWakeLock) {
1415 mKeyEventReceiver.aquireWakeLockLocked();
1416 }
Jaewan Kim50269362016-12-23 11:22:02 +09001417 // 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 -07001418 session.sendMediaButton(keyEvent,
1419 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001420 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001421 getContext().getPackageName());
Jaewan Kima7dce192017-02-16 17:10:54 +09001422 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001423 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001424 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
1425 keyEvent,
Jaewan Kimbd16f452017-02-03 16:21:38 +09001426 new MediaSession.Token(session.getControllerBinder()));
1427 } catch (RemoteException e) {
1428 Log.w(TAG, "Failed to send callback", e);
1429 }
1430 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001431 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1432 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1433 if (needWakeLock) {
1434 mKeyEventReceiver.aquireWakeLockLocked();
1435 }
1436 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1437 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1438 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1439 try {
1440 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1441 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1442 if (DEBUG_KEY_EVENT) {
1443 Log.d(TAG, "Sending " + keyEvent
Jaewan Kim92dea332017-02-02 11:52:08 +09001444 + " to the last known PendingIntent " + receiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001445 }
1446 receiver.send(getContext(),
1447 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1448 mediaButtonIntent, mKeyEventReceiver, mHandler);
1449 if (mCurrentFullUserRecord.mCallback != null) {
1450 ComponentName componentName = mCurrentFullUserRecord
1451 .mLastMediaButtonReceiver.getIntent().getComponent();
1452 if (componentName != null) {
1453 mCurrentFullUserRecord.mCallback
1454 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1455 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001456 }
RoboErikc8f92d12015-01-05 16:48:07 -08001457 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001458 } else {
1459 ComponentName receiver =
1460 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1461 if (DEBUG_KEY_EVENT) {
1462 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1463 + receiver);
1464 }
1465 mediaButtonIntent.setComponent(receiver);
1466 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim92dea332017-02-02 11:52:08 +09001467 UserHandle.of(mCurrentFullUserRecord
1468 .mRestoredMediaButtonReceiverUserId));
Jaewan Kima7dce192017-02-16 17:10:54 +09001469 if (mCurrentFullUserRecord.mCallback != null) {
1470 mCurrentFullUserRecord.mCallback
1471 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1472 keyEvent, receiver);
1473 }
RoboErikb214efb2014-07-24 13:20:30 -07001474 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001475 } catch (CanceledException e) {
1476 Log.i(TAG, "Error sending key event to media button receiver "
1477 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1478 } catch (RemoteException e) {
1479 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001480 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001481 }
1482 }
1483
1484 private void startVoiceInput(boolean needWakeLock) {
1485 Intent voiceIntent = null;
1486 // select which type of search to launch:
1487 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1488 // - device locked or screen off: action is
1489 // ACTION_VOICE_SEARCH_HANDS_FREE
1490 // with EXTRA_SECURE set to true if the device is securely locked
1491 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1492 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1493 if (!isLocked && pm.isScreenOn()) {
1494 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1495 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1496 } else {
1497 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1498 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1499 isLocked && mKeyguardManager.isKeyguardSecure());
1500 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1501 }
1502 // start the search activity
1503 if (needWakeLock) {
1504 mMediaEventWakeLock.acquire();
1505 }
1506 try {
1507 if (voiceIntent != null) {
1508 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1509 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001510 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001511 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1512 }
1513 } catch (ActivityNotFoundException e) {
1514 Log.w(TAG, "No activity for search: " + e);
1515 } finally {
1516 if (needWakeLock) {
1517 mMediaEventWakeLock.release();
1518 }
1519 }
1520 }
1521
1522 private boolean isVoiceKey(int keyCode) {
Jaewan Kimba18d8e2017-05-12 17:37:57 +09001523 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
Jaewan Kimfdb612e2017-07-01 09:23:41 +09001524 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
RoboErik9a9d0b52014-05-20 14:53:39 -07001525 }
1526
Jeff Brown38d3feb2015-03-19 18:26:30 -07001527 private boolean isUserSetupComplete() {
1528 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1529 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1530 }
1531
RoboErik9c785402014-11-11 16:52:26 -08001532 // we only handle public stream types, which are 0-5
1533 private boolean isValidLocalStreamType(int streamType) {
1534 return streamType >= AudioManager.STREAM_VOICE_CALL
1535 && streamType <= AudioManager.STREAM_NOTIFICATION;
1536 }
1537
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001538 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1539 private KeyEvent mKeyEvent;
1540 private boolean mNeedWakeLock;
1541 private boolean mHandled;
1542
1543 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1544 super(mHandler);
1545 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1546 mKeyEvent = keyEvent;
1547 mNeedWakeLock = needWakeLock;
1548 }
1549
1550 @Override
1551 public void run() {
1552 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1553 dispatchMediaKeyEvent();
1554 }
1555
1556 @Override
1557 protected void onReceiveResult(int resultCode, Bundle resultData) {
1558 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1559 mHandled = true;
1560 mHandler.removeCallbacks(this);
1561 return;
1562 }
1563 dispatchMediaKeyEvent();
1564 }
1565
1566 private void dispatchMediaKeyEvent() {
1567 if (mHandled) {
1568 return;
1569 }
1570 mHandled = true;
1571 mHandler.removeCallbacks(this);
1572 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001573 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001574 && isVoiceKey(mKeyEvent.getKeyCode())) {
1575 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1576 } else {
1577 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1578 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001579 }
1580 }
1581 }
1582
RoboErik418c10c2014-05-19 09:25:25 -07001583 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1584
RoboErikb214efb2014-07-24 13:20:30 -07001585 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1586 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001587 private final Handler mHandler;
1588 private int mRefCount = 0;
1589 private int mLastTimeoutId = 0;
1590
1591 public KeyEventWakeLockReceiver(Handler handler) {
1592 super(handler);
1593 mHandler = handler;
1594 }
1595
1596 public void onTimeout() {
1597 synchronized (mLock) {
1598 if (mRefCount == 0) {
1599 // We've already released it, so just return
1600 return;
1601 }
1602 mLastTimeoutId++;
1603 mRefCount = 0;
1604 releaseWakeLockLocked();
1605 }
1606 }
1607
1608 public void aquireWakeLockLocked() {
1609 if (mRefCount == 0) {
1610 mMediaEventWakeLock.acquire();
1611 }
1612 mRefCount++;
1613 mHandler.removeCallbacks(this);
1614 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1615
1616 }
1617
1618 @Override
1619 public void run() {
1620 onTimeout();
1621 }
1622
RoboErik8a2cfc32014-05-16 11:19:38 -07001623 @Override
1624 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001625 if (resultCode < mLastTimeoutId) {
1626 // Ignore results from calls that were before the last
1627 // timeout, just in case.
1628 return;
1629 } else {
1630 synchronized (mLock) {
1631 if (mRefCount > 0) {
1632 mRefCount--;
1633 if (mRefCount == 0) {
1634 releaseWakeLockLocked();
1635 }
1636 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001637 }
1638 }
1639 }
RoboErik418c10c2014-05-19 09:25:25 -07001640
1641 private void releaseWakeLockLocked() {
1642 mMediaEventWakeLock.release();
1643 mHandler.removeCallbacks(this);
1644 }
RoboErikb214efb2014-07-24 13:20:30 -07001645
1646 @Override
1647 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1648 String resultData, Bundle resultExtras) {
1649 onReceiveResult(resultCode, null);
1650 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001651 };
1652
1653 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1654 @Override
1655 public void onReceive(Context context, Intent intent) {
1656 if (intent == null) {
1657 return;
1658 }
1659 Bundle extras = intent.getExtras();
1660 if (extras == null) {
1661 return;
1662 }
1663 synchronized (mLock) {
1664 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1665 && mMediaEventWakeLock.isHeld()) {
1666 mMediaEventWakeLock.release();
1667 }
1668 }
1669 }
1670 };
RoboErik01fe6612014-02-13 14:19:04 -08001671 }
1672
RoboErik2e7a9162014-06-04 16:53:45 -07001673 final class MessageHandler extends Handler {
1674 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001675 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
Jaewan Kim92dea332017-02-02 11:52:08 +09001676 private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
RoboErik2e7a9162014-06-04 16:53:45 -07001677
1678 @Override
1679 public void handleMessage(Message msg) {
1680 switch (msg.what) {
1681 case MSG_SESSIONS_CHANGED:
Jaewan Kim92dea332017-02-02 11:52:08 +09001682 pushSessionsChanged((int) msg.obj);
RoboErik2e7a9162014-06-04 16:53:45 -07001683 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001684 case MSG_VOLUME_INITIAL_DOWN:
1685 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001686 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001687 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1688 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1689 // Mark that the key is already handled.
1690 user.mInitialDownVolumeKeyEvent = null;
1691 }
1692 }
1693 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001694 }
1695 }
1696
Jaewan Kim92dea332017-02-02 11:52:08 +09001697 public void postSessionsChanged(int userId) {
1698 // Use object instead of the arguments when posting message to remove pending requests.
1699 Integer userIdInteger = mIntegerCache.get(userId);
1700 if (userIdInteger == null) {
1701 userIdInteger = Integer.valueOf(userId);
1702 mIntegerCache.put(userId, userIdInteger);
1703 }
1704 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
1705 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
RoboErik2e7a9162014-06-04 16:53:45 -07001706 }
1707 }
RoboErik01fe6612014-02-13 14:19:04 -08001708}