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