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