blob: 6752c641995fa151b593726ff0c47239bb319168 [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
Jaewan Kimf7a77062018-01-27 01:34:24 +090019import static android.media.SessionToken2.TYPE_SESSION;
20
RoboErike7880d82014-04-30 12:48:25 -070021import android.app.ActivityManager;
Julia Reynoldsb852e562017-06-06 16:14:18 -040022import android.app.INotificationManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070023import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070024import android.app.PendingIntent;
25import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070026import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070027import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070028import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070029import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080030import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070031import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070032import android.content.pm.PackageManager;
Jaewan Kimf7a77062018-01-27 01:34:24 +090033import android.content.pm.PackageManager.NameNotFoundException;
Jaewan Kimceb6b6e2018-01-21 20:56:10 +090034import android.content.pm.ResolveInfo;
35import android.content.pm.ServiceInfo;
Jaewan Kima7dce192017-02-16 17:10:54 +090036import android.content.pm.UserInfo;
RoboErik7aef77b2014-08-08 15:56:54 -070037import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070038import android.media.AudioManager;
Sungsoo Lim875e6972017-11-03 02:22:35 +000039import android.media.AudioPlaybackConfiguration;
RoboErik94c716e2014-09-14 13:54:31 -070040import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070041import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070042import android.media.IRemoteVolumeController;
Jaewan Kim379e30d2018-01-29 11:57:04 +090043import android.media.ISessionTokensListener;
Jaewan Kimbcecf312018-01-23 19:30:42 +090044import android.media.MediaLibraryService2;
Jaewan Kimceb6b6e2018-01-21 20:56:10 +090045import android.media.MediaSessionService2;
Jaewan Kim04de5de2018-01-25 02:24:03 +090046import android.media.SessionToken2;
RoboErik2e7a9162014-06-04 16:53:45 -070047import android.media.session.IActiveSessionsListener;
Jaewan Kimbd16f452017-02-03 16:21:38 +090048import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080049import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090050import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070051import android.media.session.ISession;
52import android.media.session.ISessionCallback;
53import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070054import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080055import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070056import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080057import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070058import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080059import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070060import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070061import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070062import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090063import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080064import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070065import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070066import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070067import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090068import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070069import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070070import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080071import android.text.TextUtils;
72import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070073import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070074import android.util.SparseArray;
Jaewan Kima7dce192017-02-16 17:10:54 +090075import android.util.SparseIntArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070076import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090077import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080078
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060079import com.android.internal.util.DumpUtils;
RoboErik01fe6612014-02-13 14:19:04 -080080import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070081import com.android.server.Watchdog;
82import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080083
RoboErika278ea72014-04-24 14:49:01 -070084import java.io.FileDescriptor;
85import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080086import java.util.ArrayList;
RoboErike7880d82014-04-30 12:48:25 -070087import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080088
89/**
90 * System implementation of MediaSessionManager
91 */
RoboErika278ea72014-04-24 14:49:01 -070092public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080093 private static final String TAG = "MediaSessionService";
Jaewan Kim92dea332017-02-02 11:52:08 +090094 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090095 // Leave log for key event always.
96 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080097
RoboErik418c10c2014-05-19 09:25:25 -070098 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080099 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -0700100
RoboErik01fe6612014-02-13 14:19:04 -0800101 private final SessionManagerImpl mSessionManagerImpl;
102
Jaewan Kima7dce192017-02-16 17:10:54 +0900103 // Keeps the full user id for each user.
104 private final SparseIntArray mFullUserIds = new SparseIntArray();
105 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -0700106 private final ArrayList<SessionsListenerRecord> mSessionsListeners
107 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800108 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700109 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700110 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900111 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800112
RoboErik9a9d0b52014-05-20 14:53:39 -0700113 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700114 private IAudioService mAudioService;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700115 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700116 private SettingsObserver mSettingsObserver;
Julia Reynoldsb852e562017-06-06 16:14:18 -0400117 private INotificationManager mNotificationManager;
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900118 private boolean mHasFeatureLeanback;
RoboErik9a9d0b52014-05-20 14:53:39 -0700119
Jaewan Kima7dce192017-02-16 17:10:54 +0900120 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
121 // It's always not null after the MediaSessionService is started.
122 private FullUserRecord mCurrentFullUserRecord;
123 private MediaSessionRecord mGlobalPrioritySession;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000124 private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
RoboErike7880d82014-04-30 12:48:25 -0700125
RoboErik19c95182014-06-23 15:38:48 -0700126 // Used to notify system UI when remote volume was changed. TODO find a
127 // better way to handle this.
128 private IRemoteVolumeController mRvc;
129
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900130 // MediaSession2 support
131 // TODO(jaewan): Support multi-user and managed profile.
132 // TODO(jaewan): Make it priority list for handling volume/media key.
133 private final List<MediaSession2Record> mSessions = new ArrayList<>();
134
135 private final MediaSession2Record.SessionDestroyedListener mSessionDestroyedListener =
Jaewan Kimf7a77062018-01-27 01:34:24 +0900136 (record) -> {
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900137 synchronized (mLock) {
Jaewan Kimf7a77062018-01-27 01:34:24 +0900138 destroySessionLocked(record);
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900139 }
140 };
141
RoboErik01fe6612014-02-13 14:19:04 -0800142 public MediaSessionService(Context context) {
143 super(context);
144 mSessionManagerImpl = new SessionManagerImpl();
RoboErik8a2cfc32014-05-16 11:19:38 -0700145 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
146 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900147 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
Julia Reynoldsb852e562017-06-06 16:14:18 -0400148 mNotificationManager = INotificationManager.Stub.asInterface(
149 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
RoboErik01fe6612014-02-13 14:19:04 -0800150 }
151
152 @Override
153 public void onStart() {
154 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700155 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700156 mKeyguardManager =
157 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700158 mAudioService = getAudioService();
Sungsoo Lim875e6972017-11-03 02:22:35 +0000159 mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
160 mAudioPlayerStateMonitor.registerListener(
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900161 (config, isRemoved) -> {
162 if (isRemoved || !config.isActive() || config.getPlayerType()
163 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
164 return;
Jaewan Kim92dea332017-02-02 11:52:08 +0900165 }
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900166 synchronized (mLock) {
167 FullUserRecord user = getFullUserRecordLocked(
168 UserHandle.getUserId(config.getClientUid()));
169 if (user != null) {
170 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
171 }
172 }
173 }, null /* handler */);
Sungsoo Lim875e6972017-11-03 02:22:35 +0000174 mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700175 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700176 mSettingsObserver = new SettingsObserver();
177 mSettingsObserver.observe();
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900178 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
179 PackageManager.FEATURE_LEANBACK);
RoboErikc8f92d12015-01-05 16:48:07 -0800180
181 updateUser();
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900182
183 // TODO(jaewan): Query per users
184 // TODO(jaewan): Add listener to know changes in list of services.
185 // Refer TvInputManagerService.registerBroadcastReceivers()
186 buildMediaSessionService2List();
RoboErikb69ffd42014-05-30 14:57:59 -0700187 }
188
189 private IAudioService getAudioService() {
190 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
191 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700192 }
193
Jaewan Kima7dce192017-02-16 17:10:54 +0900194 private boolean isGlobalPriorityActiveLocked() {
195 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
196 }
197
RoboErika8f95142014-05-05 14:23:49 -0700198 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700199 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900200 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
Jaewan Kim101b4d52017-05-18 13:23:11 +0900201 if (user == null) {
202 Log.w(TAG, "Unknown session updated. Ignoring.");
RoboErik4646d282014-05-13 10:13:04 -0700203 return;
204 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900205 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900206 if (DEBUG_KEY_EVENT) {
207 Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
208 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900209 user.pushAddressedPlayerChangedLocked();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900210 } else {
211 if (!user.mPriorityStack.contains(record)) {
212 Log.w(TAG, "Unknown session updated. Ignoring.");
213 return;
214 }
215 user.mPriorityStack.onSessionStateChange(record);
Jaewan Kima7dce192017-02-16 17:10:54 +0900216 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900217 mHandler.postSessionsChanged(record.getUserId());
RoboErike7880d82014-04-30 12:48:25 -0700218 }
219 }
220
Jaewan Kimfa85b602017-10-10 16:49:58 +0900221 public void setGlobalPrioritySession(MediaSessionRecord record) {
222 synchronized (mLock) {
223 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
224 if (mGlobalPrioritySession != record) {
225 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
226 + " to " + record);
227 mGlobalPrioritySession = record;
228 if (user != null && user.mPriorityStack.contains(record)) {
229 // Handle the global priority session separately.
230 // Otherwise, it can be the media button session regardless of the active state
231 // because it or other system components might have been the lastly played media
232 // app.
233 user.mPriorityStack.removeSession(record);
234 }
235 }
236 }
237 }
238
Jaewan Kim101b4d52017-05-18 13:23:11 +0900239 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
Jaewan Kimda74a152017-10-03 23:58:11 +0900240 List<MediaSessionRecord> records = new ArrayList<>();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900241 if (userId == UserHandle.USER_ALL) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900242 int size = mUserRecords.size();
243 for (int i = 0; i < size; i++) {
244 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
245 }
246 } else {
247 FullUserRecord user = getFullUserRecordLocked(userId);
248 if (user == null) {
249 Log.w(TAG, "getSessions failed. Unknown user " + userId);
Jaewan Kimda74a152017-10-03 23:58:11 +0900250 return records;
Jaewan Kim101b4d52017-05-18 13:23:11 +0900251 }
Jaewan Kimda74a152017-10-03 23:58:11 +0900252 records.addAll(user.mPriorityStack.getActiveSessions(userId));
Jaewan Kim101b4d52017-05-18 13:23:11 +0900253 }
254
255 // Return global priority session at the first whenever it's asked.
256 if (isGlobalPriorityActiveLocked()
257 && (userId == UserHandle.USER_ALL
258 || userId == mGlobalPrioritySession.getUserId())) {
259 records.add(0, mGlobalPrioritySession);
260 }
261 return records;
262 }
263
RoboErik9c5b7cb2015-01-15 15:09:09 -0800264 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900265 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800266 */
267 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900268 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800269 return;
270 }
271 try {
272 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
273 } catch (Exception e) {
274 Log.wtf(TAG, "Error sending volume change to system UI.", e);
275 }
276 }
277
Jaewan Kim92dea332017-02-02 11:52:08 +0900278 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
RoboErika8f95142014-05-05 14:23:49 -0700279 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900280 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
281 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700282 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
283 return;
284 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900285 user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
RoboErika8f95142014-05-05 14:23:49 -0700286 }
287 }
288
RoboErik19c95182014-06-23 15:38:48 -0700289 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
290 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900291 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
292 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700293 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
294 return;
295 }
296 pushRemoteVolumeUpdateLocked(record.getUserId());
297 }
298 }
299
RoboErika278ea72014-04-24 14:49:01 -0700300 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900301 public void onStartUser(int userId) {
302 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700303 updateUser();
304 }
305
306 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900307 public void onSwitchUser(int userId) {
308 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700309 updateUser();
310 }
311
312 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900313 public void onStopUser(int userId) {
314 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700315 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900316 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700317 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900318 if (user.mFullUserId == userId) {
319 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
320 mUserRecords.remove(userId);
321 } else {
322 user.destroySessionsForUserLocked(userId);
323 }
RoboErik4646d282014-05-13 10:13:04 -0700324 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900325 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700326 }
327 }
328
329 @Override
RoboErika278ea72014-04-24 14:49:01 -0700330 public void monitor() {
331 synchronized (mLock) {
332 // Check for deadlock
333 }
334 }
335
RoboErik4646d282014-05-13 10:13:04 -0700336 protected void enforcePhoneStatePermission(int pid, int uid) {
337 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
338 != PackageManager.PERMISSION_GRANTED) {
339 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
340 }
341 }
342
RoboErik01fe6612014-02-13 14:19:04 -0800343 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700344 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800345 destroySessionLocked(session);
346 }
347 }
348
349 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700350 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800351 destroySessionLocked(session);
352 }
353 }
354
RoboErik4646d282014-05-13 10:13:04 -0700355 private void updateUser() {
356 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900357 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900358 mFullUserIds.clear();
359 List<UserInfo> allUsers = manager.getUsers();
360 if (allUsers != null) {
361 for (UserInfo userInfo : allUsers) {
362 if (userInfo.isManagedProfile()) {
363 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
364 } else {
365 mFullUserIds.put(userInfo.id, userInfo.id);
366 if (mUserRecords.get(userInfo.id) == null) {
367 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
368 }
369 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900370 }
RoboErik4646d282014-05-13 10:13:04 -0700371 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900372 // Ensure that the current full user exists.
373 int currentFullUserId = ActivityManager.getCurrentUser();
374 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
375 if (mCurrentFullUserRecord == null) {
376 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
377 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
378 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
379 }
380 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700381 }
382 }
383
RoboErik7aef77b2014-08-08 15:56:54 -0700384 private void updateActiveSessionListeners() {
385 synchronized (mLock) {
386 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
387 SessionsListenerRecord listener = mSessionsListeners.get(i);
388 try {
389 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
390 listener.mUserId);
391 } catch (SecurityException e) {
392 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
393 + " is no longer authorized. Disconnecting.");
394 mSessionsListeners.remove(i);
395 try {
396 listener.mListener
397 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
398 } catch (Exception e1) {
399 // ignore
400 }
401 }
402 }
403 }
404 }
405
RoboErik4646d282014-05-13 10:13:04 -0700406 /*
407 * When a session is removed several things need to happen.
408 * 1. We need to remove it from the relevant user.
409 * 2. We need to remove it from the priority stack.
410 * 3. We need to remove it from all sessions.
411 * 4. If this is the system priority session we need to clear it.
412 * 5. We need to unlink to death from the cb binder
413 * 6. We need to tell the session to do any final cleanup (onDestroy)
414 */
RoboErik01fe6612014-02-13 14:19:04 -0800415 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900416 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900417 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900418 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900419 FullUserRecord user = getFullUserRecordLocked(session.getUserId());
Jaewan Kima7dce192017-02-16 17:10:54 +0900420 if (mGlobalPrioritySession == session) {
421 mGlobalPrioritySession = null;
Jaewan Kim92dea332017-02-02 11:52:08 +0900422 if (session.isActive() && user != null) {
423 user.pushAddressedPlayerChangedLocked();
424 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900425 } else {
426 if (user != null) {
427 user.mPriorityStack.removeSession(session);
428 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900429 }
RoboErik4646d282014-05-13 10:13:04 -0700430
431 try {
432 session.getCallback().asBinder().unlinkToDeath(session, 0);
433 } catch (Exception e) {
434 // ignore exceptions while destroying a session.
435 }
436 session.onDestroy();
Jaewan Kim92dea332017-02-02 11:52:08 +0900437 mHandler.postSessionsChanged(session.getUserId());
RoboErik01fe6612014-02-13 14:19:04 -0800438 }
439
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900440 private void buildMediaSessionService2List() {
441 if (DEBUG) {
442 Log.d(TAG, "buildMediaSessionService2List");
443 }
444
445 // TODO(jaewan): Query per users.
Jaewan Kimf7a77062018-01-27 01:34:24 +0900446 // TODO(jaewan): Similar codes are also at the updatable. Can't we share codes?
447 PackageManager manager = getContext().getPackageManager();
Jaewan Kimbcecf312018-01-23 19:30:42 +0900448 List<ResolveInfo> services = new ArrayList<>();
449 // If multiple actions are declared for a service, browser gets higher priority.
Jaewan Kimf7a77062018-01-27 01:34:24 +0900450 List<ResolveInfo> libraryServices = manager.queryIntentServices(
Jaewan Kimbcecf312018-01-23 19:30:42 +0900451 new Intent(MediaLibraryService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
452 if (libraryServices != null) {
453 services.addAll(libraryServices);
454 }
Jaewan Kimf7a77062018-01-27 01:34:24 +0900455 List<ResolveInfo> sessionServices = manager.queryIntentServices(
Jaewan Kimbcecf312018-01-23 19:30:42 +0900456 new Intent(MediaSessionService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
457 if (sessionServices != null) {
458 services.addAll(sessionServices);
459 }
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900460 synchronized (mLock) {
461 mSessions.clear();
462 if (services == null) {
463 return;
464 }
465 for (int i = 0; i < services.size(); i++) {
466 if (services.get(i) == null || services.get(i).serviceInfo == null) {
467 continue;
468 }
469 ServiceInfo serviceInfo = services.get(i).serviceInfo;
Jaewan Kimf7a77062018-01-27 01:34:24 +0900470 int uid;
471 try {
Jaewan Kim44fec2d2018-01-29 21:49:41 +0900472 // TODO(jaewan): Do this per user.
Jaewan Kimf7a77062018-01-27 01:34:24 +0900473 uid = manager.getPackageUid(serviceInfo.packageName,
474 PackageManager.GET_META_DATA);
475 } catch (NameNotFoundException e) {
476 continue;
477 }
Jaewan Kim44fec2d2018-01-29 21:49:41 +0900478
479 try {
480 SessionToken2 token = new SessionToken2(getContext(),
481 serviceInfo.packageName, serviceInfo.name, uid);
Jaewan Kimf7a77062018-01-27 01:34:24 +0900482 MediaSession2Record record = new MediaSession2Record(getContext(),
483 token, mSessionDestroyedListener);
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900484 mSessions.add(record);
Jaewan Kim44fec2d2018-01-29 21:49:41 +0900485 } catch (IllegalArgumentException e) {
486 Log.d(TAG, "Invalid session service", e);
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900487 }
488 }
489 }
490 if (DEBUG) {
491 Log.d(TAG, "Found " + mSessions.size() + " session services");
492 for (int i = 0; i < mSessions.size(); i++) {
493 Log.d(TAG, " " + mSessions.get(i).getToken());
494 }
495 }
496 }
497
Jaewan Kimf7a77062018-01-27 01:34:24 +0900498 private MediaSession2Record getSessionRecordLocked(int uid, String packageName, String id) {
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900499 for (int i = 0; i < mSessions.size(); i++) {
Jaewan Kimf7a77062018-01-27 01:34:24 +0900500 SessionToken2 token = mSessions.get(i).getToken();
501 if (token.getUid() == uid && token.getPackageName().equals(packageName)
502 && token.getId().equals(id)) {
503 return mSessions.get(i);
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900504 }
505 }
506 return null;
507 }
508
Jaewan Kimf7a77062018-01-27 01:34:24 +0900509 private void destroySessionLocked(MediaSession2Record record) {
510 if (DEBUG) {
511 Log.d(TAG, record.toString() + " becomes inactive");
512 }
513 record.onSessionDestroyed();
514 if (record.getToken().getType() == TYPE_SESSION) {
515 mSessions.remove(record);
516 }
517 }
518
RoboErik01fe6612014-02-13 14:19:04 -0800519 private void enforcePackageName(String packageName, int uid) {
520 if (TextUtils.isEmpty(packageName)) {
521 throw new IllegalArgumentException("packageName may not be empty");
522 }
523 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
524 final int packageCount = packages.length;
525 for (int i = 0; i < packageCount; i++) {
526 if (packageName.equals(packages[i])) {
527 return;
528 }
529 }
530 throw new IllegalArgumentException("packageName is not owned by the calling process");
531 }
532
RoboErike7880d82014-04-30 12:48:25 -0700533 /**
534 * Checks a caller's authorization to register an IRemoteControlDisplay.
535 * Authorization is granted if one of the following is true:
536 * <ul>
537 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
538 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700539 * <li>the caller's listener is one of the enabled notification listeners
540 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700541 * </ul>
542 */
RoboErika5b02322014-05-07 17:05:49 -0700543 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
544 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500545 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700546 if (getContext()
547 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
548 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700549 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
550 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700551 throw new SecurityException("Missing permission to control media.");
552 }
553 }
554
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500555 private boolean isCurrentVolumeController(int uid, int pid) {
556 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
557 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500558 }
559
560 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500561 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700562 throw new SecurityException("Only system ui may " + action);
563 }
564 }
565
RoboErika5b02322014-05-07 17:05:49 -0700566 /**
567 * This checks if the component is an enabled notification listener for the
568 * specified user. Enabled components may only operate on behalf of the user
569 * they're running as.
570 *
571 * @param compName The component that is enabled.
572 * @param userId The user id of the caller.
573 * @param forUserId The user id they're making the request on behalf of.
574 * @return True if the component is enabled, false otherwise
575 */
576 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
577 int forUserId) {
578 if (userId != forUserId) {
579 // You may not access another user's content as an enabled listener.
580 return false;
581 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700582 if (DEBUG) {
583 Log.d(TAG, "Checking if enabled notification listener " + compName);
584 }
RoboErike7880d82014-04-30 12:48:25 -0700585 if (compName != null) {
Julia Reynoldsb852e562017-06-06 16:14:18 -0400586 try {
587 return mNotificationManager.isNotificationListenerAccessGrantedForUser(
588 compName, userId);
589 } catch(RemoteException e) {
590 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
RoboErike7880d82014-04-30 12:48:25 -0700591 }
592 }
593 return false;
594 }
595
RoboErika5b02322014-05-07 17:05:49 -0700596 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700597 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800598 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700599 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800600 }
601 }
602
RoboErik4646d282014-05-13 10:13:04 -0700603 /*
604 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700605 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700606 * 2. It needs to be added to all sessions.
607 * 3. It needs to be added to the priority stack.
608 * 4. It needs to be added to the relevant user record.
609 */
RoboErika5b02322014-05-07 17:05:49 -0700610 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
611 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900612 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700613 if (user == null) {
614 Log.wtf(TAG, "Request from invalid user: " + userId);
615 throw new RuntimeException("Session request from invalid user.");
616 }
617
RoboErika5b02322014-05-07 17:05:49 -0700618 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
Jaewan Kim92dea332017-02-02 11:52:08 +0900619 callerPackageName, cb, tag, this, mHandler.getLooper());
RoboErik01fe6612014-02-13 14:19:04 -0800620 try {
621 cb.asBinder().linkToDeath(session, 0);
622 } catch (RemoteException e) {
623 throw new RuntimeException("Media Session owner died prematurely.", e);
624 }
RoboErik4646d282014-05-13 10:13:04 -0700625
Jaewan Kim101b4d52017-05-18 13:23:11 +0900626 user.mPriorityStack.addSession(session);
Jaewan Kim92dea332017-02-02 11:52:08 +0900627 mHandler.postSessionsChanged(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700628
RoboErik01fe6612014-02-13 14:19:04 -0800629 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900630 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800631 }
632 return session;
633 }
634
RoboErik2e7a9162014-06-04 16:53:45 -0700635 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
636 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800637 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700638 return i;
639 }
640 }
641 return -1;
642 }
643
RoboErik2e7a9162014-06-04 16:53:45 -0700644 private void pushSessionsChanged(int userId) {
645 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900646 FullUserRecord user = getFullUserRecordLocked(userId);
647 if (user == null) {
648 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
649 return;
650 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900651 List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700652 int size = records.size();
Jeff Browndba34ba2014-06-24 20:46:03 -0700653 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700654 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700655 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700656 }
RoboErik19c95182014-06-23 15:38:48 -0700657 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700658 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
659 SessionsListenerRecord record = mSessionsListeners.get(i);
660 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
661 try {
662 record.mListener.onActiveSessionsChanged(tokens);
663 } catch (RemoteException e) {
664 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
665 e);
666 mSessionsListeners.remove(i);
667 }
668 }
669 }
670 }
671 }
672
RoboErik19c95182014-06-23 15:38:48 -0700673 private void pushRemoteVolumeUpdateLocked(int userId) {
674 if (mRvc != null) {
675 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900676 FullUserRecord user = getFullUserRecordLocked(userId);
677 if (user == null) {
678 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
679 return;
680 }
681 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700682 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
683 } catch (RemoteException e) {
684 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
685 }
686 }
687 }
688
Jaewan Kim92dea332017-02-02 11:52:08 +0900689 /**
690 * Called when the media button receiver for the {@param record} is changed.
691 *
692 * @param record the media session whose media button receiver is updated.
693 */
694 public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
695 synchronized (mLock) {
696 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
697 MediaSessionRecord mediaButtonSession =
698 user.mPriorityStack.getMediaButtonSession();
699 if (record == mediaButtonSession) {
700 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
701 }
702 }
703 }
704
Jaewan Kim50269362016-12-23 11:22:02 +0900705 private String getCallingPackageName(int uid) {
706 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
707 if (packages != null && packages.length > 0) {
708 return packages[0];
709 }
710 return "";
711 }
712
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900713 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900714 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900715 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900716 } catch (RemoteException e) {
717 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
718 }
719 }
720
Jaewan Kima7dce192017-02-16 17:10:54 +0900721 private FullUserRecord getFullUserRecordLocked(int userId) {
722 int fullUserId = mFullUserIds.get(userId, -1);
723 if (fullUserId < 0) {
724 return null;
725 }
726 return mUserRecords.get(fullUserId);
727 }
728
RoboErik4646d282014-05-13 10:13:04 -0700729 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900730 * Information about a full user and its corresponding managed profiles.
731 *
732 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
733 * them when he/she presses a media/volume button. So keeping media sessions for them in one
734 * place makes more sense and increases the readability.</p>
735 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700736 */
Jaewan Kim92dea332017-02-02 11:52:08 +0900737 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
Jaewan Kima7dce192017-02-16 17:10:54 +0900738 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
739 private final int mFullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900740 private final MediaSessionStack mPriorityStack;
RoboErikb214efb2014-07-24 13:20:30 -0700741 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800742 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900743 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700744
Jaewan Kim50269362016-12-23 11:22:02 +0900745 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
746 private int mOnVolumeKeyLongPressListenerUid;
747 private KeyEvent mInitialDownVolumeKeyEvent;
748 private int mInitialDownVolumeStream;
749 private boolean mInitialDownMusicOnly;
750
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800751 private IOnMediaKeyListener mOnMediaKeyListener;
752 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900753 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800754
Jaewan Kima7dce192017-02-16 17:10:54 +0900755 public FullUserRecord(int fullUserId) {
756 mFullUserId = fullUserId;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000757 mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
Jaewan Kima7dce192017-02-16 17:10:54 +0900758 // Restore the remembered media button receiver before the boot.
759 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
760 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
761 if (mediaButtonReceiver == null) {
762 return;
763 }
764 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
765 if (tokens == null || tokens.length != 2) {
766 return;
767 }
768 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
769 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700770 }
771
Jaewan Kima7dce192017-02-16 17:10:54 +0900772 public void destroySessionsForUserLocked(int userId) {
Jaewan Kim92dea332017-02-02 11:52:08 +0900773 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
Jaewan Kima7dce192017-02-16 17:10:54 +0900774 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700775 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700776 }
777 }
778
RoboErik4646d282014-05-13 10:13:04 -0700779 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900780 pw.print(prefix + "Record for full_user=" + mFullUserId);
781 // Dump managed profile user ids associated with this user.
782 int size = mFullUserIds.size();
783 for (int i = 0; i < size; i++) {
784 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
785 && mFullUserIds.valueAt(i) == mFullUserId) {
786 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
787 }
788 }
789 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700790 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900791 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
792 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900793 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800794 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
795 pw.println(indent + "Media key listener package: " +
796 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900797 pw.println(indent + "Callback: " + mCallback);
798 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
799 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
800 mPriorityStack.dump(pw, indent);
801 }
802
Jaewan Kim92dea332017-02-02 11:52:08 +0900803 @Override
804 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
805 MediaSessionRecord newMediaButtonSession) {
806 if (DEBUG_KEY_EVENT) {
Jaewan Kim98e4aaf2017-05-12 17:06:47 +0900807 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
Jaewan Kim92dea332017-02-02 11:52:08 +0900808 }
809 synchronized (mLock) {
810 if (oldMediaButtonSession != null) {
811 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
812 }
813 if (newMediaButtonSession != null) {
814 rememberMediaButtonReceiverLocked(newMediaButtonSession);
815 mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
816 }
817 pushAddressedPlayerChangedLocked();
818 }
819 }
820
821 // Remember media button receiver and keep it in the persistent storage.
822 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900823 PendingIntent receiver = record.getMediaButtonReceiver();
Jaewan Kima7dce192017-02-16 17:10:54 +0900824 mLastMediaButtonReceiver = receiver;
Jaewan Kim92dea332017-02-02 11:52:08 +0900825 mRestoredMediaButtonReceiver = null;
826 String componentName = "";
827 if (receiver != null) {
828 ComponentName component = receiver.getIntent().getComponent();
829 if (component != null
830 && record.getPackageName().equals(component.getPackageName())) {
831 componentName = component.flattenToString();
832 }
RoboErik4646d282014-05-13 10:13:04 -0700833 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900834 Settings.Secure.putStringForUser(mContentResolver,
835 Settings.System.MEDIA_BUTTON_RECEIVER,
836 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
837 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700838 }
RoboErikc8f92d12015-01-05 16:48:07 -0800839
Jaewan Kima7dce192017-02-16 17:10:54 +0900840 private void pushAddressedPlayerChangedLocked() {
841 if (mCallback == null) {
842 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800843 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900844 try {
845 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
846 if (mediaButtonSession != null) {
847 mCallback.onAddressedPlayerChangedToMediaSession(
848 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
849 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
850 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
851 mCurrentFullUserRecord.mLastMediaButtonReceiver
852 .getIntent().getComponent());
853 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
854 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
855 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
856 }
857 } catch (RemoteException e) {
858 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
859 }
860 }
861
862 private MediaSessionRecord getMediaButtonSessionLocked() {
Jaewan Kim92dea332017-02-02 11:52:08 +0900863 return isGlobalPriorityActiveLocked()
864 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
RoboErikc8f92d12015-01-05 16:48:07 -0800865 }
RoboErik4646d282014-05-13 10:13:04 -0700866 }
867
RoboErik2e7a9162014-06-04 16:53:45 -0700868 final class SessionsListenerRecord implements IBinder.DeathRecipient {
869 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700870 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700871 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700872 private final int mPid;
873 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700874
RoboErik7aef77b2014-08-08 15:56:54 -0700875 public SessionsListenerRecord(IActiveSessionsListener listener,
876 ComponentName componentName,
877 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700878 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700879 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700880 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700881 mPid = pid;
882 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700883 }
884
885 @Override
886 public void binderDied() {
887 synchronized (mLock) {
888 mSessionsListeners.remove(this);
889 }
890 }
891 }
892
RoboErik7aef77b2014-08-08 15:56:54 -0700893 final class SettingsObserver extends ContentObserver {
894 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
895 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
896
897 private SettingsObserver() {
898 super(null);
899 }
900
901 private void observe() {
902 mContentResolver.registerContentObserver(mSecureSettingsUri,
903 false, this, UserHandle.USER_ALL);
904 }
905
906 @Override
907 public void onChange(boolean selfChange, Uri uri) {
908 updateActiveSessionListeners();
909 }
910 }
911
RoboErik07c70772014-03-20 13:33:52 -0700912 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700913 private static final String EXTRA_WAKELOCK_ACQUIRED =
914 "android.media.AudioService.WAKELOCK_ACQUIRED";
915 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
916
RoboErik9a9d0b52014-05-20 14:53:39 -0700917 private boolean mVoiceButtonDown = false;
918 private boolean mVoiceButtonHandled = false;
919
RoboErik07c70772014-03-20 13:33:52 -0700920 @Override
RoboErika5b02322014-05-07 17:05:49 -0700921 public ISession createSession(String packageName, ISessionCallback cb, String tag,
922 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800923 final int pid = Binder.getCallingPid();
924 final int uid = Binder.getCallingUid();
925 final long token = Binder.clearCallingIdentity();
926 try {
927 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700928 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
929 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800930 if (cb == null) {
931 throw new IllegalArgumentException("Controller callback cannot be null");
932 }
RoboErika5b02322014-05-07 17:05:49 -0700933 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
934 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700935 } finally {
936 Binder.restoreCallingIdentity(token);
937 }
938 }
939
940 @Override
RoboErika5b02322014-05-07 17:05:49 -0700941 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700942 final int pid = Binder.getCallingPid();
943 final int uid = Binder.getCallingUid();
944 final long token = Binder.clearCallingIdentity();
945
946 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700947 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700948 ArrayList<IBinder> binders = new ArrayList<IBinder>();
949 synchronized (mLock) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900950 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
951 for (MediaSessionRecord record : records) {
952 binders.add(record.getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700953 }
954 }
955 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800956 } finally {
957 Binder.restoreCallingIdentity(token);
958 }
959 }
RoboErika278ea72014-04-24 14:49:01 -0700960
RoboErik2e7a9162014-06-04 16:53:45 -0700961 @Override
962 public void addSessionsListener(IActiveSessionsListener listener,
963 ComponentName componentName, int userId) throws RemoteException {
964 final int pid = Binder.getCallingPid();
965 final int uid = Binder.getCallingUid();
966 final long token = Binder.clearCallingIdentity();
967
968 try {
969 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
970 synchronized (mLock) {
971 int index = findIndexOfSessionsListenerLocked(listener);
972 if (index != -1) {
973 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
974 return;
975 }
976 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700977 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700978 try {
979 listener.asBinder().linkToDeath(record, 0);
980 } catch (RemoteException e) {
981 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
982 return;
983 }
984 mSessionsListeners.add(record);
985 }
986 } finally {
987 Binder.restoreCallingIdentity(token);
988 }
989 }
990
991 @Override
992 public void removeSessionsListener(IActiveSessionsListener listener)
993 throws RemoteException {
994 synchronized (mLock) {
995 int index = findIndexOfSessionsListenerLocked(listener);
996 if (index != -1) {
997 SessionsListenerRecord record = mSessionsListeners.remove(index);
998 try {
999 record.mListener.asBinder().unlinkToDeath(record, 0);
1000 } catch (Exception e) {
1001 // ignore exceptions, the record is being removed
1002 }
1003 }
1004 }
1005 }
1006
RoboErik8a2cfc32014-05-16 11:19:38 -07001007 /**
1008 * Handles the dispatching of the media button events to one of the
1009 * registered listeners, or if there was none, broadcast an
1010 * ACTION_MEDIA_BUTTON intent to the rest of the system.
1011 *
1012 * @param keyEvent a non-null KeyEvent whose key code is one of the
1013 * supported media buttons
1014 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
1015 * while this key event is dispatched.
1016 */
1017 @Override
1018 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
1019 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
1020 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
1021 return;
1022 }
Jeff Brown38d3feb2015-03-19 18:26:30 -07001023
RoboErik8a2cfc32014-05-16 11:19:38 -07001024 final int pid = Binder.getCallingPid();
1025 final int uid = Binder.getCallingUid();
1026 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -07001027 try {
Jeff Brown221a8272015-03-23 13:53:09 -07001028 if (DEBUG) {
1029 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1030 + keyEvent);
1031 }
Jeff Brown38d3feb2015-03-19 18:26:30 -07001032 if (!isUserSetupComplete()) {
1033 // Global media key handling can have the side-effect of starting new
1034 // activities which is undesirable while setup is in progress.
1035 Slog.i(TAG, "Not dispatching media key event because user "
1036 + "setup is in progress.");
1037 return;
1038 }
1039
RoboErik8a2cfc32014-05-16 11:19:38 -07001040 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001041 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001042 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
1043 // Prevent dispatching key event through reflection while the global
1044 // priority session is active.
1045 Slog.i(TAG, "Only the system can dispatch media key event "
1046 + "to the global priority session.");
1047 return;
1048 }
Jaewan Kim98003d32017-02-24 18:33:04 +09001049 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001050 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +09001051 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001052 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +09001053 }
1054 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001055 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim98003d32017-02-24 18:33:04 +09001056 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
1057 return;
1058 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001059 Log.w(TAG, "Failed to send " + keyEvent
1060 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +09001061 }
1062 }
1063 }
Jaewan Kim51255012017-02-24 16:19:14 +09001064 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001065 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -07001066 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +09001067 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -07001068 }
1069 }
1070 } finally {
1071 Binder.restoreCallingIdentity(token);
1072 }
1073 }
1074
RoboErika278ea72014-04-24 14:49:01 -07001075 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +09001076 public void setCallback(ICallback callback) {
1077 final int pid = Binder.getCallingPid();
1078 final int uid = Binder.getCallingUid();
1079 final long token = Binder.clearCallingIdentity();
1080 try {
Amith Yamasanie259ad22017-04-24 11:30:19 -07001081 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001082 throw new SecurityException("Only Bluetooth service processes can set"
1083 + " Callback");
1084 }
1085 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001086 int userId = UserHandle.getUserId(uid);
1087 FullUserRecord user = getFullUserRecordLocked(userId);
1088 if (user == null || user.mFullUserId != userId) {
1089 Log.w(TAG, "Only the full user can set the callback"
1090 + ", userId=" + userId);
1091 return;
1092 }
1093 user.mCallback = callback;
1094 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +09001095 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +09001096 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001097 return;
1098 }
1099 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001100 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +09001101 new IBinder.DeathRecipient() {
1102 @Override
1103 public void binderDied() {
1104 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001105 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001106 }
1107 }
1108 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +09001109 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +09001110 } catch (RemoteException e) {
1111 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +09001112 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001113 }
1114 }
1115 } finally {
1116 Binder.restoreCallingIdentity(token);
1117 }
1118 }
1119
1120 @Override
Jaewan Kim50269362016-12-23 11:22:02 +09001121 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
1122 final int pid = Binder.getCallingPid();
1123 final int uid = Binder.getCallingUid();
1124 final long token = Binder.clearCallingIdentity();
1125 try {
1126 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
1127 if (getContext().checkPermission(
1128 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
1129 != PackageManager.PERMISSION_GRANTED) {
1130 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
1131 " permission.");
1132 }
1133
1134 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001135 int userId = UserHandle.getUserId(uid);
1136 FullUserRecord user = getFullUserRecordLocked(userId);
1137 if (user == null || user.mFullUserId != userId) {
1138 Log.w(TAG, "Only the full user can set the volume key long-press listener"
1139 + ", userId=" + userId);
1140 return;
1141 }
Jaewan Kim50269362016-12-23 11:22:02 +09001142 if (user.mOnVolumeKeyLongPressListener != null &&
1143 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001144 Log.w(TAG, "The volume key long-press listener cannot be reset"
1145 + " by another app , mOnVolumeKeyLongPressListener="
1146 + user.mOnVolumeKeyLongPressListenerUid
1147 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +09001148 return;
1149 }
1150
1151 user.mOnVolumeKeyLongPressListener = listener;
1152 user.mOnVolumeKeyLongPressListenerUid = uid;
1153
Jaewan Kima7dce192017-02-16 17:10:54 +09001154 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001155 + listener + " is set by " + getCallingPackageName(uid));
1156
1157 if (user.mOnVolumeKeyLongPressListener != null) {
1158 try {
1159 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1160 new IBinder.DeathRecipient() {
1161 @Override
1162 public void binderDied() {
1163 synchronized (mLock) {
1164 user.mOnVolumeKeyLongPressListener = null;
1165 }
1166 }
1167 }, 0);
1168 } catch (RemoteException e) {
1169 Log.w(TAG, "Failed to set death recipient "
1170 + user.mOnVolumeKeyLongPressListener);
1171 user.mOnVolumeKeyLongPressListener = null;
1172 }
1173 }
1174 }
1175 } finally {
1176 Binder.restoreCallingIdentity(token);
1177 }
1178 }
1179
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001180 @Override
1181 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1182 final int pid = Binder.getCallingPid();
1183 final int uid = Binder.getCallingUid();
1184 final long token = Binder.clearCallingIdentity();
1185 try {
1186 // Enforce SET_MEDIA_KEY_LISTENER permission.
1187 if (getContext().checkPermission(
1188 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1189 != PackageManager.PERMISSION_GRANTED) {
1190 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1191 " permission.");
1192 }
1193
1194 synchronized (mLock) {
1195 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001196 FullUserRecord user = getFullUserRecordLocked(userId);
1197 if (user == null || user.mFullUserId != userId) {
1198 Log.w(TAG, "Only the full user can set the media key listener"
1199 + ", userId=" + userId);
1200 return;
1201 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001202 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001203 Log.w(TAG, "The media key listener cannot be reset by another app. "
1204 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1205 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001206 return;
1207 }
1208
1209 user.mOnMediaKeyListener = listener;
1210 user.mOnMediaKeyListenerUid = uid;
1211
Jaewan Kima7dce192017-02-16 17:10:54 +09001212 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001213 + " is set by " + getCallingPackageName(uid));
1214
1215 if (user.mOnMediaKeyListener != null) {
1216 try {
1217 user.mOnMediaKeyListener.asBinder().linkToDeath(
1218 new IBinder.DeathRecipient() {
1219 @Override
1220 public void binderDied() {
1221 synchronized (mLock) {
1222 user.mOnMediaKeyListener = null;
1223 }
1224 }
1225 }, 0);
1226 } catch (RemoteException e) {
1227 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1228 user.mOnMediaKeyListener = null;
1229 }
1230 }
1231 }
1232 } finally {
1233 Binder.restoreCallingIdentity(token);
1234 }
1235 }
1236
Jaewan Kim50269362016-12-23 11:22:02 +09001237 /**
1238 * Handles the dispatching of the volume button events to one of the
1239 * registered listeners. If there's a volume key long-press listener and
1240 * there's no active global priority session, long-pressess will be sent to the
1241 * long-press listener instead of adjusting volume.
1242 *
1243 * @param keyEvent a non-null KeyEvent whose key code is one of the
1244 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1245 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1246 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1247 * @param stream stream type to adjust volume.
1248 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1249 */
1250 @Override
1251 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1252 if (keyEvent == null ||
1253 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1254 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1255 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1256 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1257 return;
1258 }
1259
1260 final int pid = Binder.getCallingPid();
1261 final int uid = Binder.getCallingUid();
1262 final long token = Binder.clearCallingIdentity();
1263
Jaewan Kimb2781e72017-03-02 09:57:09 +09001264 if (DEBUG_KEY_EVENT) {
Jaewan Kim50269362016-12-23 11:22:02 +09001265 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1266 + keyEvent);
1267 }
1268
1269 try {
1270 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001271 if (isGlobalPriorityActiveLocked()
1272 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001273 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1274 } else {
1275 // TODO: Consider the case when both volume up and down keys are pressed
1276 // at the same time.
1277 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1278 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001279 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001280 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1281 KeyEvent.obtain(keyEvent);
1282 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1283 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001284 mHandler.sendMessageDelayed(
1285 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001286 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1287 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001288 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001289 }
1290 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001291 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001292 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001293 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001294 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001295 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001296 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001297 }
1298 dispatchVolumeKeyLongPressLocked(keyEvent);
1299 }
1300 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001301 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001302 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
1303 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1304 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001305 // Short-press. Should change volume.
1306 dispatchVolumeKeyEventLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001307 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1308 mCurrentFullUserRecord.mInitialDownVolumeStream,
1309 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001310 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1311 } else {
1312 dispatchVolumeKeyLongPressLocked(keyEvent);
1313 }
1314 }
1315 }
1316 }
1317 } finally {
1318 Binder.restoreCallingIdentity(token);
1319 }
1320 }
1321
Jaewan Kim50269362016-12-23 11:22:02 +09001322 private void dispatchVolumeKeyEventLocked(
1323 KeyEvent keyEvent, int stream, boolean musicOnly) {
1324 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1325 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1326 int direction = 0;
1327 boolean isMute = false;
1328 switch (keyEvent.getKeyCode()) {
1329 case KeyEvent.KEYCODE_VOLUME_UP:
1330 direction = AudioManager.ADJUST_RAISE;
1331 break;
1332 case KeyEvent.KEYCODE_VOLUME_DOWN:
1333 direction = AudioManager.ADJUST_LOWER;
1334 break;
1335 case KeyEvent.KEYCODE_VOLUME_MUTE:
1336 isMute = true;
1337 break;
1338 }
1339 if (down || up) {
1340 int flags = AudioManager.FLAG_FROM_KEY;
1341 if (musicOnly) {
1342 // This flag is used when the screen is off to only affect active media.
1343 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1344 } else {
1345 // These flags are consistent with the home screen
1346 if (up) {
1347 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1348 } else {
1349 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1350 }
1351 }
1352 if (direction != 0) {
1353 // If this is action up we want to send a beep for non-music events
1354 if (up) {
1355 direction = 0;
1356 }
1357 dispatchAdjustVolumeLocked(stream, direction, flags);
1358 } else if (isMute) {
1359 if (down && keyEvent.getRepeatCount() == 0) {
1360 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1361 }
1362 }
1363 }
1364 }
1365
1366 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001367 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001368 final long token = Binder.clearCallingIdentity();
1369 try {
1370 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001371 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001372 }
1373 } finally {
1374 Binder.restoreCallingIdentity(token);
1375 }
1376 }
1377
1378 @Override
RoboErik19c95182014-06-23 15:38:48 -07001379 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1380 final int pid = Binder.getCallingPid();
1381 final int uid = Binder.getCallingUid();
1382 final long token = Binder.clearCallingIdentity();
1383 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001384 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001385 mRvc = rvc;
1386 } finally {
1387 Binder.restoreCallingIdentity(token);
1388 }
1389 }
1390
1391 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001392 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001393 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001394 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001395 }
RoboErikde9ba392014-09-26 12:51:01 -07001396 }
1397
1398 @Override
RoboErika278ea72014-04-24 14:49:01 -07001399 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001400 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
RoboErika278ea72014-04-24 14:49:01 -07001401
1402 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1403 pw.println();
1404
1405 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001406 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001407 pw.println("Global priority session is " + mGlobalPrioritySession);
Jaewan Kim101b4d52017-05-18 13:23:11 +09001408 if (mGlobalPrioritySession != null) {
1409 mGlobalPrioritySession.dump(pw, " ");
1410 }
RoboErik4646d282014-05-13 10:13:04 -07001411 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001412 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001413 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001414 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001415 }
Sungsoo Lim875e6972017-11-03 02:22:35 +00001416 mAudioPlayerStateMonitor.dump(getContext(), pw, "");
Jaewan Kima70b3e52018-01-29 19:36:30 +09001417
1418 // TODO(jaewan): Remove this debug command before ship.
1419 if (args != null && args.length > 0 && "--purge".equals(args[0])) {
1420 mSessions.clear();
1421 }
1422 pw.println();
1423 pw.println("Session2: size=" + mSessions.size());
1424 for (int i = 0; i < mSessions.size(); i++) {
1425 pw.println(" " + mSessions.get(i));
1426 }
RoboErika278ea72014-04-24 14:49:01 -07001427 }
1428 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001429
Jaewan Kim7e9e4d92018-02-13 22:20:41 +09001430 /**
1431 * Called when a {@link android.media.MediaSession2} instance is created.
1432 * <p>
1433 * This does two things.
1434 * 1. Keep the newly created session in the service
1435 * 2. Do sanity check to ensure unique id per package, and return result
1436 *
1437 * @param sessionToken SessionToken2 object in bundled form
1438 * @return {@code true} if the session's id isn't used by the package now. {@code false}
1439 * otherwise.
1440 */
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001441 @Override
Jaewan Kimf7a77062018-01-27 01:34:24 +09001442 public boolean onSessionCreated(Bundle sessionToken) {
1443 final int uid = Binder.getCallingUid();
1444 final int pid = Binder.getCallingPid();
1445 final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken);
1446 if (token == null || token.getUid() != uid) {
1447 Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid()
1448 + " but from uid=" + uid);
1449 }
1450 if (DEBUG) {
1451 Log.d(TAG, "onSessionCreated " + token);
1452 }
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001453 synchronized (mLock) {
Jaewan Kimf7a77062018-01-27 01:34:24 +09001454 MediaSession2Record record = getSessionRecordLocked(
1455 uid, token.getPackageName(), token.getId());
1456 if (record != null) {
1457 return record.onSessionCreated(token);
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001458 } else {
Jaewan Kimf7a77062018-01-27 01:34:24 +09001459 record = new MediaSession2Record(
1460 getContext(), token, mSessionDestroyedListener);
1461 mSessions.add(record);
1462 return record.onSessionCreated(token);
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001463 }
1464 }
Jaewan Kimf7a77062018-01-27 01:34:24 +09001465 }
1466
Jaewan Kim7e9e4d92018-02-13 22:20:41 +09001467 /**
1468 * Called when a {@link android.media.MediaSession2} instance is closed. (i.e. destroyed)
1469 * <p>
1470 * Ideally service should know that a session is destroyed through the
1471 * {@link android.media.MediaController2.ControllerCallback#onDisconnected()}, which is
1472 * asynchronous call. However, we also need synchronous way together to address timing
1473 * issue. If the package recreates the session almost immediately, which happens commonly
1474 * for tests, service will reject the creation through {@link #onSessionCreated(Bundle)}
1475 * if the service hasn't notified previous destroy yet. This synchronous API will address
1476 * the issue.
1477 *
1478 * @param sessionToken SessionToken2 object in bundled form
1479 */
Jaewan Kimf7a77062018-01-27 01:34:24 +09001480 @Override
1481 public void onSessionDestroyed(Bundle sessionToken) {
1482 final int uid = Binder.getCallingUid();
1483 final int pid = Binder.getCallingPid();
1484 final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken);
1485 if (token == null || token.getUid() != uid) {
1486 Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid()
1487 + " but from uid=" + uid);
1488 }
1489 if (DEBUG) {
1490 Log.d(TAG, "onSessionDestroyed " + token);
1491 }
1492 synchronized (mLock) {
1493 MediaSession2Record record = getSessionRecordLocked(
1494 uid, token.getPackageName(), token.getId());
1495 if (record != null) {
1496 record.onSessionDestroyed();
1497 } else {
1498 if (DEBUG) {
1499 Log.d(TAG, "Cannot find a session record to destroy. uid=" + uid
1500 + ", pkg=" + token.getPackageName() + ", id=" + token.getId());
1501 }
1502 }
1503 }
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001504 }
1505
1506 // TODO(jaewan): Protect this API with permission
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001507 @Override
1508 public List<Bundle> getSessionTokens(boolean activeSessionOnly,
1509 boolean sessionServiceOnly) throws RemoteException {
1510 List<Bundle> tokens = new ArrayList<>();
1511 synchronized (mLock) {
1512 for (int i = 0; i < mSessions.size(); i++) {
1513 MediaSession2Record record = mSessions.get(i);
Jaewan Kimf7a77062018-01-27 01:34:24 +09001514 boolean isSessionService = (record.getToken().getType() != TYPE_SESSION);
1515 boolean isActive = record.getController() != null;
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001516 if ((!activeSessionOnly && isSessionService)
1517 || (!sessionServiceOnly && isActive)) {
Jaewan Kim04de5de2018-01-25 02:24:03 +09001518 SessionToken2 token = record.getToken();
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001519 if (token != null) {
1520 tokens.add(token.toBundle());
1521 } else {
1522 Log.wtf(TAG, "Null token for record=" + record);
1523 }
1524 }
1525 }
1526 }
1527 return tokens;
1528 }
1529
Jaewan Kim379e30d2018-01-29 11:57:04 +09001530 @Override
1531 public void addSessionTokensListener(ISessionTokensListener listener, int userId,
1532 String packageName) {
1533 // TODO(jaewan): Implement.
1534 }
1535
1536 @Override
1537 public void removeSessionTokensListener(ISessionTokensListener listener) {
1538 // TODO(jaewan): Implement
1539 }
1540
RoboErik2e7a9162014-06-04 16:53:45 -07001541 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1542 final int uid) {
1543 String packageName = null;
1544 if (componentName != null) {
1545 // If they gave us a component name verify they own the
1546 // package
1547 packageName = componentName.getPackageName();
1548 enforcePackageName(packageName, uid);
1549 }
1550 // Check that they can make calls on behalf of the user and
1551 // get the final user id
1552 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1553 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1554 // Check if they have the permissions or their component is
1555 // enabled for the user they're calling from.
1556 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1557 return resolvedUserId;
1558 }
1559
Jaewan Kim50269362016-12-23 11:22:02 +09001560 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001561 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1562 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001563
RoboErik9c785402014-11-11 16:52:26 -08001564 boolean preferSuggestedStream = false;
1565 if (isValidLocalStreamType(suggestedStream)
1566 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1567 preferSuggestedStream = true;
1568 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001569 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001570 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1571 + flags + ", suggestedStream=" + suggestedStream
1572 + ", preferSuggestedStream=" + preferSuggestedStream);
1573 }
RoboErik9c785402014-11-11 16:52:26 -08001574 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001575 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1576 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001577 if (DEBUG) {
1578 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001579 }
RoboErikb7c014c2014-07-22 15:58:22 -07001580 return;
RoboErik3c45c292014-07-08 16:47:31 -07001581 }
Shibin George19e84042016-06-14 20:42:13 +05301582
1583 // Execute mAudioService.adjustSuggestedStreamVolume() on
1584 // handler thread of MediaSessionService.
1585 // This will release the MediaSessionService.mLock sooner and avoid
1586 // a potential deadlock between MediaSessionService.mLock and
1587 // ActivityManagerService lock.
1588 mHandler.post(new Runnable() {
1589 @Override
1590 public void run() {
1591 try {
1592 String packageName = getContext().getOpPackageName();
1593 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1594 flags, packageName, TAG);
1595 } catch (RemoteException e) {
1596 Log.e(TAG, "Error adjusting default volume.", e);
Hyundo Moon739d6c22017-09-18 17:01:48 +09001597 } catch (IllegalArgumentException e) {
1598 Log.e(TAG, "Cannot adjust volume: direction=" + direction
1599 + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
1600 e);
Shibin George19e84042016-06-14 20:42:13 +05301601 }
1602 }
1603 });
RoboErikb69ffd42014-05-30 14:57:59 -07001604 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001605 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001606 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001607 }
1608 }
1609
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001610 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001611 int action = keyEvent.getAction();
1612 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1613 if (action == KeyEvent.ACTION_DOWN) {
1614 if (keyEvent.getRepeatCount() == 0) {
1615 mVoiceButtonDown = true;
1616 mVoiceButtonHandled = false;
1617 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1618 mVoiceButtonHandled = true;
1619 startVoiceInput(needWakeLock);
1620 }
1621 } else if (action == KeyEvent.ACTION_UP) {
1622 if (mVoiceButtonDown) {
1623 mVoiceButtonDown = false;
1624 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1625 // Resend the down then send this event through
1626 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001627 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1628 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001629 }
1630 }
1631 }
1632 }
1633
Jaewan Kim98003d32017-02-24 18:33:04 +09001634 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001635 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001636 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001637 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001638 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001639 }
1640 if (needWakeLock) {
1641 mKeyEventReceiver.aquireWakeLockLocked();
1642 }
Jaewan Kim50269362016-12-23 11:22:02 +09001643 // If we don't need a wakelock use -1 as the id so we won't release it later.
RoboErik9a9d0b52014-05-20 14:53:39 -07001644 session.sendMediaButton(keyEvent,
1645 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001646 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001647 getContext().getPackageName());
Jaewan Kima7dce192017-02-16 17:10:54 +09001648 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001649 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001650 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
1651 keyEvent,
Jaewan Kimbd16f452017-02-03 16:21:38 +09001652 new MediaSession.Token(session.getControllerBinder()));
1653 } catch (RemoteException e) {
1654 Log.w(TAG, "Failed to send callback", e);
1655 }
1656 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001657 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1658 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1659 if (needWakeLock) {
1660 mKeyEventReceiver.aquireWakeLockLocked();
1661 }
1662 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1663 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1664 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1665 try {
1666 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1667 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1668 if (DEBUG_KEY_EVENT) {
1669 Log.d(TAG, "Sending " + keyEvent
Jaewan Kim92dea332017-02-02 11:52:08 +09001670 + " to the last known PendingIntent " + receiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001671 }
1672 receiver.send(getContext(),
1673 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1674 mediaButtonIntent, mKeyEventReceiver, mHandler);
1675 if (mCurrentFullUserRecord.mCallback != null) {
1676 ComponentName componentName = mCurrentFullUserRecord
1677 .mLastMediaButtonReceiver.getIntent().getComponent();
1678 if (componentName != null) {
1679 mCurrentFullUserRecord.mCallback
1680 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1681 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001682 }
RoboErikc8f92d12015-01-05 16:48:07 -08001683 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001684 } else {
1685 ComponentName receiver =
1686 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1687 if (DEBUG_KEY_EVENT) {
1688 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1689 + receiver);
1690 }
1691 mediaButtonIntent.setComponent(receiver);
1692 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim92dea332017-02-02 11:52:08 +09001693 UserHandle.of(mCurrentFullUserRecord
1694 .mRestoredMediaButtonReceiverUserId));
Jaewan Kima7dce192017-02-16 17:10:54 +09001695 if (mCurrentFullUserRecord.mCallback != null) {
1696 mCurrentFullUserRecord.mCallback
1697 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1698 keyEvent, receiver);
1699 }
RoboErikb214efb2014-07-24 13:20:30 -07001700 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001701 } catch (CanceledException e) {
1702 Log.i(TAG, "Error sending key event to media button receiver "
1703 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1704 } catch (RemoteException e) {
1705 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001706 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001707 }
1708 }
1709
1710 private void startVoiceInput(boolean needWakeLock) {
1711 Intent voiceIntent = null;
1712 // select which type of search to launch:
1713 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1714 // - device locked or screen off: action is
1715 // ACTION_VOICE_SEARCH_HANDS_FREE
1716 // with EXTRA_SECURE set to true if the device is securely locked
1717 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1718 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1719 if (!isLocked && pm.isScreenOn()) {
1720 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1721 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1722 } else {
1723 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1724 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1725 isLocked && mKeyguardManager.isKeyguardSecure());
1726 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1727 }
1728 // start the search activity
1729 if (needWakeLock) {
1730 mMediaEventWakeLock.acquire();
1731 }
1732 try {
1733 if (voiceIntent != null) {
1734 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1735 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001736 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001737 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1738 }
1739 } catch (ActivityNotFoundException e) {
1740 Log.w(TAG, "No activity for search: " + e);
1741 } finally {
1742 if (needWakeLock) {
1743 mMediaEventWakeLock.release();
1744 }
1745 }
1746 }
1747
1748 private boolean isVoiceKey(int keyCode) {
Jaewan Kimba18d8e2017-05-12 17:37:57 +09001749 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
Jaewan Kimfdb612e2017-07-01 09:23:41 +09001750 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
RoboErik9a9d0b52014-05-20 14:53:39 -07001751 }
1752
Jeff Brown38d3feb2015-03-19 18:26:30 -07001753 private boolean isUserSetupComplete() {
1754 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1755 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1756 }
1757
RoboErik9c785402014-11-11 16:52:26 -08001758 // we only handle public stream types, which are 0-5
1759 private boolean isValidLocalStreamType(int streamType) {
1760 return streamType >= AudioManager.STREAM_VOICE_CALL
1761 && streamType <= AudioManager.STREAM_NOTIFICATION;
1762 }
1763
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001764 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1765 private KeyEvent mKeyEvent;
1766 private boolean mNeedWakeLock;
1767 private boolean mHandled;
1768
1769 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1770 super(mHandler);
1771 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1772 mKeyEvent = keyEvent;
1773 mNeedWakeLock = needWakeLock;
1774 }
1775
1776 @Override
1777 public void run() {
1778 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1779 dispatchMediaKeyEvent();
1780 }
1781
1782 @Override
1783 protected void onReceiveResult(int resultCode, Bundle resultData) {
1784 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1785 mHandled = true;
1786 mHandler.removeCallbacks(this);
1787 return;
1788 }
1789 dispatchMediaKeyEvent();
1790 }
1791
1792 private void dispatchMediaKeyEvent() {
1793 if (mHandled) {
1794 return;
1795 }
1796 mHandled = true;
1797 mHandler.removeCallbacks(this);
1798 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001799 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001800 && isVoiceKey(mKeyEvent.getKeyCode())) {
1801 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1802 } else {
1803 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1804 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001805 }
1806 }
1807 }
1808
RoboErik418c10c2014-05-19 09:25:25 -07001809 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1810
RoboErikb214efb2014-07-24 13:20:30 -07001811 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1812 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001813 private final Handler mHandler;
1814 private int mRefCount = 0;
1815 private int mLastTimeoutId = 0;
1816
1817 public KeyEventWakeLockReceiver(Handler handler) {
1818 super(handler);
1819 mHandler = handler;
1820 }
1821
1822 public void onTimeout() {
1823 synchronized (mLock) {
1824 if (mRefCount == 0) {
1825 // We've already released it, so just return
1826 return;
1827 }
1828 mLastTimeoutId++;
1829 mRefCount = 0;
1830 releaseWakeLockLocked();
1831 }
1832 }
1833
1834 public void aquireWakeLockLocked() {
1835 if (mRefCount == 0) {
1836 mMediaEventWakeLock.acquire();
1837 }
1838 mRefCount++;
1839 mHandler.removeCallbacks(this);
1840 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1841
1842 }
1843
1844 @Override
1845 public void run() {
1846 onTimeout();
1847 }
1848
RoboErik8a2cfc32014-05-16 11:19:38 -07001849 @Override
1850 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001851 if (resultCode < mLastTimeoutId) {
1852 // Ignore results from calls that were before the last
1853 // timeout, just in case.
1854 return;
1855 } else {
1856 synchronized (mLock) {
1857 if (mRefCount > 0) {
1858 mRefCount--;
1859 if (mRefCount == 0) {
1860 releaseWakeLockLocked();
1861 }
1862 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001863 }
1864 }
1865 }
RoboErik418c10c2014-05-19 09:25:25 -07001866
1867 private void releaseWakeLockLocked() {
1868 mMediaEventWakeLock.release();
1869 mHandler.removeCallbacks(this);
1870 }
RoboErikb214efb2014-07-24 13:20:30 -07001871
1872 @Override
1873 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1874 String resultData, Bundle resultExtras) {
1875 onReceiveResult(resultCode, null);
1876 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001877 };
1878
1879 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1880 @Override
1881 public void onReceive(Context context, Intent intent) {
1882 if (intent == null) {
1883 return;
1884 }
1885 Bundle extras = intent.getExtras();
1886 if (extras == null) {
1887 return;
1888 }
1889 synchronized (mLock) {
1890 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1891 && mMediaEventWakeLock.isHeld()) {
1892 mMediaEventWakeLock.release();
1893 }
1894 }
1895 }
1896 };
RoboErik01fe6612014-02-13 14:19:04 -08001897 }
1898
RoboErik2e7a9162014-06-04 16:53:45 -07001899 final class MessageHandler extends Handler {
1900 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001901 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
Jaewan Kim92dea332017-02-02 11:52:08 +09001902 private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
RoboErik2e7a9162014-06-04 16:53:45 -07001903
1904 @Override
1905 public void handleMessage(Message msg) {
1906 switch (msg.what) {
1907 case MSG_SESSIONS_CHANGED:
Jaewan Kim92dea332017-02-02 11:52:08 +09001908 pushSessionsChanged((int) msg.obj);
RoboErik2e7a9162014-06-04 16:53:45 -07001909 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001910 case MSG_VOLUME_INITIAL_DOWN:
1911 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001912 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001913 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1914 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1915 // Mark that the key is already handled.
1916 user.mInitialDownVolumeKeyEvent = null;
1917 }
1918 }
1919 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001920 }
1921 }
1922
Jaewan Kim92dea332017-02-02 11:52:08 +09001923 public void postSessionsChanged(int userId) {
1924 // Use object instead of the arguments when posting message to remove pending requests.
1925 Integer userIdInteger = mIntegerCache.get(userId);
1926 if (userIdInteger == null) {
1927 userIdInteger = Integer.valueOf(userId);
1928 mIntegerCache.put(userId, userIdInteger);
1929 }
1930 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
1931 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
RoboErik2e7a9162014-06-04 16:53:45 -07001932 }
1933 }
RoboErik01fe6612014-02-13 14:19:04 -08001934}