blob: 38c615791d9f0f0829752daed901f086cc798788 [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
RoboErika278ea72014-04-24 14:49:01 -070019import android.Manifest;
Jaewan Kim50269362016-12-23 11:22:02 +090020import android.annotation.NonNull;
RoboErik8a2cfc32014-05-16 11:19:38 -070021import android.app.Activity;
RoboErike7880d82014-04-30 12:48:25 -070022import android.app.ActivityManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070023import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070024import android.app.PendingIntent;
25import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070026import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070027import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070028import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070029import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080030import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070031import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070032import android.content.pm.PackageManager;
Jaewan Kima7dce192017-02-16 17:10:54 +090033import android.content.pm.UserInfo;
RoboErik7aef77b2014-08-08 15:56:54 -070034import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070035import android.media.AudioManager;
John Spurlockeb69e242015-02-17 17:15:04 -050036import android.media.AudioManagerInternal;
RoboErik94c716e2014-09-14 13:54:31 -070037import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070038import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070039import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070040import android.media.session.IActiveSessionsListener;
Jaewan Kimbd16f452017-02-03 16:21:38 +090041import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080042import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090043import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070044import android.media.session.ISession;
45import android.media.session.ISessionCallback;
46import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070047import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080048import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070049import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080050import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070051import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080052import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070053import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070054import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070055import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090056import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080057import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070058import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070059import android.os.ServiceManager;
Jaewan Kim92dea332017-02-02 11:52:08 +090060import android.os.SystemProperties;
RoboErike7880d82014-04-30 12:48:25 -070061import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090062import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070063import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070064import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080065import android.text.TextUtils;
Jaewan Kim92dea332017-02-02 11:52:08 +090066import android.util.IntArray;
RoboErik01fe6612014-02-13 14:19:04 -080067import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070068import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070069import android.util.SparseArray;
Jaewan Kima7dce192017-02-16 17:10:54 +090070import android.util.SparseIntArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070071import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090072import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080073
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060074import com.android.internal.util.DumpUtils;
John Spurlockeb69e242015-02-17 17:15:04 -050075import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080076import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070077import com.android.server.Watchdog;
78import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080079
RoboErika278ea72014-04-24 14:49:01 -070080import java.io.FileDescriptor;
81import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080082import java.util.ArrayList;
Jaewan Kim8f729082016-06-21 12:36:26 +090083import java.util.Arrays;
RoboErike7880d82014-04-30 12:48:25 -070084import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080085
86/**
87 * System implementation of MediaSessionManager
88 */
RoboErika278ea72014-04-24 14:49:01 -070089public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080090 private static final String TAG = "MediaSessionService";
Jaewan Kim92dea332017-02-02 11:52:08 +090091 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090092 // Leave log for key event always.
93 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080094
RoboErik418c10c2014-05-19 09:25:25 -070095 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080096 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070097
RoboErik01fe6612014-02-13 14:19:04 -080098 private final SessionManagerImpl mSessionManagerImpl;
99
Jaewan Kima7dce192017-02-16 17:10:54 +0900100 // Keeps the full user id for each user.
101 private final SparseIntArray mFullUserIds = new SparseIntArray();
102 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -0700103 private final ArrayList<SessionsListenerRecord> mSessionsListeners
104 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800105 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700106 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700107 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900108 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800109
RoboErik9a9d0b52014-05-20 14:53:39 -0700110 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700111 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -0500112 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700113 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700114 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -0700115
Jaewan Kima7dce192017-02-16 17:10:54 +0900116 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
117 // It's always not null after the MediaSessionService is started.
118 private FullUserRecord mCurrentFullUserRecord;
119 private MediaSessionRecord mGlobalPrioritySession;
Jaewan Kim92dea332017-02-02 11:52:08 +0900120 private AudioPlaybackMonitor mAudioPlaybackMonitor;
RoboErike7880d82014-04-30 12:48:25 -0700121
RoboErik19c95182014-06-23 15:38:48 -0700122 // Used to notify system UI when remote volume was changed. TODO find a
123 // better way to handle this.
124 private IRemoteVolumeController mRvc;
125
RoboErik01fe6612014-02-13 14:19:04 -0800126 public MediaSessionService(Context context) {
127 super(context);
128 mSessionManagerImpl = new SessionManagerImpl();
RoboErik8a2cfc32014-05-16 11:19:38 -0700129 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
130 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900131 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
RoboErik01fe6612014-02-13 14:19:04 -0800132 }
133
134 @Override
135 public void onStart() {
136 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700137 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700138 mKeyguardManager =
139 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700140 mAudioService = getAudioService();
Jaewan Kim92dea332017-02-02 11:52:08 +0900141 mAudioPlaybackMonitor = new AudioPlaybackMonitor(getContext(), mAudioService,
142 new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() {
143 @Override
144 public void onAudioPlaybackStarted(int uid) {
145 synchronized (mLock) {
146 FullUserRecord user =
147 getFullUserRecordLocked(UserHandle.getUserId(uid));
148 if (user != null) {
149 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
150 }
151 }
152 }
153 });
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();
RoboErikc8f92d12015-01-05 16:48:07 -0800158
159 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700160 }
161
162 private IAudioService getAudioService() {
163 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
164 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700165 }
166
Jaewan Kima7dce192017-02-16 17:10:54 +0900167 private boolean isGlobalPriorityActiveLocked() {
168 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
169 }
170
RoboErika8f95142014-05-05 14:23:49 -0700171 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700172 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900173 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
Jaewan Kim101b4d52017-05-18 13:23:11 +0900174 if (user == null) {
175 Log.w(TAG, "Unknown session updated. Ignoring.");
RoboErik4646d282014-05-13 10:13:04 -0700176 return;
177 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900178 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900179 if (mGlobalPrioritySession != record) {
180 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
181 + " to " + record);
182 mGlobalPrioritySession = record;
183 if (user != null && user.mPriorityStack.contains(record)) {
184 // Handle the global priority session separately.
185 // Otherwise, it will be the media button session even after it becomes
186 // inactive because it has been the lastly played media app.
187 user.mPriorityStack.removeSession(record);
188 }
189 }
190 if (DEBUG_KEY_EVENT) {
191 Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
192 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900193 user.pushAddressedPlayerChangedLocked();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900194 } else {
195 if (!user.mPriorityStack.contains(record)) {
196 Log.w(TAG, "Unknown session updated. Ignoring.");
197 return;
198 }
199 user.mPriorityStack.onSessionStateChange(record);
Jaewan Kima7dce192017-02-16 17:10:54 +0900200 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900201 mHandler.postSessionsChanged(record.getUserId());
RoboErike7880d82014-04-30 12:48:25 -0700202 }
203 }
204
Jaewan Kim101b4d52017-05-18 13:23:11 +0900205 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
206 List<MediaSessionRecord> records;
207 if (userId == UserHandle.USER_ALL) {
208 records = new ArrayList<>();
209 int size = mUserRecords.size();
210 for (int i = 0; i < size; i++) {
211 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
212 }
213 } else {
214 FullUserRecord user = getFullUserRecordLocked(userId);
215 if (user == null) {
216 Log.w(TAG, "getSessions failed. Unknown user " + userId);
217 return new ArrayList<>();
218 }
219 records = user.mPriorityStack.getActiveSessions(userId);
220 }
221
222 // Return global priority session at the first whenever it's asked.
223 if (isGlobalPriorityActiveLocked()
224 && (userId == UserHandle.USER_ALL
225 || userId == mGlobalPrioritySession.getUserId())) {
226 records.add(0, mGlobalPrioritySession);
227 }
228 return records;
229 }
230
RoboErik9c5b7cb2015-01-15 15:09:09 -0800231 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900232 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800233 */
234 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900235 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800236 return;
237 }
238 try {
239 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
240 } catch (Exception e) {
241 Log.wtf(TAG, "Error sending volume change to system UI.", e);
242 }
243 }
244
Jaewan Kim92dea332017-02-02 11:52:08 +0900245 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
RoboErika8f95142014-05-05 14:23:49 -0700246 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900247 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
248 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700249 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
250 return;
251 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900252 user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
RoboErika8f95142014-05-05 14:23:49 -0700253 }
254 }
255
RoboErik19c95182014-06-23 15:38:48 -0700256 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
257 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900258 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
259 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700260 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
261 return;
262 }
263 pushRemoteVolumeUpdateLocked(record.getUserId());
264 }
265 }
266
RoboErika278ea72014-04-24 14:49:01 -0700267 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900268 public void onStartUser(int userId) {
269 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700270 updateUser();
271 }
272
273 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900274 public void onSwitchUser(int userId) {
275 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700276 updateUser();
277 }
278
279 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900280 public void onStopUser(int userId) {
281 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700282 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900283 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700284 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900285 if (user.mFullUserId == userId) {
286 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
287 mUserRecords.remove(userId);
288 } else {
289 user.destroySessionsForUserLocked(userId);
290 }
RoboErik4646d282014-05-13 10:13:04 -0700291 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900292 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700293 }
294 }
295
296 @Override
RoboErika278ea72014-04-24 14:49:01 -0700297 public void monitor() {
298 synchronized (mLock) {
299 // Check for deadlock
300 }
301 }
302
RoboErik4646d282014-05-13 10:13:04 -0700303 protected void enforcePhoneStatePermission(int pid, int uid) {
304 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
305 != PackageManager.PERMISSION_GRANTED) {
306 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
307 }
308 }
309
RoboErik01fe6612014-02-13 14:19:04 -0800310 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700311 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800312 destroySessionLocked(session);
313 }
314 }
315
316 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700317 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800318 destroySessionLocked(session);
319 }
320 }
321
RoboErik4646d282014-05-13 10:13:04 -0700322 private void updateUser() {
323 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900324 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900325 mFullUserIds.clear();
326 List<UserInfo> allUsers = manager.getUsers();
327 if (allUsers != null) {
328 for (UserInfo userInfo : allUsers) {
329 if (userInfo.isManagedProfile()) {
330 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
331 } else {
332 mFullUserIds.put(userInfo.id, userInfo.id);
333 if (mUserRecords.get(userInfo.id) == null) {
334 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
335 }
336 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900337 }
RoboErik4646d282014-05-13 10:13:04 -0700338 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900339 // Ensure that the current full user exists.
340 int currentFullUserId = ActivityManager.getCurrentUser();
341 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
342 if (mCurrentFullUserRecord == null) {
343 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
344 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
345 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
346 }
347 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700348 }
349 }
350
RoboErik7aef77b2014-08-08 15:56:54 -0700351 private void updateActiveSessionListeners() {
352 synchronized (mLock) {
353 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
354 SessionsListenerRecord listener = mSessionsListeners.get(i);
355 try {
356 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
357 listener.mUserId);
358 } catch (SecurityException e) {
359 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
360 + " is no longer authorized. Disconnecting.");
361 mSessionsListeners.remove(i);
362 try {
363 listener.mListener
364 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
365 } catch (Exception e1) {
366 // ignore
367 }
368 }
369 }
370 }
371 }
372
RoboErik4646d282014-05-13 10:13:04 -0700373 /*
374 * When a session is removed several things need to happen.
375 * 1. We need to remove it from the relevant user.
376 * 2. We need to remove it from the priority stack.
377 * 3. We need to remove it from all sessions.
378 * 4. If this is the system priority session we need to clear it.
379 * 5. We need to unlink to death from the cb binder
380 * 6. We need to tell the session to do any final cleanup (onDestroy)
381 */
RoboErik01fe6612014-02-13 14:19:04 -0800382 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900383 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900384 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900385 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900386 FullUserRecord user = getFullUserRecordLocked(session.getUserId());
Jaewan Kima7dce192017-02-16 17:10:54 +0900387 if (mGlobalPrioritySession == session) {
388 mGlobalPrioritySession = null;
Jaewan Kim92dea332017-02-02 11:52:08 +0900389 if (session.isActive() && user != null) {
390 user.pushAddressedPlayerChangedLocked();
391 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900392 } else {
393 if (user != null) {
394 user.mPriorityStack.removeSession(session);
395 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900396 }
RoboErik4646d282014-05-13 10:13:04 -0700397
398 try {
399 session.getCallback().asBinder().unlinkToDeath(session, 0);
400 } catch (Exception e) {
401 // ignore exceptions while destroying a session.
402 }
403 session.onDestroy();
Jaewan Kim92dea332017-02-02 11:52:08 +0900404 mHandler.postSessionsChanged(session.getUserId());
RoboErik01fe6612014-02-13 14:19:04 -0800405 }
406
407 private void enforcePackageName(String packageName, int uid) {
408 if (TextUtils.isEmpty(packageName)) {
409 throw new IllegalArgumentException("packageName may not be empty");
410 }
411 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
412 final int packageCount = packages.length;
413 for (int i = 0; i < packageCount; i++) {
414 if (packageName.equals(packages[i])) {
415 return;
416 }
417 }
418 throw new IllegalArgumentException("packageName is not owned by the calling process");
419 }
420
RoboErike7880d82014-04-30 12:48:25 -0700421 /**
422 * Checks a caller's authorization to register an IRemoteControlDisplay.
423 * Authorization is granted if one of the following is true:
424 * <ul>
425 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
426 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700427 * <li>the caller's listener is one of the enabled notification listeners
428 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700429 * </ul>
430 */
RoboErika5b02322014-05-07 17:05:49 -0700431 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
432 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500433 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700434 if (getContext()
435 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
436 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700437 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
438 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700439 throw new SecurityException("Missing permission to control media.");
440 }
441 }
442
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500443 private boolean isCurrentVolumeController(int uid, int pid) {
444 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
445 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500446 }
447
448 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500449 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700450 throw new SecurityException("Only system ui may " + action);
451 }
452 }
453
RoboErika5b02322014-05-07 17:05:49 -0700454 /**
455 * This checks if the component is an enabled notification listener for the
456 * specified user. Enabled components may only operate on behalf of the user
457 * they're running as.
458 *
459 * @param compName The component that is enabled.
460 * @param userId The user id of the caller.
461 * @param forUserId The user id they're making the request on behalf of.
462 * @return True if the component is enabled, false otherwise
463 */
464 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
465 int forUserId) {
466 if (userId != forUserId) {
467 // You may not access another user's content as an enabled listener.
468 return false;
469 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700470 if (DEBUG) {
471 Log.d(TAG, "Checking if enabled notification listener " + compName);
472 }
RoboErike7880d82014-04-30 12:48:25 -0700473 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700474 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700475 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700476 userId);
RoboErike7880d82014-04-30 12:48:25 -0700477 if (enabledNotifListeners != null) {
478 final String[] components = enabledNotifListeners.split(":");
479 for (int i = 0; i < components.length; i++) {
480 final ComponentName component =
481 ComponentName.unflattenFromString(components[i]);
482 if (component != null) {
483 if (compName.equals(component)) {
484 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900485 Log.d(TAG, "ok to get sessions. " + component +
RoboErike7880d82014-04-30 12:48:25 -0700486 " is authorized notification listener");
487 }
488 return true;
489 }
490 }
491 }
492 }
493 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900494 Log.d(TAG, "not ok to get sessions. " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700495 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700496 }
497 }
498 return false;
499 }
500
RoboErika5b02322014-05-07 17:05:49 -0700501 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700502 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800503 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700504 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800505 }
506 }
507
RoboErik4646d282014-05-13 10:13:04 -0700508 /*
509 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700510 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700511 * 2. It needs to be added to all sessions.
512 * 3. It needs to be added to the priority stack.
513 * 4. It needs to be added to the relevant user record.
514 */
RoboErika5b02322014-05-07 17:05:49 -0700515 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
516 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900517 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700518 if (user == null) {
519 Log.wtf(TAG, "Request from invalid user: " + userId);
520 throw new RuntimeException("Session request from invalid user.");
521 }
522
RoboErika5b02322014-05-07 17:05:49 -0700523 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
Jaewan Kim92dea332017-02-02 11:52:08 +0900524 callerPackageName, cb, tag, this, mHandler.getLooper());
RoboErik01fe6612014-02-13 14:19:04 -0800525 try {
526 cb.asBinder().linkToDeath(session, 0);
527 } catch (RemoteException e) {
528 throw new RuntimeException("Media Session owner died prematurely.", e);
529 }
RoboErik4646d282014-05-13 10:13:04 -0700530
Jaewan Kim101b4d52017-05-18 13:23:11 +0900531 user.mPriorityStack.addSession(session);
Jaewan Kim92dea332017-02-02 11:52:08 +0900532 mHandler.postSessionsChanged(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700533
RoboErik01fe6612014-02-13 14:19:04 -0800534 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900535 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800536 }
537 return session;
538 }
539
RoboErik2e7a9162014-06-04 16:53:45 -0700540 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
541 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800542 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700543 return i;
544 }
545 }
546 return -1;
547 }
548
RoboErik2e7a9162014-06-04 16:53:45 -0700549 private void pushSessionsChanged(int userId) {
550 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900551 FullUserRecord user = getFullUserRecordLocked(userId);
552 if (user == null) {
553 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
554 return;
555 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900556 List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700557 int size = records.size();
Jeff Browndba34ba2014-06-24 20:46:03 -0700558 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700559 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700560 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700561 }
RoboErik19c95182014-06-23 15:38:48 -0700562 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700563 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
564 SessionsListenerRecord record = mSessionsListeners.get(i);
565 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
566 try {
567 record.mListener.onActiveSessionsChanged(tokens);
568 } catch (RemoteException e) {
569 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
570 e);
571 mSessionsListeners.remove(i);
572 }
573 }
574 }
575 }
576 }
577
RoboErik19c95182014-06-23 15:38:48 -0700578 private void pushRemoteVolumeUpdateLocked(int userId) {
579 if (mRvc != null) {
580 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900581 FullUserRecord user = getFullUserRecordLocked(userId);
582 if (user == null) {
583 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
584 return;
585 }
586 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700587 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
588 } catch (RemoteException e) {
589 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
590 }
591 }
592 }
593
Jaewan Kim92dea332017-02-02 11:52:08 +0900594 /**
595 * Called when the media button receiver for the {@param record} is changed.
596 *
597 * @param record the media session whose media button receiver is updated.
598 */
599 public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
600 synchronized (mLock) {
601 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
602 MediaSessionRecord mediaButtonSession =
603 user.mPriorityStack.getMediaButtonSession();
604 if (record == mediaButtonSession) {
605 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
606 }
607 }
608 }
609
Jaewan Kim50269362016-12-23 11:22:02 +0900610 private String getCallingPackageName(int uid) {
611 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
612 if (packages != null && packages.length > 0) {
613 return packages[0];
614 }
615 return "";
616 }
617
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900618 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900619 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900620 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900621 } catch (RemoteException e) {
622 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
623 }
624 }
625
Jaewan Kima7dce192017-02-16 17:10:54 +0900626 private FullUserRecord getFullUserRecordLocked(int userId) {
627 int fullUserId = mFullUserIds.get(userId, -1);
628 if (fullUserId < 0) {
629 return null;
630 }
631 return mUserRecords.get(fullUserId);
632 }
633
RoboErik4646d282014-05-13 10:13:04 -0700634 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900635 * Information about a full user and its corresponding managed profiles.
636 *
637 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
638 * them when he/she presses a media/volume button. So keeping media sessions for them in one
639 * place makes more sense and increases the readability.</p>
640 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700641 */
Jaewan Kim92dea332017-02-02 11:52:08 +0900642 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
Jaewan Kima7dce192017-02-16 17:10:54 +0900643 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
644 private final int mFullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900645 private final MediaSessionStack mPriorityStack;
RoboErikb214efb2014-07-24 13:20:30 -0700646 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800647 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900648 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700649
Jaewan Kim50269362016-12-23 11:22:02 +0900650 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
651 private int mOnVolumeKeyLongPressListenerUid;
652 private KeyEvent mInitialDownVolumeKeyEvent;
653 private int mInitialDownVolumeStream;
654 private boolean mInitialDownMusicOnly;
655
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800656 private IOnMediaKeyListener mOnMediaKeyListener;
657 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900658 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800659
Jaewan Kima7dce192017-02-16 17:10:54 +0900660 public FullUserRecord(int fullUserId) {
661 mFullUserId = fullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900662 mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this);
Jaewan Kima7dce192017-02-16 17:10:54 +0900663 // Restore the remembered media button receiver before the boot.
664 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
665 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
666 if (mediaButtonReceiver == null) {
667 return;
668 }
669 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
670 if (tokens == null || tokens.length != 2) {
671 return;
672 }
673 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
674 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700675 }
676
Jaewan Kima7dce192017-02-16 17:10:54 +0900677 public void destroySessionsForUserLocked(int userId) {
Jaewan Kim92dea332017-02-02 11:52:08 +0900678 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
Jaewan Kima7dce192017-02-16 17:10:54 +0900679 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700680 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700681 }
682 }
683
RoboErik4646d282014-05-13 10:13:04 -0700684 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900685 pw.print(prefix + "Record for full_user=" + mFullUserId);
686 // Dump managed profile user ids associated with this user.
687 int size = mFullUserIds.size();
688 for (int i = 0; i < size; i++) {
689 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
690 && mFullUserIds.valueAt(i) == mFullUserId) {
691 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
692 }
693 }
694 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700695 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900696 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
697 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900698 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800699 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
700 pw.println(indent + "Media key listener package: " +
701 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900702 pw.println(indent + "Callback: " + mCallback);
703 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
704 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
705 mPriorityStack.dump(pw, indent);
706 }
707
Jaewan Kim92dea332017-02-02 11:52:08 +0900708 @Override
709 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
710 MediaSessionRecord newMediaButtonSession) {
711 if (DEBUG_KEY_EVENT) {
Jaewan Kim98e4aaf2017-05-12 17:06:47 +0900712 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
Jaewan Kim92dea332017-02-02 11:52:08 +0900713 }
714 synchronized (mLock) {
715 if (oldMediaButtonSession != null) {
716 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
717 }
718 if (newMediaButtonSession != null) {
719 rememberMediaButtonReceiverLocked(newMediaButtonSession);
720 mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
721 }
722 pushAddressedPlayerChangedLocked();
723 }
724 }
725
726 // Remember media button receiver and keep it in the persistent storage.
727 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900728 PendingIntent receiver = record.getMediaButtonReceiver();
Jaewan Kima7dce192017-02-16 17:10:54 +0900729 mLastMediaButtonReceiver = receiver;
Jaewan Kim92dea332017-02-02 11:52:08 +0900730 mRestoredMediaButtonReceiver = null;
731 String componentName = "";
732 if (receiver != null) {
733 ComponentName component = receiver.getIntent().getComponent();
734 if (component != null
735 && record.getPackageName().equals(component.getPackageName())) {
736 componentName = component.flattenToString();
737 }
RoboErik4646d282014-05-13 10:13:04 -0700738 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900739 Settings.Secure.putStringForUser(mContentResolver,
740 Settings.System.MEDIA_BUTTON_RECEIVER,
741 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
742 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700743 }
RoboErikc8f92d12015-01-05 16:48:07 -0800744
Jaewan Kima7dce192017-02-16 17:10:54 +0900745 private void pushAddressedPlayerChangedLocked() {
746 if (mCallback == null) {
747 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800748 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900749 try {
750 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
751 if (mediaButtonSession != null) {
752 mCallback.onAddressedPlayerChangedToMediaSession(
753 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
754 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
755 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
756 mCurrentFullUserRecord.mLastMediaButtonReceiver
757 .getIntent().getComponent());
758 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
759 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
760 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
761 }
762 } catch (RemoteException e) {
763 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
764 }
765 }
766
767 private MediaSessionRecord getMediaButtonSessionLocked() {
Jaewan Kim92dea332017-02-02 11:52:08 +0900768 return isGlobalPriorityActiveLocked()
769 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
RoboErikc8f92d12015-01-05 16:48:07 -0800770 }
RoboErik4646d282014-05-13 10:13:04 -0700771 }
772
RoboErik2e7a9162014-06-04 16:53:45 -0700773 final class SessionsListenerRecord implements IBinder.DeathRecipient {
774 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700775 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700776 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700777 private final int mPid;
778 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700779
RoboErik7aef77b2014-08-08 15:56:54 -0700780 public SessionsListenerRecord(IActiveSessionsListener listener,
781 ComponentName componentName,
782 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700783 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700784 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700785 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700786 mPid = pid;
787 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700788 }
789
790 @Override
791 public void binderDied() {
792 synchronized (mLock) {
793 mSessionsListeners.remove(this);
794 }
795 }
796 }
797
RoboErik7aef77b2014-08-08 15:56:54 -0700798 final class SettingsObserver extends ContentObserver {
799 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
800 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
801
802 private SettingsObserver() {
803 super(null);
804 }
805
806 private void observe() {
807 mContentResolver.registerContentObserver(mSecureSettingsUri,
808 false, this, UserHandle.USER_ALL);
809 }
810
811 @Override
812 public void onChange(boolean selfChange, Uri uri) {
813 updateActiveSessionListeners();
814 }
815 }
816
RoboErik07c70772014-03-20 13:33:52 -0700817 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700818 private static final String EXTRA_WAKELOCK_ACQUIRED =
819 "android.media.AudioService.WAKELOCK_ACQUIRED";
820 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
821
RoboErik9a9d0b52014-05-20 14:53:39 -0700822 private boolean mVoiceButtonDown = false;
823 private boolean mVoiceButtonHandled = false;
824
RoboErik07c70772014-03-20 13:33:52 -0700825 @Override
RoboErika5b02322014-05-07 17:05:49 -0700826 public ISession createSession(String packageName, ISessionCallback cb, String tag,
827 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800828 final int pid = Binder.getCallingPid();
829 final int uid = Binder.getCallingUid();
830 final long token = Binder.clearCallingIdentity();
831 try {
832 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700833 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
834 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800835 if (cb == null) {
836 throw new IllegalArgumentException("Controller callback cannot be null");
837 }
RoboErika5b02322014-05-07 17:05:49 -0700838 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
839 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700840 } finally {
841 Binder.restoreCallingIdentity(token);
842 }
843 }
844
845 @Override
RoboErika5b02322014-05-07 17:05:49 -0700846 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700847 final int pid = Binder.getCallingPid();
848 final int uid = Binder.getCallingUid();
849 final long token = Binder.clearCallingIdentity();
850
851 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700852 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700853 ArrayList<IBinder> binders = new ArrayList<IBinder>();
854 synchronized (mLock) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900855 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
856 for (MediaSessionRecord record : records) {
857 binders.add(record.getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700858 }
859 }
860 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800861 } finally {
862 Binder.restoreCallingIdentity(token);
863 }
864 }
RoboErika278ea72014-04-24 14:49:01 -0700865
RoboErik2e7a9162014-06-04 16:53:45 -0700866 @Override
867 public void addSessionsListener(IActiveSessionsListener listener,
868 ComponentName componentName, int userId) throws RemoteException {
869 final int pid = Binder.getCallingPid();
870 final int uid = Binder.getCallingUid();
871 final long token = Binder.clearCallingIdentity();
872
873 try {
874 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
875 synchronized (mLock) {
876 int index = findIndexOfSessionsListenerLocked(listener);
877 if (index != -1) {
878 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
879 return;
880 }
881 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700882 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700883 try {
884 listener.asBinder().linkToDeath(record, 0);
885 } catch (RemoteException e) {
886 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
887 return;
888 }
889 mSessionsListeners.add(record);
890 }
891 } finally {
892 Binder.restoreCallingIdentity(token);
893 }
894 }
895
896 @Override
897 public void removeSessionsListener(IActiveSessionsListener listener)
898 throws RemoteException {
899 synchronized (mLock) {
900 int index = findIndexOfSessionsListenerLocked(listener);
901 if (index != -1) {
902 SessionsListenerRecord record = mSessionsListeners.remove(index);
903 try {
904 record.mListener.asBinder().unlinkToDeath(record, 0);
905 } catch (Exception e) {
906 // ignore exceptions, the record is being removed
907 }
908 }
909 }
910 }
911
RoboErik8a2cfc32014-05-16 11:19:38 -0700912 /**
913 * Handles the dispatching of the media button events to one of the
914 * registered listeners, or if there was none, broadcast an
915 * ACTION_MEDIA_BUTTON intent to the rest of the system.
916 *
917 * @param keyEvent a non-null KeyEvent whose key code is one of the
918 * supported media buttons
919 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
920 * while this key event is dispatched.
921 */
922 @Override
923 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
924 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
925 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
926 return;
927 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700928
RoboErik8a2cfc32014-05-16 11:19:38 -0700929 final int pid = Binder.getCallingPid();
930 final int uid = Binder.getCallingUid();
931 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700932 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700933 if (DEBUG) {
934 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
935 + keyEvent);
936 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700937 if (!isUserSetupComplete()) {
938 // Global media key handling can have the side-effect of starting new
939 // activities which is undesirable while setup is in progress.
940 Slog.i(TAG, "Not dispatching media key event because user "
941 + "setup is in progress.");
942 return;
943 }
944
RoboErik8a2cfc32014-05-16 11:19:38 -0700945 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900946 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +0900947 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
948 // Prevent dispatching key event through reflection while the global
949 // priority session is active.
950 Slog.i(TAG, "Only the system can dispatch media key event "
951 + "to the global priority session.");
952 return;
953 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900954 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900955 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +0900956 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900957 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900958 }
959 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900960 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim98003d32017-02-24 18:33:04 +0900961 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
962 return;
963 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900964 Log.w(TAG, "Failed to send " + keyEvent
965 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900966 }
967 }
968 }
Jaewan Kim51255012017-02-24 16:19:14 +0900969 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800970 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700971 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +0900972 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700973 }
974 }
975 } finally {
976 Binder.restoreCallingIdentity(token);
977 }
978 }
979
RoboErika278ea72014-04-24 14:49:01 -0700980 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +0900981 public void setCallback(ICallback callback) {
982 final int pid = Binder.getCallingPid();
983 final int uid = Binder.getCallingUid();
984 final long token = Binder.clearCallingIdentity();
985 try {
Amith Yamasanie259ad22017-04-24 11:30:19 -0700986 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900987 throw new SecurityException("Only Bluetooth service processes can set"
988 + " Callback");
989 }
990 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900991 int userId = UserHandle.getUserId(uid);
992 FullUserRecord user = getFullUserRecordLocked(userId);
993 if (user == null || user.mFullUserId != userId) {
994 Log.w(TAG, "Only the full user can set the callback"
995 + ", userId=" + userId);
996 return;
997 }
998 user.mCallback = callback;
999 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +09001000 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +09001001 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001002 return;
1003 }
1004 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001005 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +09001006 new IBinder.DeathRecipient() {
1007 @Override
1008 public void binderDied() {
1009 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001010 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001011 }
1012 }
1013 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +09001014 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +09001015 } catch (RemoteException e) {
1016 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +09001017 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001018 }
1019 }
1020 } finally {
1021 Binder.restoreCallingIdentity(token);
1022 }
1023 }
1024
1025 @Override
Jaewan Kim50269362016-12-23 11:22:02 +09001026 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
1027 final int pid = Binder.getCallingPid();
1028 final int uid = Binder.getCallingUid();
1029 final long token = Binder.clearCallingIdentity();
1030 try {
1031 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
1032 if (getContext().checkPermission(
1033 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
1034 != PackageManager.PERMISSION_GRANTED) {
1035 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
1036 " permission.");
1037 }
1038
1039 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001040 int userId = UserHandle.getUserId(uid);
1041 FullUserRecord user = getFullUserRecordLocked(userId);
1042 if (user == null || user.mFullUserId != userId) {
1043 Log.w(TAG, "Only the full user can set the volume key long-press listener"
1044 + ", userId=" + userId);
1045 return;
1046 }
Jaewan Kim50269362016-12-23 11:22:02 +09001047 if (user.mOnVolumeKeyLongPressListener != null &&
1048 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001049 Log.w(TAG, "The volume key long-press listener cannot be reset"
1050 + " by another app , mOnVolumeKeyLongPressListener="
1051 + user.mOnVolumeKeyLongPressListenerUid
1052 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +09001053 return;
1054 }
1055
1056 user.mOnVolumeKeyLongPressListener = listener;
1057 user.mOnVolumeKeyLongPressListenerUid = uid;
1058
Jaewan Kima7dce192017-02-16 17:10:54 +09001059 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001060 + listener + " is set by " + getCallingPackageName(uid));
1061
1062 if (user.mOnVolumeKeyLongPressListener != null) {
1063 try {
1064 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1065 new IBinder.DeathRecipient() {
1066 @Override
1067 public void binderDied() {
1068 synchronized (mLock) {
1069 user.mOnVolumeKeyLongPressListener = null;
1070 }
1071 }
1072 }, 0);
1073 } catch (RemoteException e) {
1074 Log.w(TAG, "Failed to set death recipient "
1075 + user.mOnVolumeKeyLongPressListener);
1076 user.mOnVolumeKeyLongPressListener = null;
1077 }
1078 }
1079 }
1080 } finally {
1081 Binder.restoreCallingIdentity(token);
1082 }
1083 }
1084
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001085 @Override
1086 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1087 final int pid = Binder.getCallingPid();
1088 final int uid = Binder.getCallingUid();
1089 final long token = Binder.clearCallingIdentity();
1090 try {
1091 // Enforce SET_MEDIA_KEY_LISTENER permission.
1092 if (getContext().checkPermission(
1093 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1094 != PackageManager.PERMISSION_GRANTED) {
1095 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1096 " permission.");
1097 }
1098
1099 synchronized (mLock) {
1100 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001101 FullUserRecord user = getFullUserRecordLocked(userId);
1102 if (user == null || user.mFullUserId != userId) {
1103 Log.w(TAG, "Only the full user can set the media key listener"
1104 + ", userId=" + userId);
1105 return;
1106 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001107 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001108 Log.w(TAG, "The media key listener cannot be reset by another app. "
1109 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1110 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001111 return;
1112 }
1113
1114 user.mOnMediaKeyListener = listener;
1115 user.mOnMediaKeyListenerUid = uid;
1116
Jaewan Kima7dce192017-02-16 17:10:54 +09001117 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001118 + " is set by " + getCallingPackageName(uid));
1119
1120 if (user.mOnMediaKeyListener != null) {
1121 try {
1122 user.mOnMediaKeyListener.asBinder().linkToDeath(
1123 new IBinder.DeathRecipient() {
1124 @Override
1125 public void binderDied() {
1126 synchronized (mLock) {
1127 user.mOnMediaKeyListener = null;
1128 }
1129 }
1130 }, 0);
1131 } catch (RemoteException e) {
1132 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1133 user.mOnMediaKeyListener = null;
1134 }
1135 }
1136 }
1137 } finally {
1138 Binder.restoreCallingIdentity(token);
1139 }
1140 }
1141
Jaewan Kim50269362016-12-23 11:22:02 +09001142 /**
1143 * Handles the dispatching of the volume button events to one of the
1144 * registered listeners. If there's a volume key long-press listener and
1145 * there's no active global priority session, long-pressess will be sent to the
1146 * long-press listener instead of adjusting volume.
1147 *
1148 * @param keyEvent a non-null KeyEvent whose key code is one of the
1149 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1150 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1151 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1152 * @param stream stream type to adjust volume.
1153 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1154 */
1155 @Override
1156 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1157 if (keyEvent == null ||
1158 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1159 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1160 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1161 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1162 return;
1163 }
1164
1165 final int pid = Binder.getCallingPid();
1166 final int uid = Binder.getCallingUid();
1167 final long token = Binder.clearCallingIdentity();
1168
Jaewan Kimb2781e72017-03-02 09:57:09 +09001169 if (DEBUG_KEY_EVENT) {
Jaewan Kim50269362016-12-23 11:22:02 +09001170 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1171 + keyEvent);
1172 }
1173
1174 try {
1175 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001176 if (isGlobalPriorityActiveLocked()
1177 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001178 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1179 } else {
1180 // TODO: Consider the case when both volume up and down keys are pressed
1181 // at the same time.
1182 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1183 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001184 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001185 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1186 KeyEvent.obtain(keyEvent);
1187 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1188 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001189 mHandler.sendMessageDelayed(
1190 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001191 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1192 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001193 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001194 }
1195 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
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) {
Jaewan Kim50269362016-12-23 11:22:02 +09001198 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001199 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001200 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001201 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001202 }
1203 dispatchVolumeKeyLongPressLocked(keyEvent);
1204 }
1205 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001206 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001207 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
1208 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1209 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001210 // Short-press. Should change volume.
1211 dispatchVolumeKeyEventLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001212 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1213 mCurrentFullUserRecord.mInitialDownVolumeStream,
1214 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001215 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1216 } else {
1217 dispatchVolumeKeyLongPressLocked(keyEvent);
1218 }
1219 }
1220 }
1221 }
1222 } finally {
1223 Binder.restoreCallingIdentity(token);
1224 }
1225 }
1226
Jaewan Kim50269362016-12-23 11:22:02 +09001227 private void dispatchVolumeKeyEventLocked(
1228 KeyEvent keyEvent, int stream, boolean musicOnly) {
1229 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1230 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1231 int direction = 0;
1232 boolean isMute = false;
1233 switch (keyEvent.getKeyCode()) {
1234 case KeyEvent.KEYCODE_VOLUME_UP:
1235 direction = AudioManager.ADJUST_RAISE;
1236 break;
1237 case KeyEvent.KEYCODE_VOLUME_DOWN:
1238 direction = AudioManager.ADJUST_LOWER;
1239 break;
1240 case KeyEvent.KEYCODE_VOLUME_MUTE:
1241 isMute = true;
1242 break;
1243 }
1244 if (down || up) {
1245 int flags = AudioManager.FLAG_FROM_KEY;
1246 if (musicOnly) {
1247 // This flag is used when the screen is off to only affect active media.
1248 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1249 } else {
1250 // These flags are consistent with the home screen
1251 if (up) {
1252 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1253 } else {
1254 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1255 }
1256 }
1257 if (direction != 0) {
1258 // If this is action up we want to send a beep for non-music events
1259 if (up) {
1260 direction = 0;
1261 }
1262 dispatchAdjustVolumeLocked(stream, direction, flags);
1263 } else if (isMute) {
1264 if (down && keyEvent.getRepeatCount() == 0) {
1265 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1266 }
1267 }
1268 }
1269 }
1270
1271 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001272 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001273 final long token = Binder.clearCallingIdentity();
1274 try {
1275 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001276 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001277 }
1278 } finally {
1279 Binder.restoreCallingIdentity(token);
1280 }
1281 }
1282
1283 @Override
RoboErik19c95182014-06-23 15:38:48 -07001284 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1285 final int pid = Binder.getCallingPid();
1286 final int uid = Binder.getCallingUid();
1287 final long token = Binder.clearCallingIdentity();
1288 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001289 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001290 mRvc = rvc;
1291 } finally {
1292 Binder.restoreCallingIdentity(token);
1293 }
1294 }
1295
1296 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001297 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001298 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001299 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001300 }
RoboErikde9ba392014-09-26 12:51:01 -07001301 }
1302
1303 @Override
RoboErika278ea72014-04-24 14:49:01 -07001304 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001305 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
RoboErika278ea72014-04-24 14:49:01 -07001306
1307 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1308 pw.println();
1309
1310 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001311 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001312 pw.println("Global priority session is " + mGlobalPrioritySession);
Jaewan Kim101b4d52017-05-18 13:23:11 +09001313 if (mGlobalPrioritySession != null) {
1314 mGlobalPrioritySession.dump(pw, " ");
1315 }
RoboErik4646d282014-05-13 10:13:04 -07001316 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001317 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001318 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001319 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001320 }
Jaewan Kim92dea332017-02-02 11:52:08 +09001321 mAudioPlaybackMonitor.dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001322 }
1323 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001324
RoboErik2e7a9162014-06-04 16:53:45 -07001325 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1326 final int uid) {
1327 String packageName = null;
1328 if (componentName != null) {
1329 // If they gave us a component name verify they own the
1330 // package
1331 packageName = componentName.getPackageName();
1332 enforcePackageName(packageName, uid);
1333 }
1334 // Check that they can make calls on behalf of the user and
1335 // get the final user id
1336 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1337 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1338 // Check if they have the permissions or their component is
1339 // enabled for the user they're calling from.
1340 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1341 return resolvedUserId;
1342 }
1343
Jaewan Kim50269362016-12-23 11:22:02 +09001344 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001345 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1346 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001347
RoboErik9c785402014-11-11 16:52:26 -08001348 boolean preferSuggestedStream = false;
1349 if (isValidLocalStreamType(suggestedStream)
1350 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1351 preferSuggestedStream = true;
1352 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001353 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001354 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1355 + flags + ", suggestedStream=" + suggestedStream
1356 + ", preferSuggestedStream=" + preferSuggestedStream);
1357 }
RoboErik9c785402014-11-11 16:52:26 -08001358 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001359 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1360 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001361 if (DEBUG) {
1362 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001363 }
RoboErikb7c014c2014-07-22 15:58:22 -07001364 return;
RoboErik3c45c292014-07-08 16:47:31 -07001365 }
Shibin George19e84042016-06-14 20:42:13 +05301366
1367 // Execute mAudioService.adjustSuggestedStreamVolume() on
1368 // handler thread of MediaSessionService.
1369 // This will release the MediaSessionService.mLock sooner and avoid
1370 // a potential deadlock between MediaSessionService.mLock and
1371 // ActivityManagerService lock.
1372 mHandler.post(new Runnable() {
1373 @Override
1374 public void run() {
1375 try {
1376 String packageName = getContext().getOpPackageName();
1377 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1378 flags, packageName, TAG);
1379 } catch (RemoteException e) {
1380 Log.e(TAG, "Error adjusting default volume.", e);
1381 }
1382 }
1383 });
RoboErikb69ffd42014-05-30 14:57:59 -07001384 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001385 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001386 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001387 }
1388 }
1389
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001390 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001391 int action = keyEvent.getAction();
1392 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1393 if (action == KeyEvent.ACTION_DOWN) {
1394 if (keyEvent.getRepeatCount() == 0) {
1395 mVoiceButtonDown = true;
1396 mVoiceButtonHandled = false;
1397 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1398 mVoiceButtonHandled = true;
1399 startVoiceInput(needWakeLock);
1400 }
1401 } else if (action == KeyEvent.ACTION_UP) {
1402 if (mVoiceButtonDown) {
1403 mVoiceButtonDown = false;
1404 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1405 // Resend the down then send this event through
1406 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001407 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1408 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001409 }
1410 }
1411 }
1412 }
1413
Jaewan Kim98003d32017-02-24 18:33:04 +09001414 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001415 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001416 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001417 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001418 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001419 }
1420 if (needWakeLock) {
1421 mKeyEventReceiver.aquireWakeLockLocked();
1422 }
Jaewan Kim50269362016-12-23 11:22:02 +09001423 // 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 -07001424 session.sendMediaButton(keyEvent,
1425 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001426 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001427 getContext().getPackageName());
Jaewan Kima7dce192017-02-16 17:10:54 +09001428 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001429 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001430 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
1431 keyEvent,
Jaewan Kimbd16f452017-02-03 16:21:38 +09001432 new MediaSession.Token(session.getControllerBinder()));
1433 } catch (RemoteException e) {
1434 Log.w(TAG, "Failed to send callback", e);
1435 }
1436 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001437 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1438 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1439 if (needWakeLock) {
1440 mKeyEventReceiver.aquireWakeLockLocked();
1441 }
1442 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1443 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1444 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1445 try {
1446 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1447 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1448 if (DEBUG_KEY_EVENT) {
1449 Log.d(TAG, "Sending " + keyEvent
Jaewan Kim92dea332017-02-02 11:52:08 +09001450 + " to the last known PendingIntent " + receiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001451 }
1452 receiver.send(getContext(),
1453 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1454 mediaButtonIntent, mKeyEventReceiver, mHandler);
1455 if (mCurrentFullUserRecord.mCallback != null) {
1456 ComponentName componentName = mCurrentFullUserRecord
1457 .mLastMediaButtonReceiver.getIntent().getComponent();
1458 if (componentName != null) {
1459 mCurrentFullUserRecord.mCallback
1460 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1461 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001462 }
RoboErikc8f92d12015-01-05 16:48:07 -08001463 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001464 } else {
1465 ComponentName receiver =
1466 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1467 if (DEBUG_KEY_EVENT) {
1468 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1469 + receiver);
1470 }
1471 mediaButtonIntent.setComponent(receiver);
1472 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim92dea332017-02-02 11:52:08 +09001473 UserHandle.of(mCurrentFullUserRecord
1474 .mRestoredMediaButtonReceiverUserId));
Jaewan Kima7dce192017-02-16 17:10:54 +09001475 if (mCurrentFullUserRecord.mCallback != null) {
1476 mCurrentFullUserRecord.mCallback
1477 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1478 keyEvent, receiver);
1479 }
RoboErikb214efb2014-07-24 13:20:30 -07001480 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001481 } catch (CanceledException e) {
1482 Log.i(TAG, "Error sending key event to media button receiver "
1483 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1484 } catch (RemoteException e) {
1485 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001486 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001487 }
1488 }
1489
1490 private void startVoiceInput(boolean needWakeLock) {
1491 Intent voiceIntent = null;
1492 // select which type of search to launch:
1493 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1494 // - device locked or screen off: action is
1495 // ACTION_VOICE_SEARCH_HANDS_FREE
1496 // with EXTRA_SECURE set to true if the device is securely locked
1497 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1498 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1499 if (!isLocked && pm.isScreenOn()) {
1500 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1501 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1502 } else {
1503 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1504 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1505 isLocked && mKeyguardManager.isKeyguardSecure());
1506 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1507 }
1508 // start the search activity
1509 if (needWakeLock) {
1510 mMediaEventWakeLock.acquire();
1511 }
1512 try {
1513 if (voiceIntent != null) {
1514 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1515 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001516 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001517 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1518 }
1519 } catch (ActivityNotFoundException e) {
1520 Log.w(TAG, "No activity for search: " + e);
1521 } finally {
1522 if (needWakeLock) {
1523 mMediaEventWakeLock.release();
1524 }
1525 }
1526 }
1527
1528 private boolean isVoiceKey(int keyCode) {
Jaewan Kimba18d8e2017-05-12 17:37:57 +09001529 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
1530 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
RoboErik9a9d0b52014-05-20 14:53:39 -07001531 }
1532
Jeff Brown38d3feb2015-03-19 18:26:30 -07001533 private boolean isUserSetupComplete() {
1534 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1535 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1536 }
1537
RoboErik9c785402014-11-11 16:52:26 -08001538 // we only handle public stream types, which are 0-5
1539 private boolean isValidLocalStreamType(int streamType) {
1540 return streamType >= AudioManager.STREAM_VOICE_CALL
1541 && streamType <= AudioManager.STREAM_NOTIFICATION;
1542 }
1543
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001544 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1545 private KeyEvent mKeyEvent;
1546 private boolean mNeedWakeLock;
1547 private boolean mHandled;
1548
1549 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1550 super(mHandler);
1551 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1552 mKeyEvent = keyEvent;
1553 mNeedWakeLock = needWakeLock;
1554 }
1555
1556 @Override
1557 public void run() {
1558 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1559 dispatchMediaKeyEvent();
1560 }
1561
1562 @Override
1563 protected void onReceiveResult(int resultCode, Bundle resultData) {
1564 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1565 mHandled = true;
1566 mHandler.removeCallbacks(this);
1567 return;
1568 }
1569 dispatchMediaKeyEvent();
1570 }
1571
1572 private void dispatchMediaKeyEvent() {
1573 if (mHandled) {
1574 return;
1575 }
1576 mHandled = true;
1577 mHandler.removeCallbacks(this);
1578 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001579 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001580 && isVoiceKey(mKeyEvent.getKeyCode())) {
1581 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1582 } else {
1583 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1584 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001585 }
1586 }
1587 }
1588
RoboErik418c10c2014-05-19 09:25:25 -07001589 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1590
RoboErikb214efb2014-07-24 13:20:30 -07001591 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1592 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001593 private final Handler mHandler;
1594 private int mRefCount = 0;
1595 private int mLastTimeoutId = 0;
1596
1597 public KeyEventWakeLockReceiver(Handler handler) {
1598 super(handler);
1599 mHandler = handler;
1600 }
1601
1602 public void onTimeout() {
1603 synchronized (mLock) {
1604 if (mRefCount == 0) {
1605 // We've already released it, so just return
1606 return;
1607 }
1608 mLastTimeoutId++;
1609 mRefCount = 0;
1610 releaseWakeLockLocked();
1611 }
1612 }
1613
1614 public void aquireWakeLockLocked() {
1615 if (mRefCount == 0) {
1616 mMediaEventWakeLock.acquire();
1617 }
1618 mRefCount++;
1619 mHandler.removeCallbacks(this);
1620 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1621
1622 }
1623
1624 @Override
1625 public void run() {
1626 onTimeout();
1627 }
1628
RoboErik8a2cfc32014-05-16 11:19:38 -07001629 @Override
1630 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001631 if (resultCode < mLastTimeoutId) {
1632 // Ignore results from calls that were before the last
1633 // timeout, just in case.
1634 return;
1635 } else {
1636 synchronized (mLock) {
1637 if (mRefCount > 0) {
1638 mRefCount--;
1639 if (mRefCount == 0) {
1640 releaseWakeLockLocked();
1641 }
1642 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001643 }
1644 }
1645 }
RoboErik418c10c2014-05-19 09:25:25 -07001646
1647 private void releaseWakeLockLocked() {
1648 mMediaEventWakeLock.release();
1649 mHandler.removeCallbacks(this);
1650 }
RoboErikb214efb2014-07-24 13:20:30 -07001651
1652 @Override
1653 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1654 String resultData, Bundle resultExtras) {
1655 onReceiveResult(resultCode, null);
1656 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001657 };
1658
1659 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1660 @Override
1661 public void onReceive(Context context, Intent intent) {
1662 if (intent == null) {
1663 return;
1664 }
1665 Bundle extras = intent.getExtras();
1666 if (extras == null) {
1667 return;
1668 }
1669 synchronized (mLock) {
1670 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1671 && mMediaEventWakeLock.isHeld()) {
1672 mMediaEventWakeLock.release();
1673 }
1674 }
1675 }
1676 };
RoboErik01fe6612014-02-13 14:19:04 -08001677 }
1678
RoboErik2e7a9162014-06-04 16:53:45 -07001679 final class MessageHandler extends Handler {
1680 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001681 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
Jaewan Kim92dea332017-02-02 11:52:08 +09001682 private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
RoboErik2e7a9162014-06-04 16:53:45 -07001683
1684 @Override
1685 public void handleMessage(Message msg) {
1686 switch (msg.what) {
1687 case MSG_SESSIONS_CHANGED:
Jaewan Kim92dea332017-02-02 11:52:08 +09001688 pushSessionsChanged((int) msg.obj);
RoboErik2e7a9162014-06-04 16:53:45 -07001689 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001690 case MSG_VOLUME_INITIAL_DOWN:
1691 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001692 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001693 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1694 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1695 // Mark that the key is already handled.
1696 user.mInitialDownVolumeKeyEvent = null;
1697 }
1698 }
1699 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001700 }
1701 }
1702
Jaewan Kim92dea332017-02-02 11:52:08 +09001703 public void postSessionsChanged(int userId) {
1704 // Use object instead of the arguments when posting message to remove pending requests.
1705 Integer userIdInteger = mIntegerCache.get(userId);
1706 if (userIdInteger == null) {
1707 userIdInteger = Integer.valueOf(userId);
1708 mIntegerCache.put(userId, userIdInteger);
1709 }
1710 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
1711 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
RoboErik2e7a9162014-06-04 16:53:45 -07001712 }
1713 }
RoboErik01fe6612014-02-13 14:19:04 -08001714}