blob: b9a2d184aade0e530e203e417fd6f64b690b8908 [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;
John Spurlockeb69e242015-02-17 17:15:04 -050034import android.media.AudioManagerInternal;
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;
John Spurlockeb69e242015-02-17 17:15:04 -050071import com.android.server.LocalServices;
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;
John Spurlockeb69e242015-02-17 17:15:04 -0500107 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700108 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700109 private SettingsObserver mSettingsObserver;
Julia Reynoldsb852e562017-06-06 16:14:18 -0400110 private INotificationManager mNotificationManager;
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900111 private boolean mHasFeatureLeanback;
RoboErik9a9d0b52014-05-20 14:53:39 -0700112
Jaewan Kima7dce192017-02-16 17:10:54 +0900113 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
114 // It's always not null after the MediaSessionService is started.
115 private FullUserRecord mCurrentFullUserRecord;
116 private MediaSessionRecord mGlobalPrioritySession;
Jaewan Kim92dea332017-02-02 11:52:08 +0900117 private AudioPlaybackMonitor mAudioPlaybackMonitor;
RoboErike7880d82014-04-30 12:48:25 -0700118
RoboErik19c95182014-06-23 15:38:48 -0700119 // Used to notify system UI when remote volume was changed. TODO find a
120 // better way to handle this.
121 private IRemoteVolumeController mRvc;
122
RoboErik01fe6612014-02-13 14:19:04 -0800123 public MediaSessionService(Context context) {
124 super(context);
125 mSessionManagerImpl = new SessionManagerImpl();
RoboErik8a2cfc32014-05-16 11:19:38 -0700126 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
127 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900128 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
Julia Reynoldsb852e562017-06-06 16:14:18 -0400129 mNotificationManager = INotificationManager.Stub.asInterface(
130 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
RoboErik01fe6612014-02-13 14:19:04 -0800131 }
132
133 @Override
134 public void onStart() {
135 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700136 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700137 mKeyguardManager =
138 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700139 mAudioService = getAudioService();
Sungsoob3658562017-05-22 17:10:44 +0900140 mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(getContext(), mAudioService);
141 mAudioPlaybackMonitor.registerOnAudioPlaybackStartedListener(
Jaewan Kim92dea332017-02-02 11:52:08 +0900142 new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() {
Sungsoob3658562017-05-22 17:10:44 +0900143 @Override
144 public void onAudioPlaybackStarted(int uid) {
145 synchronized (mLock) {
146 FullUserRecord user =
147 getFullUserRecordLocked(UserHandle.getUserId(uid));
148 if (user != null) {
149 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
Jaewan Kim92dea332017-02-02 11:52:08 +0900150 }
Sungsoob3658562017-05-22 17:10:44 +0900151 }
152 }
153 });
John Spurlockeb69e242015-02-17 17:15:04 -0500154 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700155 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700156 mSettingsObserver = new SettingsObserver();
157 mSettingsObserver.observe();
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900158 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
159 PackageManager.FEATURE_LEANBACK);
RoboErikc8f92d12015-01-05 16:48:07 -0800160
161 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700162 }
163
164 private IAudioService getAudioService() {
165 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
166 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700167 }
168
Jaewan Kima7dce192017-02-16 17:10:54 +0900169 private boolean isGlobalPriorityActiveLocked() {
170 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
171 }
172
RoboErika8f95142014-05-05 14:23:49 -0700173 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700174 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900175 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
Jaewan Kim101b4d52017-05-18 13:23:11 +0900176 if (user == null) {
177 Log.w(TAG, "Unknown session updated. Ignoring.");
RoboErik4646d282014-05-13 10:13:04 -0700178 return;
179 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900180 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900181 if (mGlobalPrioritySession != record) {
182 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
183 + " to " + record);
184 mGlobalPrioritySession = record;
185 if (user != null && user.mPriorityStack.contains(record)) {
186 // Handle the global priority session separately.
187 // Otherwise, it will be the media button session even after it becomes
188 // inactive because it has been the lastly played media app.
189 user.mPriorityStack.removeSession(record);
190 }
191 }
192 if (DEBUG_KEY_EVENT) {
193 Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
194 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900195 user.pushAddressedPlayerChangedLocked();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900196 } else {
197 if (!user.mPriorityStack.contains(record)) {
198 Log.w(TAG, "Unknown session updated. Ignoring.");
199 return;
200 }
201 user.mPriorityStack.onSessionStateChange(record);
Jaewan Kima7dce192017-02-16 17:10:54 +0900202 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900203 mHandler.postSessionsChanged(record.getUserId());
RoboErike7880d82014-04-30 12:48:25 -0700204 }
205 }
206
Jaewan Kim101b4d52017-05-18 13:23:11 +0900207 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
208 List<MediaSessionRecord> records;
209 if (userId == UserHandle.USER_ALL) {
210 records = new ArrayList<>();
211 int size = mUserRecords.size();
212 for (int i = 0; i < size; i++) {
213 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
214 }
215 } else {
216 FullUserRecord user = getFullUserRecordLocked(userId);
217 if (user == null) {
218 Log.w(TAG, "getSessions failed. Unknown user " + userId);
219 return new ArrayList<>();
220 }
221 records = user.mPriorityStack.getActiveSessions(userId);
222 }
223
224 // Return global priority session at the first whenever it's asked.
225 if (isGlobalPriorityActiveLocked()
226 && (userId == UserHandle.USER_ALL
227 || userId == mGlobalPrioritySession.getUserId())) {
228 records.add(0, mGlobalPrioritySession);
229 }
230 return records;
231 }
232
RoboErik9c5b7cb2015-01-15 15:09:09 -0800233 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900234 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800235 */
236 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900237 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800238 return;
239 }
240 try {
241 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
242 } catch (Exception e) {
243 Log.wtf(TAG, "Error sending volume change to system UI.", e);
244 }
245 }
246
Jaewan Kim92dea332017-02-02 11:52:08 +0900247 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
RoboErika8f95142014-05-05 14:23:49 -0700248 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900249 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
250 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700251 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
252 return;
253 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900254 user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
RoboErika8f95142014-05-05 14:23:49 -0700255 }
256 }
257
RoboErik19c95182014-06-23 15:38:48 -0700258 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
259 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900260 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
261 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700262 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
263 return;
264 }
265 pushRemoteVolumeUpdateLocked(record.getUserId());
266 }
267 }
268
RoboErika278ea72014-04-24 14:49:01 -0700269 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900270 public void onStartUser(int userId) {
271 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700272 updateUser();
273 }
274
275 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900276 public void onSwitchUser(int userId) {
277 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700278 updateUser();
279 }
280
281 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900282 public void onStopUser(int userId) {
283 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700284 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900285 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700286 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900287 if (user.mFullUserId == userId) {
288 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
289 mUserRecords.remove(userId);
290 } else {
291 user.destroySessionsForUserLocked(userId);
292 }
RoboErik4646d282014-05-13 10:13:04 -0700293 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900294 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700295 }
296 }
297
298 @Override
RoboErika278ea72014-04-24 14:49:01 -0700299 public void monitor() {
300 synchronized (mLock) {
301 // Check for deadlock
302 }
303 }
304
RoboErik4646d282014-05-13 10:13:04 -0700305 protected void enforcePhoneStatePermission(int pid, int uid) {
306 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
307 != PackageManager.PERMISSION_GRANTED) {
308 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
309 }
310 }
311
RoboErik01fe6612014-02-13 14:19:04 -0800312 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700313 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800314 destroySessionLocked(session);
315 }
316 }
317
318 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700319 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800320 destroySessionLocked(session);
321 }
322 }
323
RoboErik4646d282014-05-13 10:13:04 -0700324 private void updateUser() {
325 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900326 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900327 mFullUserIds.clear();
328 List<UserInfo> allUsers = manager.getUsers();
329 if (allUsers != null) {
330 for (UserInfo userInfo : allUsers) {
331 if (userInfo.isManagedProfile()) {
332 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
333 } else {
334 mFullUserIds.put(userInfo.id, userInfo.id);
335 if (mUserRecords.get(userInfo.id) == null) {
336 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
337 }
338 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900339 }
RoboErik4646d282014-05-13 10:13:04 -0700340 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900341 // Ensure that the current full user exists.
342 int currentFullUserId = ActivityManager.getCurrentUser();
343 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
344 if (mCurrentFullUserRecord == null) {
345 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
346 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
347 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
348 }
349 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700350 }
351 }
352
RoboErik7aef77b2014-08-08 15:56:54 -0700353 private void updateActiveSessionListeners() {
354 synchronized (mLock) {
355 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
356 SessionsListenerRecord listener = mSessionsListeners.get(i);
357 try {
358 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
359 listener.mUserId);
360 } catch (SecurityException e) {
361 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
362 + " is no longer authorized. Disconnecting.");
363 mSessionsListeners.remove(i);
364 try {
365 listener.mListener
366 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
367 } catch (Exception e1) {
368 // ignore
369 }
370 }
371 }
372 }
373 }
374
RoboErik4646d282014-05-13 10:13:04 -0700375 /*
376 * When a session is removed several things need to happen.
377 * 1. We need to remove it from the relevant user.
378 * 2. We need to remove it from the priority stack.
379 * 3. We need to remove it from all sessions.
380 * 4. If this is the system priority session we need to clear it.
381 * 5. We need to unlink to death from the cb binder
382 * 6. We need to tell the session to do any final cleanup (onDestroy)
383 */
RoboErik01fe6612014-02-13 14:19:04 -0800384 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900385 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900386 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900387 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900388 FullUserRecord user = getFullUserRecordLocked(session.getUserId());
Jaewan Kima7dce192017-02-16 17:10:54 +0900389 if (mGlobalPrioritySession == session) {
390 mGlobalPrioritySession = null;
Jaewan Kim92dea332017-02-02 11:52:08 +0900391 if (session.isActive() && user != null) {
392 user.pushAddressedPlayerChangedLocked();
393 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900394 } else {
395 if (user != null) {
396 user.mPriorityStack.removeSession(session);
397 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900398 }
RoboErik4646d282014-05-13 10:13:04 -0700399
400 try {
401 session.getCallback().asBinder().unlinkToDeath(session, 0);
402 } catch (Exception e) {
403 // ignore exceptions while destroying a session.
404 }
405 session.onDestroy();
Jaewan Kim92dea332017-02-02 11:52:08 +0900406 mHandler.postSessionsChanged(session.getUserId());
RoboErik01fe6612014-02-13 14:19:04 -0800407 }
408
409 private void enforcePackageName(String packageName, int uid) {
410 if (TextUtils.isEmpty(packageName)) {
411 throw new IllegalArgumentException("packageName may not be empty");
412 }
413 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
414 final int packageCount = packages.length;
415 for (int i = 0; i < packageCount; i++) {
416 if (packageName.equals(packages[i])) {
417 return;
418 }
419 }
420 throw new IllegalArgumentException("packageName is not owned by the calling process");
421 }
422
RoboErike7880d82014-04-30 12:48:25 -0700423 /**
424 * Checks a caller's authorization to register an IRemoteControlDisplay.
425 * Authorization is granted if one of the following is true:
426 * <ul>
427 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
428 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700429 * <li>the caller's listener is one of the enabled notification listeners
430 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700431 * </ul>
432 */
RoboErika5b02322014-05-07 17:05:49 -0700433 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
434 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500435 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700436 if (getContext()
437 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
438 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700439 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
440 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700441 throw new SecurityException("Missing permission to control media.");
442 }
443 }
444
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500445 private boolean isCurrentVolumeController(int uid, int pid) {
446 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
447 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500448 }
449
450 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500451 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700452 throw new SecurityException("Only system ui may " + action);
453 }
454 }
455
RoboErika5b02322014-05-07 17:05:49 -0700456 /**
457 * This checks if the component is an enabled notification listener for the
458 * specified user. Enabled components may only operate on behalf of the user
459 * they're running as.
460 *
461 * @param compName The component that is enabled.
462 * @param userId The user id of the caller.
463 * @param forUserId The user id they're making the request on behalf of.
464 * @return True if the component is enabled, false otherwise
465 */
466 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
467 int forUserId) {
468 if (userId != forUserId) {
469 // You may not access another user's content as an enabled listener.
470 return false;
471 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700472 if (DEBUG) {
473 Log.d(TAG, "Checking if enabled notification listener " + compName);
474 }
RoboErike7880d82014-04-30 12:48:25 -0700475 if (compName != null) {
Julia Reynoldsb852e562017-06-06 16:14:18 -0400476 try {
477 return mNotificationManager.isNotificationListenerAccessGrantedForUser(
478 compName, userId);
479 } catch(RemoteException e) {
480 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
RoboErike7880d82014-04-30 12:48:25 -0700481 }
482 }
483 return false;
484 }
485
RoboErika5b02322014-05-07 17:05:49 -0700486 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700487 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800488 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700489 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800490 }
491 }
492
RoboErik4646d282014-05-13 10:13:04 -0700493 /*
494 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700495 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700496 * 2. It needs to be added to all sessions.
497 * 3. It needs to be added to the priority stack.
498 * 4. It needs to be added to the relevant user record.
499 */
RoboErika5b02322014-05-07 17:05:49 -0700500 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
501 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900502 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700503 if (user == null) {
504 Log.wtf(TAG, "Request from invalid user: " + userId);
505 throw new RuntimeException("Session request from invalid user.");
506 }
507
RoboErika5b02322014-05-07 17:05:49 -0700508 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
Jaewan Kim92dea332017-02-02 11:52:08 +0900509 callerPackageName, cb, tag, this, mHandler.getLooper());
RoboErik01fe6612014-02-13 14:19:04 -0800510 try {
511 cb.asBinder().linkToDeath(session, 0);
512 } catch (RemoteException e) {
513 throw new RuntimeException("Media Session owner died prematurely.", e);
514 }
RoboErik4646d282014-05-13 10:13:04 -0700515
Jaewan Kim101b4d52017-05-18 13:23:11 +0900516 user.mPriorityStack.addSession(session);
Jaewan Kim92dea332017-02-02 11:52:08 +0900517 mHandler.postSessionsChanged(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700518
RoboErik01fe6612014-02-13 14:19:04 -0800519 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900520 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800521 }
522 return session;
523 }
524
RoboErik2e7a9162014-06-04 16:53:45 -0700525 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
526 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800527 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700528 return i;
529 }
530 }
531 return -1;
532 }
533
RoboErik2e7a9162014-06-04 16:53:45 -0700534 private void pushSessionsChanged(int userId) {
535 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900536 FullUserRecord user = getFullUserRecordLocked(userId);
537 if (user == null) {
538 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
539 return;
540 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900541 List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700542 int size = records.size();
Jeff Browndba34ba2014-06-24 20:46:03 -0700543 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700544 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700545 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700546 }
RoboErik19c95182014-06-23 15:38:48 -0700547 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700548 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
549 SessionsListenerRecord record = mSessionsListeners.get(i);
550 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
551 try {
552 record.mListener.onActiveSessionsChanged(tokens);
553 } catch (RemoteException e) {
554 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
555 e);
556 mSessionsListeners.remove(i);
557 }
558 }
559 }
560 }
561 }
562
RoboErik19c95182014-06-23 15:38:48 -0700563 private void pushRemoteVolumeUpdateLocked(int userId) {
564 if (mRvc != null) {
565 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900566 FullUserRecord user = getFullUserRecordLocked(userId);
567 if (user == null) {
568 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
569 return;
570 }
571 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700572 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
573 } catch (RemoteException e) {
574 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
575 }
576 }
577 }
578
Jaewan Kim92dea332017-02-02 11:52:08 +0900579 /**
580 * Called when the media button receiver for the {@param record} is changed.
581 *
582 * @param record the media session whose media button receiver is updated.
583 */
584 public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
585 synchronized (mLock) {
586 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
587 MediaSessionRecord mediaButtonSession =
588 user.mPriorityStack.getMediaButtonSession();
589 if (record == mediaButtonSession) {
590 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
591 }
592 }
593 }
594
Jaewan Kim50269362016-12-23 11:22:02 +0900595 private String getCallingPackageName(int uid) {
596 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
597 if (packages != null && packages.length > 0) {
598 return packages[0];
599 }
600 return "";
601 }
602
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900603 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900604 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900605 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900606 } catch (RemoteException e) {
607 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
608 }
609 }
610
Jaewan Kima7dce192017-02-16 17:10:54 +0900611 private FullUserRecord getFullUserRecordLocked(int userId) {
612 int fullUserId = mFullUserIds.get(userId, -1);
613 if (fullUserId < 0) {
614 return null;
615 }
616 return mUserRecords.get(fullUserId);
617 }
618
RoboErik4646d282014-05-13 10:13:04 -0700619 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900620 * Information about a full user and its corresponding managed profiles.
621 *
622 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
623 * them when he/she presses a media/volume button. So keeping media sessions for them in one
624 * place makes more sense and increases the readability.</p>
625 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700626 */
Jaewan Kim92dea332017-02-02 11:52:08 +0900627 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
Jaewan Kima7dce192017-02-16 17:10:54 +0900628 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
629 private final int mFullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900630 private final MediaSessionStack mPriorityStack;
RoboErikb214efb2014-07-24 13:20:30 -0700631 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800632 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900633 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700634
Jaewan Kim50269362016-12-23 11:22:02 +0900635 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
636 private int mOnVolumeKeyLongPressListenerUid;
637 private KeyEvent mInitialDownVolumeKeyEvent;
638 private int mInitialDownVolumeStream;
639 private boolean mInitialDownMusicOnly;
640
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800641 private IOnMediaKeyListener mOnMediaKeyListener;
642 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900643 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800644
Jaewan Kima7dce192017-02-16 17:10:54 +0900645 public FullUserRecord(int fullUserId) {
646 mFullUserId = fullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900647 mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this);
Jaewan Kima7dce192017-02-16 17:10:54 +0900648 // Restore the remembered media button receiver before the boot.
649 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
650 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
651 if (mediaButtonReceiver == null) {
652 return;
653 }
654 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
655 if (tokens == null || tokens.length != 2) {
656 return;
657 }
658 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
659 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700660 }
661
Jaewan Kima7dce192017-02-16 17:10:54 +0900662 public void destroySessionsForUserLocked(int userId) {
Jaewan Kim92dea332017-02-02 11:52:08 +0900663 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
Jaewan Kima7dce192017-02-16 17:10:54 +0900664 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700665 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700666 }
667 }
668
RoboErik4646d282014-05-13 10:13:04 -0700669 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900670 pw.print(prefix + "Record for full_user=" + mFullUserId);
671 // Dump managed profile user ids associated with this user.
672 int size = mFullUserIds.size();
673 for (int i = 0; i < size; i++) {
674 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
675 && mFullUserIds.valueAt(i) == mFullUserId) {
676 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
677 }
678 }
679 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700680 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900681 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
682 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900683 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800684 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
685 pw.println(indent + "Media key listener package: " +
686 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900687 pw.println(indent + "Callback: " + mCallback);
688 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
689 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
690 mPriorityStack.dump(pw, indent);
691 }
692
Jaewan Kim92dea332017-02-02 11:52:08 +0900693 @Override
694 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
695 MediaSessionRecord newMediaButtonSession) {
696 if (DEBUG_KEY_EVENT) {
Jaewan Kim98e4aaf2017-05-12 17:06:47 +0900697 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
Jaewan Kim92dea332017-02-02 11:52:08 +0900698 }
699 synchronized (mLock) {
700 if (oldMediaButtonSession != null) {
701 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
702 }
703 if (newMediaButtonSession != null) {
704 rememberMediaButtonReceiverLocked(newMediaButtonSession);
705 mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
706 }
707 pushAddressedPlayerChangedLocked();
708 }
709 }
710
711 // Remember media button receiver and keep it in the persistent storage.
712 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900713 PendingIntent receiver = record.getMediaButtonReceiver();
Jaewan Kima7dce192017-02-16 17:10:54 +0900714 mLastMediaButtonReceiver = receiver;
Jaewan Kim92dea332017-02-02 11:52:08 +0900715 mRestoredMediaButtonReceiver = null;
716 String componentName = "";
717 if (receiver != null) {
718 ComponentName component = receiver.getIntent().getComponent();
719 if (component != null
720 && record.getPackageName().equals(component.getPackageName())) {
721 componentName = component.flattenToString();
722 }
RoboErik4646d282014-05-13 10:13:04 -0700723 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900724 Settings.Secure.putStringForUser(mContentResolver,
725 Settings.System.MEDIA_BUTTON_RECEIVER,
726 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
727 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700728 }
RoboErikc8f92d12015-01-05 16:48:07 -0800729
Jaewan Kima7dce192017-02-16 17:10:54 +0900730 private void pushAddressedPlayerChangedLocked() {
731 if (mCallback == null) {
732 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800733 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900734 try {
735 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
736 if (mediaButtonSession != null) {
737 mCallback.onAddressedPlayerChangedToMediaSession(
738 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
739 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
740 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
741 mCurrentFullUserRecord.mLastMediaButtonReceiver
742 .getIntent().getComponent());
743 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
744 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
745 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
746 }
747 } catch (RemoteException e) {
748 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
749 }
750 }
751
752 private MediaSessionRecord getMediaButtonSessionLocked() {
Jaewan Kim92dea332017-02-02 11:52:08 +0900753 return isGlobalPriorityActiveLocked()
754 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
RoboErikc8f92d12015-01-05 16:48:07 -0800755 }
RoboErik4646d282014-05-13 10:13:04 -0700756 }
757
RoboErik2e7a9162014-06-04 16:53:45 -0700758 final class SessionsListenerRecord implements IBinder.DeathRecipient {
759 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700760 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700761 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700762 private final int mPid;
763 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700764
RoboErik7aef77b2014-08-08 15:56:54 -0700765 public SessionsListenerRecord(IActiveSessionsListener listener,
766 ComponentName componentName,
767 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700768 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700769 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700770 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700771 mPid = pid;
772 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700773 }
774
775 @Override
776 public void binderDied() {
777 synchronized (mLock) {
778 mSessionsListeners.remove(this);
779 }
780 }
781 }
782
RoboErik7aef77b2014-08-08 15:56:54 -0700783 final class SettingsObserver extends ContentObserver {
784 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
785 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
786
787 private SettingsObserver() {
788 super(null);
789 }
790
791 private void observe() {
792 mContentResolver.registerContentObserver(mSecureSettingsUri,
793 false, this, UserHandle.USER_ALL);
794 }
795
796 @Override
797 public void onChange(boolean selfChange, Uri uri) {
798 updateActiveSessionListeners();
799 }
800 }
801
RoboErik07c70772014-03-20 13:33:52 -0700802 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700803 private static final String EXTRA_WAKELOCK_ACQUIRED =
804 "android.media.AudioService.WAKELOCK_ACQUIRED";
805 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
806
RoboErik9a9d0b52014-05-20 14:53:39 -0700807 private boolean mVoiceButtonDown = false;
808 private boolean mVoiceButtonHandled = false;
809
RoboErik07c70772014-03-20 13:33:52 -0700810 @Override
RoboErika5b02322014-05-07 17:05:49 -0700811 public ISession createSession(String packageName, ISessionCallback cb, String tag,
812 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800813 final int pid = Binder.getCallingPid();
814 final int uid = Binder.getCallingUid();
815 final long token = Binder.clearCallingIdentity();
816 try {
817 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700818 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
819 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800820 if (cb == null) {
821 throw new IllegalArgumentException("Controller callback cannot be null");
822 }
RoboErika5b02322014-05-07 17:05:49 -0700823 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
824 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700825 } finally {
826 Binder.restoreCallingIdentity(token);
827 }
828 }
829
830 @Override
RoboErika5b02322014-05-07 17:05:49 -0700831 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700832 final int pid = Binder.getCallingPid();
833 final int uid = Binder.getCallingUid();
834 final long token = Binder.clearCallingIdentity();
835
836 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700837 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700838 ArrayList<IBinder> binders = new ArrayList<IBinder>();
839 synchronized (mLock) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900840 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
841 for (MediaSessionRecord record : records) {
842 binders.add(record.getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700843 }
844 }
845 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800846 } finally {
847 Binder.restoreCallingIdentity(token);
848 }
849 }
RoboErika278ea72014-04-24 14:49:01 -0700850
RoboErik2e7a9162014-06-04 16:53:45 -0700851 @Override
852 public void addSessionsListener(IActiveSessionsListener listener,
853 ComponentName componentName, int userId) throws RemoteException {
854 final int pid = Binder.getCallingPid();
855 final int uid = Binder.getCallingUid();
856 final long token = Binder.clearCallingIdentity();
857
858 try {
859 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
860 synchronized (mLock) {
861 int index = findIndexOfSessionsListenerLocked(listener);
862 if (index != -1) {
863 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
864 return;
865 }
866 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700867 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700868 try {
869 listener.asBinder().linkToDeath(record, 0);
870 } catch (RemoteException e) {
871 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
872 return;
873 }
874 mSessionsListeners.add(record);
875 }
876 } finally {
877 Binder.restoreCallingIdentity(token);
878 }
879 }
880
881 @Override
882 public void removeSessionsListener(IActiveSessionsListener listener)
883 throws RemoteException {
884 synchronized (mLock) {
885 int index = findIndexOfSessionsListenerLocked(listener);
886 if (index != -1) {
887 SessionsListenerRecord record = mSessionsListeners.remove(index);
888 try {
889 record.mListener.asBinder().unlinkToDeath(record, 0);
890 } catch (Exception e) {
891 // ignore exceptions, the record is being removed
892 }
893 }
894 }
895 }
896
RoboErik8a2cfc32014-05-16 11:19:38 -0700897 /**
898 * Handles the dispatching of the media button events to one of the
899 * registered listeners, or if there was none, broadcast an
900 * ACTION_MEDIA_BUTTON intent to the rest of the system.
901 *
902 * @param keyEvent a non-null KeyEvent whose key code is one of the
903 * supported media buttons
904 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
905 * while this key event is dispatched.
906 */
907 @Override
908 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
909 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
910 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
911 return;
912 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700913
RoboErik8a2cfc32014-05-16 11:19:38 -0700914 final int pid = Binder.getCallingPid();
915 final int uid = Binder.getCallingUid();
916 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700917 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700918 if (DEBUG) {
919 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
920 + keyEvent);
921 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700922 if (!isUserSetupComplete()) {
923 // Global media key handling can have the side-effect of starting new
924 // activities which is undesirable while setup is in progress.
925 Slog.i(TAG, "Not dispatching media key event because user "
926 + "setup is in progress.");
927 return;
928 }
929
RoboErik8a2cfc32014-05-16 11:19:38 -0700930 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900931 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +0900932 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
933 // Prevent dispatching key event through reflection while the global
934 // priority session is active.
935 Slog.i(TAG, "Only the system can dispatch media key event "
936 + "to the global priority session.");
937 return;
938 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900939 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900940 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +0900941 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900942 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900943 }
944 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900945 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim98003d32017-02-24 18:33:04 +0900946 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
947 return;
948 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900949 Log.w(TAG, "Failed to send " + keyEvent
950 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900951 }
952 }
953 }
Jaewan Kim51255012017-02-24 16:19:14 +0900954 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800955 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700956 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +0900957 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700958 }
959 }
960 } finally {
961 Binder.restoreCallingIdentity(token);
962 }
963 }
964
RoboErika278ea72014-04-24 14:49:01 -0700965 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +0900966 public void setCallback(ICallback callback) {
967 final int pid = Binder.getCallingPid();
968 final int uid = Binder.getCallingUid();
969 final long token = Binder.clearCallingIdentity();
970 try {
Amith Yamasanie259ad22017-04-24 11:30:19 -0700971 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900972 throw new SecurityException("Only Bluetooth service processes can set"
973 + " Callback");
974 }
975 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900976 int userId = UserHandle.getUserId(uid);
977 FullUserRecord user = getFullUserRecordLocked(userId);
978 if (user == null || user.mFullUserId != userId) {
979 Log.w(TAG, "Only the full user can set the callback"
980 + ", userId=" + userId);
981 return;
982 }
983 user.mCallback = callback;
984 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +0900985 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900986 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900987 return;
988 }
989 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900990 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +0900991 new IBinder.DeathRecipient() {
992 @Override
993 public void binderDied() {
994 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900995 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +0900996 }
997 }
998 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +0900999 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +09001000 } catch (RemoteException e) {
1001 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +09001002 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001003 }
1004 }
1005 } finally {
1006 Binder.restoreCallingIdentity(token);
1007 }
1008 }
1009
1010 @Override
Jaewan Kim50269362016-12-23 11:22:02 +09001011 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
1012 final int pid = Binder.getCallingPid();
1013 final int uid = Binder.getCallingUid();
1014 final long token = Binder.clearCallingIdentity();
1015 try {
1016 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
1017 if (getContext().checkPermission(
1018 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
1019 != PackageManager.PERMISSION_GRANTED) {
1020 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
1021 " permission.");
1022 }
1023
1024 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001025 int userId = UserHandle.getUserId(uid);
1026 FullUserRecord user = getFullUserRecordLocked(userId);
1027 if (user == null || user.mFullUserId != userId) {
1028 Log.w(TAG, "Only the full user can set the volume key long-press listener"
1029 + ", userId=" + userId);
1030 return;
1031 }
Jaewan Kim50269362016-12-23 11:22:02 +09001032 if (user.mOnVolumeKeyLongPressListener != null &&
1033 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001034 Log.w(TAG, "The volume key long-press listener cannot be reset"
1035 + " by another app , mOnVolumeKeyLongPressListener="
1036 + user.mOnVolumeKeyLongPressListenerUid
1037 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +09001038 return;
1039 }
1040
1041 user.mOnVolumeKeyLongPressListener = listener;
1042 user.mOnVolumeKeyLongPressListenerUid = uid;
1043
Jaewan Kima7dce192017-02-16 17:10:54 +09001044 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001045 + listener + " is set by " + getCallingPackageName(uid));
1046
1047 if (user.mOnVolumeKeyLongPressListener != null) {
1048 try {
1049 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1050 new IBinder.DeathRecipient() {
1051 @Override
1052 public void binderDied() {
1053 synchronized (mLock) {
1054 user.mOnVolumeKeyLongPressListener = null;
1055 }
1056 }
1057 }, 0);
1058 } catch (RemoteException e) {
1059 Log.w(TAG, "Failed to set death recipient "
1060 + user.mOnVolumeKeyLongPressListener);
1061 user.mOnVolumeKeyLongPressListener = null;
1062 }
1063 }
1064 }
1065 } finally {
1066 Binder.restoreCallingIdentity(token);
1067 }
1068 }
1069
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001070 @Override
1071 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1072 final int pid = Binder.getCallingPid();
1073 final int uid = Binder.getCallingUid();
1074 final long token = Binder.clearCallingIdentity();
1075 try {
1076 // Enforce SET_MEDIA_KEY_LISTENER permission.
1077 if (getContext().checkPermission(
1078 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1079 != PackageManager.PERMISSION_GRANTED) {
1080 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1081 " permission.");
1082 }
1083
1084 synchronized (mLock) {
1085 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001086 FullUserRecord user = getFullUserRecordLocked(userId);
1087 if (user == null || user.mFullUserId != userId) {
1088 Log.w(TAG, "Only the full user can set the media key listener"
1089 + ", userId=" + userId);
1090 return;
1091 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001092 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001093 Log.w(TAG, "The media key listener cannot be reset by another app. "
1094 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1095 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001096 return;
1097 }
1098
1099 user.mOnMediaKeyListener = listener;
1100 user.mOnMediaKeyListenerUid = uid;
1101
Jaewan Kima7dce192017-02-16 17:10:54 +09001102 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001103 + " is set by " + getCallingPackageName(uid));
1104
1105 if (user.mOnMediaKeyListener != null) {
1106 try {
1107 user.mOnMediaKeyListener.asBinder().linkToDeath(
1108 new IBinder.DeathRecipient() {
1109 @Override
1110 public void binderDied() {
1111 synchronized (mLock) {
1112 user.mOnMediaKeyListener = null;
1113 }
1114 }
1115 }, 0);
1116 } catch (RemoteException e) {
1117 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1118 user.mOnMediaKeyListener = null;
1119 }
1120 }
1121 }
1122 } finally {
1123 Binder.restoreCallingIdentity(token);
1124 }
1125 }
1126
Jaewan Kim50269362016-12-23 11:22:02 +09001127 /**
1128 * Handles the dispatching of the volume button events to one of the
1129 * registered listeners. If there's a volume key long-press listener and
1130 * there's no active global priority session, long-pressess will be sent to the
1131 * long-press listener instead of adjusting volume.
1132 *
1133 * @param keyEvent a non-null KeyEvent whose key code is one of the
1134 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1135 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1136 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1137 * @param stream stream type to adjust volume.
1138 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1139 */
1140 @Override
1141 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1142 if (keyEvent == null ||
1143 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1144 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1145 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1146 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1147 return;
1148 }
1149
1150 final int pid = Binder.getCallingPid();
1151 final int uid = Binder.getCallingUid();
1152 final long token = Binder.clearCallingIdentity();
1153
Jaewan Kimb2781e72017-03-02 09:57:09 +09001154 if (DEBUG_KEY_EVENT) {
Jaewan Kim50269362016-12-23 11:22:02 +09001155 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1156 + keyEvent);
1157 }
1158
1159 try {
1160 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001161 if (isGlobalPriorityActiveLocked()
1162 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001163 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1164 } else {
1165 // TODO: Consider the case when both volume up and down keys are pressed
1166 // at the same time.
1167 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1168 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001169 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001170 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1171 KeyEvent.obtain(keyEvent);
1172 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1173 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001174 mHandler.sendMessageDelayed(
1175 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001176 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1177 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001178 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001179 }
1180 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001181 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001182 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001183 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001184 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001185 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001186 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001187 }
1188 dispatchVolumeKeyLongPressLocked(keyEvent);
1189 }
1190 } else { // if up
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
1193 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1194 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001195 // Short-press. Should change volume.
1196 dispatchVolumeKeyEventLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001197 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1198 mCurrentFullUserRecord.mInitialDownVolumeStream,
1199 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001200 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1201 } else {
1202 dispatchVolumeKeyLongPressLocked(keyEvent);
1203 }
1204 }
1205 }
1206 }
1207 } finally {
1208 Binder.restoreCallingIdentity(token);
1209 }
1210 }
1211
Jaewan Kim50269362016-12-23 11:22:02 +09001212 private void dispatchVolumeKeyEventLocked(
1213 KeyEvent keyEvent, int stream, boolean musicOnly) {
1214 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1215 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1216 int direction = 0;
1217 boolean isMute = false;
1218 switch (keyEvent.getKeyCode()) {
1219 case KeyEvent.KEYCODE_VOLUME_UP:
1220 direction = AudioManager.ADJUST_RAISE;
1221 break;
1222 case KeyEvent.KEYCODE_VOLUME_DOWN:
1223 direction = AudioManager.ADJUST_LOWER;
1224 break;
1225 case KeyEvent.KEYCODE_VOLUME_MUTE:
1226 isMute = true;
1227 break;
1228 }
1229 if (down || up) {
1230 int flags = AudioManager.FLAG_FROM_KEY;
1231 if (musicOnly) {
1232 // This flag is used when the screen is off to only affect active media.
1233 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1234 } else {
1235 // These flags are consistent with the home screen
1236 if (up) {
1237 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1238 } else {
1239 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1240 }
1241 }
1242 if (direction != 0) {
1243 // If this is action up we want to send a beep for non-music events
1244 if (up) {
1245 direction = 0;
1246 }
1247 dispatchAdjustVolumeLocked(stream, direction, flags);
1248 } else if (isMute) {
1249 if (down && keyEvent.getRepeatCount() == 0) {
1250 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1251 }
1252 }
1253 }
1254 }
1255
1256 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001257 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001258 final long token = Binder.clearCallingIdentity();
1259 try {
1260 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001261 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001262 }
1263 } finally {
1264 Binder.restoreCallingIdentity(token);
1265 }
1266 }
1267
1268 @Override
RoboErik19c95182014-06-23 15:38:48 -07001269 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1270 final int pid = Binder.getCallingPid();
1271 final int uid = Binder.getCallingUid();
1272 final long token = Binder.clearCallingIdentity();
1273 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001274 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001275 mRvc = rvc;
1276 } finally {
1277 Binder.restoreCallingIdentity(token);
1278 }
1279 }
1280
1281 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001282 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001283 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001284 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001285 }
RoboErikde9ba392014-09-26 12:51:01 -07001286 }
1287
1288 @Override
RoboErika278ea72014-04-24 14:49:01 -07001289 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001290 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
RoboErika278ea72014-04-24 14:49:01 -07001291
1292 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1293 pw.println();
1294
1295 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001296 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001297 pw.println("Global priority session is " + mGlobalPrioritySession);
Jaewan Kim101b4d52017-05-18 13:23:11 +09001298 if (mGlobalPrioritySession != null) {
1299 mGlobalPrioritySession.dump(pw, " ");
1300 }
RoboErik4646d282014-05-13 10:13:04 -07001301 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001302 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001303 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001304 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001305 }
Jaewan Kim92dea332017-02-02 11:52:08 +09001306 mAudioPlaybackMonitor.dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001307 }
1308 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001309
RoboErik2e7a9162014-06-04 16:53:45 -07001310 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1311 final int uid) {
1312 String packageName = null;
1313 if (componentName != null) {
1314 // If they gave us a component name verify they own the
1315 // package
1316 packageName = componentName.getPackageName();
1317 enforcePackageName(packageName, uid);
1318 }
1319 // Check that they can make calls on behalf of the user and
1320 // get the final user id
1321 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1322 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1323 // Check if they have the permissions or their component is
1324 // enabled for the user they're calling from.
1325 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1326 return resolvedUserId;
1327 }
1328
Jaewan Kim50269362016-12-23 11:22:02 +09001329 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001330 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1331 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001332
RoboErik9c785402014-11-11 16:52:26 -08001333 boolean preferSuggestedStream = false;
1334 if (isValidLocalStreamType(suggestedStream)
1335 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1336 preferSuggestedStream = true;
1337 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001338 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001339 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1340 + flags + ", suggestedStream=" + suggestedStream
1341 + ", preferSuggestedStream=" + preferSuggestedStream);
1342 }
RoboErik9c785402014-11-11 16:52:26 -08001343 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001344 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1345 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001346 if (DEBUG) {
1347 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001348 }
RoboErikb7c014c2014-07-22 15:58:22 -07001349 return;
RoboErik3c45c292014-07-08 16:47:31 -07001350 }
Shibin George19e84042016-06-14 20:42:13 +05301351
1352 // Execute mAudioService.adjustSuggestedStreamVolume() on
1353 // handler thread of MediaSessionService.
1354 // This will release the MediaSessionService.mLock sooner and avoid
1355 // a potential deadlock between MediaSessionService.mLock and
1356 // ActivityManagerService lock.
1357 mHandler.post(new Runnable() {
1358 @Override
1359 public void run() {
1360 try {
1361 String packageName = getContext().getOpPackageName();
1362 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1363 flags, packageName, TAG);
1364 } catch (RemoteException e) {
1365 Log.e(TAG, "Error adjusting default volume.", e);
Hyundo Moon739d6c22017-09-18 17:01:48 +09001366 } catch (IllegalArgumentException e) {
1367 Log.e(TAG, "Cannot adjust volume: direction=" + direction
1368 + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
1369 e);
Shibin George19e84042016-06-14 20:42:13 +05301370 }
1371 }
1372 });
RoboErikb69ffd42014-05-30 14:57:59 -07001373 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001374 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001375 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001376 }
1377 }
1378
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001379 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001380 int action = keyEvent.getAction();
1381 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1382 if (action == KeyEvent.ACTION_DOWN) {
1383 if (keyEvent.getRepeatCount() == 0) {
1384 mVoiceButtonDown = true;
1385 mVoiceButtonHandled = false;
1386 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1387 mVoiceButtonHandled = true;
1388 startVoiceInput(needWakeLock);
1389 }
1390 } else if (action == KeyEvent.ACTION_UP) {
1391 if (mVoiceButtonDown) {
1392 mVoiceButtonDown = false;
1393 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1394 // Resend the down then send this event through
1395 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001396 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1397 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001398 }
1399 }
1400 }
1401 }
1402
Jaewan Kim98003d32017-02-24 18:33:04 +09001403 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001404 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001405 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001406 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001407 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001408 }
1409 if (needWakeLock) {
1410 mKeyEventReceiver.aquireWakeLockLocked();
1411 }
Jaewan Kim50269362016-12-23 11:22:02 +09001412 // 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 -07001413 session.sendMediaButton(keyEvent,
1414 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001415 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001416 getContext().getPackageName());
Jaewan Kima7dce192017-02-16 17:10:54 +09001417 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001418 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001419 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
1420 keyEvent,
Jaewan Kimbd16f452017-02-03 16:21:38 +09001421 new MediaSession.Token(session.getControllerBinder()));
1422 } catch (RemoteException e) {
1423 Log.w(TAG, "Failed to send callback", e);
1424 }
1425 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001426 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1427 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1428 if (needWakeLock) {
1429 mKeyEventReceiver.aquireWakeLockLocked();
1430 }
1431 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1432 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1433 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1434 try {
1435 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1436 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1437 if (DEBUG_KEY_EVENT) {
1438 Log.d(TAG, "Sending " + keyEvent
Jaewan Kim92dea332017-02-02 11:52:08 +09001439 + " to the last known PendingIntent " + receiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001440 }
1441 receiver.send(getContext(),
1442 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1443 mediaButtonIntent, mKeyEventReceiver, mHandler);
1444 if (mCurrentFullUserRecord.mCallback != null) {
1445 ComponentName componentName = mCurrentFullUserRecord
1446 .mLastMediaButtonReceiver.getIntent().getComponent();
1447 if (componentName != null) {
1448 mCurrentFullUserRecord.mCallback
1449 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1450 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001451 }
RoboErikc8f92d12015-01-05 16:48:07 -08001452 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001453 } else {
1454 ComponentName receiver =
1455 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1456 if (DEBUG_KEY_EVENT) {
1457 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1458 + receiver);
1459 }
1460 mediaButtonIntent.setComponent(receiver);
1461 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim92dea332017-02-02 11:52:08 +09001462 UserHandle.of(mCurrentFullUserRecord
1463 .mRestoredMediaButtonReceiverUserId));
Jaewan Kima7dce192017-02-16 17:10:54 +09001464 if (mCurrentFullUserRecord.mCallback != null) {
1465 mCurrentFullUserRecord.mCallback
1466 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1467 keyEvent, receiver);
1468 }
RoboErikb214efb2014-07-24 13:20:30 -07001469 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001470 } catch (CanceledException e) {
1471 Log.i(TAG, "Error sending key event to media button receiver "
1472 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1473 } catch (RemoteException e) {
1474 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001475 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001476 }
1477 }
1478
1479 private void startVoiceInput(boolean needWakeLock) {
1480 Intent voiceIntent = null;
1481 // select which type of search to launch:
1482 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1483 // - device locked or screen off: action is
1484 // ACTION_VOICE_SEARCH_HANDS_FREE
1485 // with EXTRA_SECURE set to true if the device is securely locked
1486 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1487 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1488 if (!isLocked && pm.isScreenOn()) {
1489 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1490 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1491 } else {
1492 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1493 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1494 isLocked && mKeyguardManager.isKeyguardSecure());
1495 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1496 }
1497 // start the search activity
1498 if (needWakeLock) {
1499 mMediaEventWakeLock.acquire();
1500 }
1501 try {
1502 if (voiceIntent != null) {
1503 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1504 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001505 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001506 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1507 }
1508 } catch (ActivityNotFoundException e) {
1509 Log.w(TAG, "No activity for search: " + e);
1510 } finally {
1511 if (needWakeLock) {
1512 mMediaEventWakeLock.release();
1513 }
1514 }
1515 }
1516
1517 private boolean isVoiceKey(int keyCode) {
Jaewan Kimba18d8e2017-05-12 17:37:57 +09001518 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
Jaewan Kimfdb612e2017-07-01 09:23:41 +09001519 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
RoboErik9a9d0b52014-05-20 14:53:39 -07001520 }
1521
Jeff Brown38d3feb2015-03-19 18:26:30 -07001522 private boolean isUserSetupComplete() {
1523 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1524 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1525 }
1526
RoboErik9c785402014-11-11 16:52:26 -08001527 // we only handle public stream types, which are 0-5
1528 private boolean isValidLocalStreamType(int streamType) {
1529 return streamType >= AudioManager.STREAM_VOICE_CALL
1530 && streamType <= AudioManager.STREAM_NOTIFICATION;
1531 }
1532
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001533 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1534 private KeyEvent mKeyEvent;
1535 private boolean mNeedWakeLock;
1536 private boolean mHandled;
1537
1538 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1539 super(mHandler);
1540 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1541 mKeyEvent = keyEvent;
1542 mNeedWakeLock = needWakeLock;
1543 }
1544
1545 @Override
1546 public void run() {
1547 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1548 dispatchMediaKeyEvent();
1549 }
1550
1551 @Override
1552 protected void onReceiveResult(int resultCode, Bundle resultData) {
1553 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1554 mHandled = true;
1555 mHandler.removeCallbacks(this);
1556 return;
1557 }
1558 dispatchMediaKeyEvent();
1559 }
1560
1561 private void dispatchMediaKeyEvent() {
1562 if (mHandled) {
1563 return;
1564 }
1565 mHandled = true;
1566 mHandler.removeCallbacks(this);
1567 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001568 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001569 && isVoiceKey(mKeyEvent.getKeyCode())) {
1570 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1571 } else {
1572 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1573 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001574 }
1575 }
1576 }
1577
RoboErik418c10c2014-05-19 09:25:25 -07001578 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1579
RoboErikb214efb2014-07-24 13:20:30 -07001580 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1581 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001582 private final Handler mHandler;
1583 private int mRefCount = 0;
1584 private int mLastTimeoutId = 0;
1585
1586 public KeyEventWakeLockReceiver(Handler handler) {
1587 super(handler);
1588 mHandler = handler;
1589 }
1590
1591 public void onTimeout() {
1592 synchronized (mLock) {
1593 if (mRefCount == 0) {
1594 // We've already released it, so just return
1595 return;
1596 }
1597 mLastTimeoutId++;
1598 mRefCount = 0;
1599 releaseWakeLockLocked();
1600 }
1601 }
1602
1603 public void aquireWakeLockLocked() {
1604 if (mRefCount == 0) {
1605 mMediaEventWakeLock.acquire();
1606 }
1607 mRefCount++;
1608 mHandler.removeCallbacks(this);
1609 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1610
1611 }
1612
1613 @Override
1614 public void run() {
1615 onTimeout();
1616 }
1617
RoboErik8a2cfc32014-05-16 11:19:38 -07001618 @Override
1619 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001620 if (resultCode < mLastTimeoutId) {
1621 // Ignore results from calls that were before the last
1622 // timeout, just in case.
1623 return;
1624 } else {
1625 synchronized (mLock) {
1626 if (mRefCount > 0) {
1627 mRefCount--;
1628 if (mRefCount == 0) {
1629 releaseWakeLockLocked();
1630 }
1631 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001632 }
1633 }
1634 }
RoboErik418c10c2014-05-19 09:25:25 -07001635
1636 private void releaseWakeLockLocked() {
1637 mMediaEventWakeLock.release();
1638 mHandler.removeCallbacks(this);
1639 }
RoboErikb214efb2014-07-24 13:20:30 -07001640
1641 @Override
1642 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1643 String resultData, Bundle resultExtras) {
1644 onReceiveResult(resultCode, null);
1645 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001646 };
1647
1648 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1649 @Override
1650 public void onReceive(Context context, Intent intent) {
1651 if (intent == null) {
1652 return;
1653 }
1654 Bundle extras = intent.getExtras();
1655 if (extras == null) {
1656 return;
1657 }
1658 synchronized (mLock) {
1659 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1660 && mMediaEventWakeLock.isHeld()) {
1661 mMediaEventWakeLock.release();
1662 }
1663 }
1664 }
1665 };
RoboErik01fe6612014-02-13 14:19:04 -08001666 }
1667
RoboErik2e7a9162014-06-04 16:53:45 -07001668 final class MessageHandler extends Handler {
1669 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001670 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
Jaewan Kim92dea332017-02-02 11:52:08 +09001671 private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
RoboErik2e7a9162014-06-04 16:53:45 -07001672
1673 @Override
1674 public void handleMessage(Message msg) {
1675 switch (msg.what) {
1676 case MSG_SESSIONS_CHANGED:
Jaewan Kim92dea332017-02-02 11:52:08 +09001677 pushSessionsChanged((int) msg.obj);
RoboErik2e7a9162014-06-04 16:53:45 -07001678 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001679 case MSG_VOLUME_INITIAL_DOWN:
1680 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001681 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001682 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1683 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1684 // Mark that the key is already handled.
1685 user.mInitialDownVolumeKeyEvent = null;
1686 }
1687 }
1688 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001689 }
1690 }
1691
Jaewan Kim92dea332017-02-02 11:52:08 +09001692 public void postSessionsChanged(int userId) {
1693 // Use object instead of the arguments when posting message to remove pending requests.
1694 Integer userIdInteger = mIntegerCache.get(userId);
1695 if (userIdInteger == null) {
1696 userIdInteger = Integer.valueOf(userId);
1697 mIntegerCache.put(userId, userIdInteger);
1698 }
1699 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
1700 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
RoboErik2e7a9162014-06-04 16:53:45 -07001701 }
1702 }
RoboErik01fe6612014-02-13 14:19:04 -08001703}