blob: c9c7d04e6e1411bc40b06939b897fb8d86a16a5a [file] [log] [blame]
RoboErik01fe6612014-02-13 14:19:04 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.media;
18
RoboErike7880d82014-04-30 12:48:25 -070019import android.app.ActivityManager;
Julia Reynoldsb852e562017-06-06 16:14:18 -040020import android.app.INotificationManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070021import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070022import android.app.PendingIntent;
23import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070024import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070025import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070026import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070027import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080028import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070029import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070030import android.content.pm.PackageManager;
Jaewan Kimceb6b6e2018-01-21 20:56:10 +090031import android.content.pm.ResolveInfo;
32import android.content.pm.ServiceInfo;
Jaewan Kima7dce192017-02-16 17:10:54 +090033import android.content.pm.UserInfo;
RoboErik7aef77b2014-08-08 15:56:54 -070034import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070035import android.media.AudioManager;
Sungsoo Lim875e6972017-11-03 02:22:35 +000036import android.media.AudioPlaybackConfiguration;
RoboErik94c716e2014-09-14 13:54:31 -070037import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070038import android.media.IAudioService;
Jaewan Kimceb6b6e2018-01-21 20:56:10 +090039import android.media.IMediaSession2;
RoboErik19c95182014-06-23 15:38:48 -070040import android.media.IRemoteVolumeController;
Jaewan Kimbcecf312018-01-23 19:30:42 +090041import android.media.MediaLibraryService2;
Jaewan Kimceb6b6e2018-01-21 20:56:10 +090042import android.media.MediaSessionService2;
43import android.media.SessionToken;
RoboErik2e7a9162014-06-04 16:53:45 -070044import android.media.session.IActiveSessionsListener;
Jaewan Kimbd16f452017-02-03 16:21:38 +090045import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080046import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090047import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070048import android.media.session.ISession;
49import android.media.session.ISessionCallback;
50import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070051import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080052import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070053import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080054import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070055import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080056import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070057import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070058import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070059import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090060import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080061import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070062import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070063import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070064import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090065import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070066import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070067import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080068import android.text.TextUtils;
69import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070070import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070071import android.util.SparseArray;
Jaewan Kima7dce192017-02-16 17:10:54 +090072import android.util.SparseIntArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070073import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090074import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080075
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060076import com.android.internal.util.DumpUtils;
RoboErik01fe6612014-02-13 14:19:04 -080077import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070078import com.android.server.Watchdog;
79import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080080
RoboErika278ea72014-04-24 14:49:01 -070081import java.io.FileDescriptor;
82import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080083import java.util.ArrayList;
RoboErike7880d82014-04-30 12:48:25 -070084import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080085
86/**
87 * System implementation of MediaSessionManager
88 */
RoboErika278ea72014-04-24 14:49:01 -070089public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080090 private static final String TAG = "MediaSessionService";
Jaewan Kim92dea332017-02-02 11:52:08 +090091 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090092 // Leave log for key event always.
93 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080094
RoboErik418c10c2014-05-19 09:25:25 -070095 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080096 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070097
RoboErik01fe6612014-02-13 14:19:04 -080098 private final SessionManagerImpl mSessionManagerImpl;
99
Jaewan Kima7dce192017-02-16 17:10:54 +0900100 // Keeps the full user id for each user.
101 private final SparseIntArray mFullUserIds = new SparseIntArray();
102 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -0700103 private final ArrayList<SessionsListenerRecord> mSessionsListeners
104 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800105 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700106 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700107 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900108 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800109
RoboErik9a9d0b52014-05-20 14:53:39 -0700110 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700111 private IAudioService mAudioService;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700112 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700113 private SettingsObserver mSettingsObserver;
Julia Reynoldsb852e562017-06-06 16:14:18 -0400114 private INotificationManager mNotificationManager;
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900115 private boolean mHasFeatureLeanback;
RoboErik9a9d0b52014-05-20 14:53:39 -0700116
Jaewan Kima7dce192017-02-16 17:10:54 +0900117 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
118 // It's always not null after the MediaSessionService is started.
119 private FullUserRecord mCurrentFullUserRecord;
120 private MediaSessionRecord mGlobalPrioritySession;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000121 private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
RoboErike7880d82014-04-30 12:48:25 -0700122
RoboErik19c95182014-06-23 15:38:48 -0700123 // Used to notify system UI when remote volume was changed. TODO find a
124 // better way to handle this.
125 private IRemoteVolumeController mRvc;
126
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900127 // MediaSession2 support
128 // TODO(jaewan): Support multi-user and managed profile.
129 // TODO(jaewan): Make it priority list for handling volume/media key.
130 private final List<MediaSession2Record> mSessions = new ArrayList<>();
131
132 private final MediaSession2Record.SessionDestroyedListener mSessionDestroyedListener =
133 (MediaSession2Record record) -> {
134 synchronized (mLock) {
135 if (DEBUG) {
136 Log.d(TAG, record.toString() + " becomes inactive");
137 }
138 record.onSessionDestroyed();
139 if (!(record instanceof MediaSessionService2Record)) {
140 mSessions.remove(record);
141 }
142 }
143 };
144
RoboErik01fe6612014-02-13 14:19:04 -0800145 public MediaSessionService(Context context) {
146 super(context);
147 mSessionManagerImpl = new SessionManagerImpl();
RoboErik8a2cfc32014-05-16 11:19:38 -0700148 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
149 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900150 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
Julia Reynoldsb852e562017-06-06 16:14:18 -0400151 mNotificationManager = INotificationManager.Stub.asInterface(
152 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
RoboErik01fe6612014-02-13 14:19:04 -0800153 }
154
155 @Override
156 public void onStart() {
157 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700158 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700159 mKeyguardManager =
160 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700161 mAudioService = getAudioService();
Sungsoo Lim875e6972017-11-03 02:22:35 +0000162 mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
163 mAudioPlayerStateMonitor.registerListener(
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900164 (config, isRemoved) -> {
165 if (isRemoved || !config.isActive() || config.getPlayerType()
166 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
167 return;
Jaewan Kim92dea332017-02-02 11:52:08 +0900168 }
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900169 synchronized (mLock) {
170 FullUserRecord user = getFullUserRecordLocked(
171 UserHandle.getUserId(config.getClientUid()));
172 if (user != null) {
173 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
174 }
175 }
176 }, null /* handler */);
Sungsoo Lim875e6972017-11-03 02:22:35 +0000177 mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700178 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700179 mSettingsObserver = new SettingsObserver();
180 mSettingsObserver.observe();
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900181 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
182 PackageManager.FEATURE_LEANBACK);
RoboErikc8f92d12015-01-05 16:48:07 -0800183
184 updateUser();
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900185
186 // TODO(jaewan): Query per users
187 // TODO(jaewan): Add listener to know changes in list of services.
188 // Refer TvInputManagerService.registerBroadcastReceivers()
189 buildMediaSessionService2List();
RoboErikb69ffd42014-05-30 14:57:59 -0700190 }
191
192 private IAudioService getAudioService() {
193 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
194 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700195 }
196
Jaewan Kima7dce192017-02-16 17:10:54 +0900197 private boolean isGlobalPriorityActiveLocked() {
198 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
199 }
200
RoboErika8f95142014-05-05 14:23:49 -0700201 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700202 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900203 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
Jaewan Kim101b4d52017-05-18 13:23:11 +0900204 if (user == null) {
205 Log.w(TAG, "Unknown session updated. Ignoring.");
RoboErik4646d282014-05-13 10:13:04 -0700206 return;
207 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900208 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900209 if (DEBUG_KEY_EVENT) {
210 Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
211 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900212 user.pushAddressedPlayerChangedLocked();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900213 } else {
214 if (!user.mPriorityStack.contains(record)) {
215 Log.w(TAG, "Unknown session updated. Ignoring.");
216 return;
217 }
218 user.mPriorityStack.onSessionStateChange(record);
Jaewan Kima7dce192017-02-16 17:10:54 +0900219 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900220 mHandler.postSessionsChanged(record.getUserId());
RoboErike7880d82014-04-30 12:48:25 -0700221 }
222 }
223
Jaewan Kimfa85b602017-10-10 16:49:58 +0900224 public void setGlobalPrioritySession(MediaSessionRecord record) {
225 synchronized (mLock) {
226 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
227 if (mGlobalPrioritySession != record) {
228 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
229 + " to " + record);
230 mGlobalPrioritySession = record;
231 if (user != null && user.mPriorityStack.contains(record)) {
232 // Handle the global priority session separately.
233 // Otherwise, it can be the media button session regardless of the active state
234 // because it or other system components might have been the lastly played media
235 // app.
236 user.mPriorityStack.removeSession(record);
237 }
238 }
239 }
240 }
241
Jaewan Kim101b4d52017-05-18 13:23:11 +0900242 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
Jaewan Kimda74a152017-10-03 23:58:11 +0900243 List<MediaSessionRecord> records = new ArrayList<>();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900244 if (userId == UserHandle.USER_ALL) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900245 int size = mUserRecords.size();
246 for (int i = 0; i < size; i++) {
247 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
248 }
249 } else {
250 FullUserRecord user = getFullUserRecordLocked(userId);
251 if (user == null) {
252 Log.w(TAG, "getSessions failed. Unknown user " + userId);
Jaewan Kimda74a152017-10-03 23:58:11 +0900253 return records;
Jaewan Kim101b4d52017-05-18 13:23:11 +0900254 }
Jaewan Kimda74a152017-10-03 23:58:11 +0900255 records.addAll(user.mPriorityStack.getActiveSessions(userId));
Jaewan Kim101b4d52017-05-18 13:23:11 +0900256 }
257
258 // Return global priority session at the first whenever it's asked.
259 if (isGlobalPriorityActiveLocked()
260 && (userId == UserHandle.USER_ALL
261 || userId == mGlobalPrioritySession.getUserId())) {
262 records.add(0, mGlobalPrioritySession);
263 }
264 return records;
265 }
266
RoboErik9c5b7cb2015-01-15 15:09:09 -0800267 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900268 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800269 */
270 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900271 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800272 return;
273 }
274 try {
275 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
276 } catch (Exception e) {
277 Log.wtf(TAG, "Error sending volume change to system UI.", e);
278 }
279 }
280
Jaewan Kim92dea332017-02-02 11:52:08 +0900281 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
RoboErika8f95142014-05-05 14:23:49 -0700282 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900283 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
284 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700285 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
286 return;
287 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900288 user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
RoboErika8f95142014-05-05 14:23:49 -0700289 }
290 }
291
RoboErik19c95182014-06-23 15:38:48 -0700292 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
293 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900294 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
295 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700296 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
297 return;
298 }
299 pushRemoteVolumeUpdateLocked(record.getUserId());
300 }
301 }
302
RoboErika278ea72014-04-24 14:49:01 -0700303 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900304 public void onStartUser(int userId) {
305 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700306 updateUser();
307 }
308
309 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900310 public void onSwitchUser(int userId) {
311 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700312 updateUser();
313 }
314
315 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900316 public void onStopUser(int userId) {
317 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700318 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900319 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700320 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900321 if (user.mFullUserId == userId) {
322 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
323 mUserRecords.remove(userId);
324 } else {
325 user.destroySessionsForUserLocked(userId);
326 }
RoboErik4646d282014-05-13 10:13:04 -0700327 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900328 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700329 }
330 }
331
332 @Override
RoboErika278ea72014-04-24 14:49:01 -0700333 public void monitor() {
334 synchronized (mLock) {
335 // Check for deadlock
336 }
337 }
338
RoboErik4646d282014-05-13 10:13:04 -0700339 protected void enforcePhoneStatePermission(int pid, int uid) {
340 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
341 != PackageManager.PERMISSION_GRANTED) {
342 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
343 }
344 }
345
RoboErik01fe6612014-02-13 14:19:04 -0800346 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700347 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800348 destroySessionLocked(session);
349 }
350 }
351
352 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700353 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800354 destroySessionLocked(session);
355 }
356 }
357
RoboErik4646d282014-05-13 10:13:04 -0700358 private void updateUser() {
359 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900360 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900361 mFullUserIds.clear();
362 List<UserInfo> allUsers = manager.getUsers();
363 if (allUsers != null) {
364 for (UserInfo userInfo : allUsers) {
365 if (userInfo.isManagedProfile()) {
366 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
367 } else {
368 mFullUserIds.put(userInfo.id, userInfo.id);
369 if (mUserRecords.get(userInfo.id) == null) {
370 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
371 }
372 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900373 }
RoboErik4646d282014-05-13 10:13:04 -0700374 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900375 // Ensure that the current full user exists.
376 int currentFullUserId = ActivityManager.getCurrentUser();
377 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
378 if (mCurrentFullUserRecord == null) {
379 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
380 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
381 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
382 }
383 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700384 }
385 }
386
RoboErik7aef77b2014-08-08 15:56:54 -0700387 private void updateActiveSessionListeners() {
388 synchronized (mLock) {
389 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
390 SessionsListenerRecord listener = mSessionsListeners.get(i);
391 try {
392 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
393 listener.mUserId);
394 } catch (SecurityException e) {
395 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
396 + " is no longer authorized. Disconnecting.");
397 mSessionsListeners.remove(i);
398 try {
399 listener.mListener
400 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
401 } catch (Exception e1) {
402 // ignore
403 }
404 }
405 }
406 }
407 }
408
RoboErik4646d282014-05-13 10:13:04 -0700409 /*
410 * When a session is removed several things need to happen.
411 * 1. We need to remove it from the relevant user.
412 * 2. We need to remove it from the priority stack.
413 * 3. We need to remove it from all sessions.
414 * 4. If this is the system priority session we need to clear it.
415 * 5. We need to unlink to death from the cb binder
416 * 6. We need to tell the session to do any final cleanup (onDestroy)
417 */
RoboErik01fe6612014-02-13 14:19:04 -0800418 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900419 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900420 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900421 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900422 FullUserRecord user = getFullUserRecordLocked(session.getUserId());
Jaewan Kima7dce192017-02-16 17:10:54 +0900423 if (mGlobalPrioritySession == session) {
424 mGlobalPrioritySession = null;
Jaewan Kim92dea332017-02-02 11:52:08 +0900425 if (session.isActive() && user != null) {
426 user.pushAddressedPlayerChangedLocked();
427 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900428 } else {
429 if (user != null) {
430 user.mPriorityStack.removeSession(session);
431 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900432 }
RoboErik4646d282014-05-13 10:13:04 -0700433
434 try {
435 session.getCallback().asBinder().unlinkToDeath(session, 0);
436 } catch (Exception e) {
437 // ignore exceptions while destroying a session.
438 }
439 session.onDestroy();
Jaewan Kim92dea332017-02-02 11:52:08 +0900440 mHandler.postSessionsChanged(session.getUserId());
RoboErik01fe6612014-02-13 14:19:04 -0800441 }
442
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900443 private void buildMediaSessionService2List() {
444 if (DEBUG) {
445 Log.d(TAG, "buildMediaSessionService2List");
446 }
447
448 // TODO(jaewan): Query per users.
Jaewan Kimbcecf312018-01-23 19:30:42 +0900449 List<ResolveInfo> services = new ArrayList<>();
450 // If multiple actions are declared for a service, browser gets higher priority.
451 List<ResolveInfo> libraryServices = getContext().getPackageManager().queryIntentServices(
452 new Intent(MediaLibraryService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
453 if (libraryServices != null) {
454 services.addAll(libraryServices);
455 }
456 List<ResolveInfo> sessionServices = getContext().getPackageManager().queryIntentServices(
457 new Intent(MediaSessionService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
458 if (sessionServices != null) {
459 services.addAll(sessionServices);
460 }
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900461 synchronized (mLock) {
462 mSessions.clear();
463 if (services == null) {
464 return;
465 }
466 for (int i = 0; i < services.size(); i++) {
467 if (services.get(i) == null || services.get(i).serviceInfo == null) {
468 continue;
469 }
470 ServiceInfo serviceInfo = services.get(i).serviceInfo;
471 String id = (serviceInfo.metaData != null) ? serviceInfo.metaData.getString(
472 MediaSessionService2.SERVICE_META_DATA) : null;
473 // Do basic sanity check
474 // TODO(jaewan): also santity check if it's protected with the system|privileged
475 // permission
476 boolean conflict = (getSessionRecordLocked(serviceInfo.name, id) != null);
477 if (conflict) {
478 Log.w(TAG, serviceInfo.packageName + " contains multiple"
479 + " MediaSessionService2s declared in the manifest with"
480 + " the same ID=" + id + ". Ignoring "
481 + serviceInfo.packageName + "/" + serviceInfo.name);
482 } else {
Jaewan Kimbcecf312018-01-23 19:30:42 +0900483 int type = (libraryServices.contains(services.get(i)))
484 ? SessionToken.TYPE_LIBRARY_SERVICE : SessionToken.TYPE_SESSION_SERVICE;
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900485 MediaSessionService2Record record =
486 new MediaSessionService2Record(getContext(), mSessionDestroyedListener,
Jaewan Kimbcecf312018-01-23 19:30:42 +0900487 type, serviceInfo.packageName, serviceInfo.name, id);
Jaewan Kimceb6b6e2018-01-21 20:56:10 +0900488 mSessions.add(record);
489 }
490 }
491 }
492 if (DEBUG) {
493 Log.d(TAG, "Found " + mSessions.size() + " session services");
494 for (int i = 0; i < mSessions.size(); i++) {
495 Log.d(TAG, " " + mSessions.get(i).getToken());
496 }
497 }
498 }
499
500 MediaSession2Record getSessionRecordLocked(String packageName, String id) {
501 for (int i = 0; i < mSessions.size(); i++) {
502 MediaSession2Record record = mSessions.get(i);
503 if (record.getToken().getPackageName().equals(packageName)
504 && record.getToken().getId().equals(id)) {
505 return record;
506 }
507 }
508 return null;
509 }
510
RoboErik01fe6612014-02-13 14:19:04 -0800511 private void enforcePackageName(String packageName, int uid) {
512 if (TextUtils.isEmpty(packageName)) {
513 throw new IllegalArgumentException("packageName may not be empty");
514 }
515 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
516 final int packageCount = packages.length;
517 for (int i = 0; i < packageCount; i++) {
518 if (packageName.equals(packages[i])) {
519 return;
520 }
521 }
522 throw new IllegalArgumentException("packageName is not owned by the calling process");
523 }
524
RoboErike7880d82014-04-30 12:48:25 -0700525 /**
526 * Checks a caller's authorization to register an IRemoteControlDisplay.
527 * Authorization is granted if one of the following is true:
528 * <ul>
529 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
530 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700531 * <li>the caller's listener is one of the enabled notification listeners
532 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700533 * </ul>
534 */
RoboErika5b02322014-05-07 17:05:49 -0700535 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
536 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500537 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700538 if (getContext()
539 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
540 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700541 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
542 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700543 throw new SecurityException("Missing permission to control media.");
544 }
545 }
546
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500547 private boolean isCurrentVolumeController(int uid, int pid) {
548 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
549 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500550 }
551
552 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500553 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700554 throw new SecurityException("Only system ui may " + action);
555 }
556 }
557
RoboErika5b02322014-05-07 17:05:49 -0700558 /**
559 * This checks if the component is an enabled notification listener for the
560 * specified user. Enabled components may only operate on behalf of the user
561 * they're running as.
562 *
563 * @param compName The component that is enabled.
564 * @param userId The user id of the caller.
565 * @param forUserId The user id they're making the request on behalf of.
566 * @return True if the component is enabled, false otherwise
567 */
568 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
569 int forUserId) {
570 if (userId != forUserId) {
571 // You may not access another user's content as an enabled listener.
572 return false;
573 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700574 if (DEBUG) {
575 Log.d(TAG, "Checking if enabled notification listener " + compName);
576 }
RoboErike7880d82014-04-30 12:48:25 -0700577 if (compName != null) {
Julia Reynoldsb852e562017-06-06 16:14:18 -0400578 try {
579 return mNotificationManager.isNotificationListenerAccessGrantedForUser(
580 compName, userId);
581 } catch(RemoteException e) {
582 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
RoboErike7880d82014-04-30 12:48:25 -0700583 }
584 }
585 return false;
586 }
587
RoboErika5b02322014-05-07 17:05:49 -0700588 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700589 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800590 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700591 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800592 }
593 }
594
RoboErik4646d282014-05-13 10:13:04 -0700595 /*
596 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700597 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700598 * 2. It needs to be added to all sessions.
599 * 3. It needs to be added to the priority stack.
600 * 4. It needs to be added to the relevant user record.
601 */
RoboErika5b02322014-05-07 17:05:49 -0700602 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
603 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900604 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700605 if (user == null) {
606 Log.wtf(TAG, "Request from invalid user: " + userId);
607 throw new RuntimeException("Session request from invalid user.");
608 }
609
RoboErika5b02322014-05-07 17:05:49 -0700610 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
Jaewan Kim92dea332017-02-02 11:52:08 +0900611 callerPackageName, cb, tag, this, mHandler.getLooper());
RoboErik01fe6612014-02-13 14:19:04 -0800612 try {
613 cb.asBinder().linkToDeath(session, 0);
614 } catch (RemoteException e) {
615 throw new RuntimeException("Media Session owner died prematurely.", e);
616 }
RoboErik4646d282014-05-13 10:13:04 -0700617
Jaewan Kim101b4d52017-05-18 13:23:11 +0900618 user.mPriorityStack.addSession(session);
Jaewan Kim92dea332017-02-02 11:52:08 +0900619 mHandler.postSessionsChanged(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700620
RoboErik01fe6612014-02-13 14:19:04 -0800621 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900622 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800623 }
624 return session;
625 }
626
RoboErik2e7a9162014-06-04 16:53:45 -0700627 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
628 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800629 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700630 return i;
631 }
632 }
633 return -1;
634 }
635
RoboErik2e7a9162014-06-04 16:53:45 -0700636 private void pushSessionsChanged(int userId) {
637 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900638 FullUserRecord user = getFullUserRecordLocked(userId);
639 if (user == null) {
640 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
641 return;
642 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900643 List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700644 int size = records.size();
Jeff Browndba34ba2014-06-24 20:46:03 -0700645 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700646 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700647 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700648 }
RoboErik19c95182014-06-23 15:38:48 -0700649 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700650 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
651 SessionsListenerRecord record = mSessionsListeners.get(i);
652 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
653 try {
654 record.mListener.onActiveSessionsChanged(tokens);
655 } catch (RemoteException e) {
656 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
657 e);
658 mSessionsListeners.remove(i);
659 }
660 }
661 }
662 }
663 }
664
RoboErik19c95182014-06-23 15:38:48 -0700665 private void pushRemoteVolumeUpdateLocked(int userId) {
666 if (mRvc != null) {
667 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900668 FullUserRecord user = getFullUserRecordLocked(userId);
669 if (user == null) {
670 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
671 return;
672 }
673 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700674 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
675 } catch (RemoteException e) {
676 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
677 }
678 }
679 }
680
Jaewan Kim92dea332017-02-02 11:52:08 +0900681 /**
682 * Called when the media button receiver for the {@param record} is changed.
683 *
684 * @param record the media session whose media button receiver is updated.
685 */
686 public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
687 synchronized (mLock) {
688 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
689 MediaSessionRecord mediaButtonSession =
690 user.mPriorityStack.getMediaButtonSession();
691 if (record == mediaButtonSession) {
692 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
693 }
694 }
695 }
696
Jaewan Kim50269362016-12-23 11:22:02 +0900697 private String getCallingPackageName(int uid) {
698 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
699 if (packages != null && packages.length > 0) {
700 return packages[0];
701 }
702 return "";
703 }
704
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900705 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900706 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900707 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900708 } catch (RemoteException e) {
709 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
710 }
711 }
712
Jaewan Kima7dce192017-02-16 17:10:54 +0900713 private FullUserRecord getFullUserRecordLocked(int userId) {
714 int fullUserId = mFullUserIds.get(userId, -1);
715 if (fullUserId < 0) {
716 return null;
717 }
718 return mUserRecords.get(fullUserId);
719 }
720
RoboErik4646d282014-05-13 10:13:04 -0700721 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900722 * Information about a full user and its corresponding managed profiles.
723 *
724 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
725 * them when he/she presses a media/volume button. So keeping media sessions for them in one
726 * place makes more sense and increases the readability.</p>
727 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700728 */
Jaewan Kim92dea332017-02-02 11:52:08 +0900729 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
Jaewan Kima7dce192017-02-16 17:10:54 +0900730 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
731 private final int mFullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900732 private final MediaSessionStack mPriorityStack;
RoboErikb214efb2014-07-24 13:20:30 -0700733 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800734 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900735 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700736
Jaewan Kim50269362016-12-23 11:22:02 +0900737 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
738 private int mOnVolumeKeyLongPressListenerUid;
739 private KeyEvent mInitialDownVolumeKeyEvent;
740 private int mInitialDownVolumeStream;
741 private boolean mInitialDownMusicOnly;
742
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800743 private IOnMediaKeyListener mOnMediaKeyListener;
744 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900745 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800746
Jaewan Kima7dce192017-02-16 17:10:54 +0900747 public FullUserRecord(int fullUserId) {
748 mFullUserId = fullUserId;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000749 mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
Jaewan Kima7dce192017-02-16 17:10:54 +0900750 // Restore the remembered media button receiver before the boot.
751 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
752 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
753 if (mediaButtonReceiver == null) {
754 return;
755 }
756 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
757 if (tokens == null || tokens.length != 2) {
758 return;
759 }
760 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
761 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700762 }
763
Jaewan Kima7dce192017-02-16 17:10:54 +0900764 public void destroySessionsForUserLocked(int userId) {
Jaewan Kim92dea332017-02-02 11:52:08 +0900765 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
Jaewan Kima7dce192017-02-16 17:10:54 +0900766 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700767 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700768 }
769 }
770
RoboErik4646d282014-05-13 10:13:04 -0700771 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900772 pw.print(prefix + "Record for full_user=" + mFullUserId);
773 // Dump managed profile user ids associated with this user.
774 int size = mFullUserIds.size();
775 for (int i = 0; i < size; i++) {
776 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
777 && mFullUserIds.valueAt(i) == mFullUserId) {
778 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
779 }
780 }
781 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700782 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900783 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
784 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900785 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800786 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
787 pw.println(indent + "Media key listener package: " +
788 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900789 pw.println(indent + "Callback: " + mCallback);
790 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
791 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
792 mPriorityStack.dump(pw, indent);
793 }
794
Jaewan Kim92dea332017-02-02 11:52:08 +0900795 @Override
796 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
797 MediaSessionRecord newMediaButtonSession) {
798 if (DEBUG_KEY_EVENT) {
Jaewan Kim98e4aaf2017-05-12 17:06:47 +0900799 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
Jaewan Kim92dea332017-02-02 11:52:08 +0900800 }
801 synchronized (mLock) {
802 if (oldMediaButtonSession != null) {
803 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
804 }
805 if (newMediaButtonSession != null) {
806 rememberMediaButtonReceiverLocked(newMediaButtonSession);
807 mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
808 }
809 pushAddressedPlayerChangedLocked();
810 }
811 }
812
813 // Remember media button receiver and keep it in the persistent storage.
814 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900815 PendingIntent receiver = record.getMediaButtonReceiver();
Jaewan Kima7dce192017-02-16 17:10:54 +0900816 mLastMediaButtonReceiver = receiver;
Jaewan Kim92dea332017-02-02 11:52:08 +0900817 mRestoredMediaButtonReceiver = null;
818 String componentName = "";
819 if (receiver != null) {
820 ComponentName component = receiver.getIntent().getComponent();
821 if (component != null
822 && record.getPackageName().equals(component.getPackageName())) {
823 componentName = component.flattenToString();
824 }
RoboErik4646d282014-05-13 10:13:04 -0700825 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900826 Settings.Secure.putStringForUser(mContentResolver,
827 Settings.System.MEDIA_BUTTON_RECEIVER,
828 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
829 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700830 }
RoboErikc8f92d12015-01-05 16:48:07 -0800831
Jaewan Kima7dce192017-02-16 17:10:54 +0900832 private void pushAddressedPlayerChangedLocked() {
833 if (mCallback == null) {
834 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800835 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900836 try {
837 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
838 if (mediaButtonSession != null) {
839 mCallback.onAddressedPlayerChangedToMediaSession(
840 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
841 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
842 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
843 mCurrentFullUserRecord.mLastMediaButtonReceiver
844 .getIntent().getComponent());
845 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
846 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
847 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
848 }
849 } catch (RemoteException e) {
850 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
851 }
852 }
853
854 private MediaSessionRecord getMediaButtonSessionLocked() {
Jaewan Kim92dea332017-02-02 11:52:08 +0900855 return isGlobalPriorityActiveLocked()
856 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
RoboErikc8f92d12015-01-05 16:48:07 -0800857 }
RoboErik4646d282014-05-13 10:13:04 -0700858 }
859
RoboErik2e7a9162014-06-04 16:53:45 -0700860 final class SessionsListenerRecord implements IBinder.DeathRecipient {
861 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700862 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700863 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700864 private final int mPid;
865 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700866
RoboErik7aef77b2014-08-08 15:56:54 -0700867 public SessionsListenerRecord(IActiveSessionsListener listener,
868 ComponentName componentName,
869 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700870 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700871 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700872 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700873 mPid = pid;
874 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700875 }
876
877 @Override
878 public void binderDied() {
879 synchronized (mLock) {
880 mSessionsListeners.remove(this);
881 }
882 }
883 }
884
RoboErik7aef77b2014-08-08 15:56:54 -0700885 final class SettingsObserver extends ContentObserver {
886 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
887 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
888
889 private SettingsObserver() {
890 super(null);
891 }
892
893 private void observe() {
894 mContentResolver.registerContentObserver(mSecureSettingsUri,
895 false, this, UserHandle.USER_ALL);
896 }
897
898 @Override
899 public void onChange(boolean selfChange, Uri uri) {
900 updateActiveSessionListeners();
901 }
902 }
903
RoboErik07c70772014-03-20 13:33:52 -0700904 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700905 private static final String EXTRA_WAKELOCK_ACQUIRED =
906 "android.media.AudioService.WAKELOCK_ACQUIRED";
907 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
908
RoboErik9a9d0b52014-05-20 14:53:39 -0700909 private boolean mVoiceButtonDown = false;
910 private boolean mVoiceButtonHandled = false;
911
RoboErik07c70772014-03-20 13:33:52 -0700912 @Override
RoboErika5b02322014-05-07 17:05:49 -0700913 public ISession createSession(String packageName, ISessionCallback cb, String tag,
914 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800915 final int pid = Binder.getCallingPid();
916 final int uid = Binder.getCallingUid();
917 final long token = Binder.clearCallingIdentity();
918 try {
919 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700920 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
921 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800922 if (cb == null) {
923 throw new IllegalArgumentException("Controller callback cannot be null");
924 }
RoboErika5b02322014-05-07 17:05:49 -0700925 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
926 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700927 } finally {
928 Binder.restoreCallingIdentity(token);
929 }
930 }
931
932 @Override
RoboErika5b02322014-05-07 17:05:49 -0700933 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700934 final int pid = Binder.getCallingPid();
935 final int uid = Binder.getCallingUid();
936 final long token = Binder.clearCallingIdentity();
937
938 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700939 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700940 ArrayList<IBinder> binders = new ArrayList<IBinder>();
941 synchronized (mLock) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900942 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
943 for (MediaSessionRecord record : records) {
944 binders.add(record.getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700945 }
946 }
947 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800948 } finally {
949 Binder.restoreCallingIdentity(token);
950 }
951 }
RoboErika278ea72014-04-24 14:49:01 -0700952
RoboErik2e7a9162014-06-04 16:53:45 -0700953 @Override
954 public void addSessionsListener(IActiveSessionsListener listener,
955 ComponentName componentName, int userId) throws RemoteException {
956 final int pid = Binder.getCallingPid();
957 final int uid = Binder.getCallingUid();
958 final long token = Binder.clearCallingIdentity();
959
960 try {
961 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
962 synchronized (mLock) {
963 int index = findIndexOfSessionsListenerLocked(listener);
964 if (index != -1) {
965 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
966 return;
967 }
968 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700969 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700970 try {
971 listener.asBinder().linkToDeath(record, 0);
972 } catch (RemoteException e) {
973 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
974 return;
975 }
976 mSessionsListeners.add(record);
977 }
978 } finally {
979 Binder.restoreCallingIdentity(token);
980 }
981 }
982
983 @Override
984 public void removeSessionsListener(IActiveSessionsListener listener)
985 throws RemoteException {
986 synchronized (mLock) {
987 int index = findIndexOfSessionsListenerLocked(listener);
988 if (index != -1) {
989 SessionsListenerRecord record = mSessionsListeners.remove(index);
990 try {
991 record.mListener.asBinder().unlinkToDeath(record, 0);
992 } catch (Exception e) {
993 // ignore exceptions, the record is being removed
994 }
995 }
996 }
997 }
998
RoboErik8a2cfc32014-05-16 11:19:38 -0700999 /**
1000 * Handles the dispatching of the media button events to one of the
1001 * registered listeners, or if there was none, broadcast an
1002 * ACTION_MEDIA_BUTTON intent to the rest of the system.
1003 *
1004 * @param keyEvent a non-null KeyEvent whose key code is one of the
1005 * supported media buttons
1006 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
1007 * while this key event is dispatched.
1008 */
1009 @Override
1010 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
1011 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
1012 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
1013 return;
1014 }
Jeff Brown38d3feb2015-03-19 18:26:30 -07001015
RoboErik8a2cfc32014-05-16 11:19:38 -07001016 final int pid = Binder.getCallingPid();
1017 final int uid = Binder.getCallingUid();
1018 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -07001019 try {
Jeff Brown221a8272015-03-23 13:53:09 -07001020 if (DEBUG) {
1021 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1022 + keyEvent);
1023 }
Jeff Brown38d3feb2015-03-19 18:26:30 -07001024 if (!isUserSetupComplete()) {
1025 // Global media key handling can have the side-effect of starting new
1026 // activities which is undesirable while setup is in progress.
1027 Slog.i(TAG, "Not dispatching media key event because user "
1028 + "setup is in progress.");
1029 return;
1030 }
1031
RoboErik8a2cfc32014-05-16 11:19:38 -07001032 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001033 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001034 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
1035 // Prevent dispatching key event through reflection while the global
1036 // priority session is active.
1037 Slog.i(TAG, "Only the system can dispatch media key event "
1038 + "to the global priority session.");
1039 return;
1040 }
Jaewan Kim98003d32017-02-24 18:33:04 +09001041 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001042 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +09001043 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001044 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +09001045 }
1046 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001047 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim98003d32017-02-24 18:33:04 +09001048 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
1049 return;
1050 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001051 Log.w(TAG, "Failed to send " + keyEvent
1052 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +09001053 }
1054 }
1055 }
Jaewan Kim51255012017-02-24 16:19:14 +09001056 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001057 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -07001058 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +09001059 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -07001060 }
1061 }
1062 } finally {
1063 Binder.restoreCallingIdentity(token);
1064 }
1065 }
1066
RoboErika278ea72014-04-24 14:49:01 -07001067 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +09001068 public void setCallback(ICallback callback) {
1069 final int pid = Binder.getCallingPid();
1070 final int uid = Binder.getCallingUid();
1071 final long token = Binder.clearCallingIdentity();
1072 try {
Amith Yamasanie259ad22017-04-24 11:30:19 -07001073 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001074 throw new SecurityException("Only Bluetooth service processes can set"
1075 + " Callback");
1076 }
1077 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001078 int userId = UserHandle.getUserId(uid);
1079 FullUserRecord user = getFullUserRecordLocked(userId);
1080 if (user == null || user.mFullUserId != userId) {
1081 Log.w(TAG, "Only the full user can set the callback"
1082 + ", userId=" + userId);
1083 return;
1084 }
1085 user.mCallback = callback;
1086 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +09001087 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +09001088 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001089 return;
1090 }
1091 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001092 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +09001093 new IBinder.DeathRecipient() {
1094 @Override
1095 public void binderDied() {
1096 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001097 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001098 }
1099 }
1100 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +09001101 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +09001102 } catch (RemoteException e) {
1103 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +09001104 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001105 }
1106 }
1107 } finally {
1108 Binder.restoreCallingIdentity(token);
1109 }
1110 }
1111
1112 @Override
Jaewan Kim50269362016-12-23 11:22:02 +09001113 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
1114 final int pid = Binder.getCallingPid();
1115 final int uid = Binder.getCallingUid();
1116 final long token = Binder.clearCallingIdentity();
1117 try {
1118 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
1119 if (getContext().checkPermission(
1120 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
1121 != PackageManager.PERMISSION_GRANTED) {
1122 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
1123 " permission.");
1124 }
1125
1126 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001127 int userId = UserHandle.getUserId(uid);
1128 FullUserRecord user = getFullUserRecordLocked(userId);
1129 if (user == null || user.mFullUserId != userId) {
1130 Log.w(TAG, "Only the full user can set the volume key long-press listener"
1131 + ", userId=" + userId);
1132 return;
1133 }
Jaewan Kim50269362016-12-23 11:22:02 +09001134 if (user.mOnVolumeKeyLongPressListener != null &&
1135 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001136 Log.w(TAG, "The volume key long-press listener cannot be reset"
1137 + " by another app , mOnVolumeKeyLongPressListener="
1138 + user.mOnVolumeKeyLongPressListenerUid
1139 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +09001140 return;
1141 }
1142
1143 user.mOnVolumeKeyLongPressListener = listener;
1144 user.mOnVolumeKeyLongPressListenerUid = uid;
1145
Jaewan Kima7dce192017-02-16 17:10:54 +09001146 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001147 + listener + " is set by " + getCallingPackageName(uid));
1148
1149 if (user.mOnVolumeKeyLongPressListener != null) {
1150 try {
1151 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1152 new IBinder.DeathRecipient() {
1153 @Override
1154 public void binderDied() {
1155 synchronized (mLock) {
1156 user.mOnVolumeKeyLongPressListener = null;
1157 }
1158 }
1159 }, 0);
1160 } catch (RemoteException e) {
1161 Log.w(TAG, "Failed to set death recipient "
1162 + user.mOnVolumeKeyLongPressListener);
1163 user.mOnVolumeKeyLongPressListener = null;
1164 }
1165 }
1166 }
1167 } finally {
1168 Binder.restoreCallingIdentity(token);
1169 }
1170 }
1171
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001172 @Override
1173 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1174 final int pid = Binder.getCallingPid();
1175 final int uid = Binder.getCallingUid();
1176 final long token = Binder.clearCallingIdentity();
1177 try {
1178 // Enforce SET_MEDIA_KEY_LISTENER permission.
1179 if (getContext().checkPermission(
1180 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1181 != PackageManager.PERMISSION_GRANTED) {
1182 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1183 " permission.");
1184 }
1185
1186 synchronized (mLock) {
1187 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001188 FullUserRecord user = getFullUserRecordLocked(userId);
1189 if (user == null || user.mFullUserId != userId) {
1190 Log.w(TAG, "Only the full user can set the media key listener"
1191 + ", userId=" + userId);
1192 return;
1193 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001194 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001195 Log.w(TAG, "The media key listener cannot be reset by another app. "
1196 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1197 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001198 return;
1199 }
1200
1201 user.mOnMediaKeyListener = listener;
1202 user.mOnMediaKeyListenerUid = uid;
1203
Jaewan Kima7dce192017-02-16 17:10:54 +09001204 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001205 + " is set by " + getCallingPackageName(uid));
1206
1207 if (user.mOnMediaKeyListener != null) {
1208 try {
1209 user.mOnMediaKeyListener.asBinder().linkToDeath(
1210 new IBinder.DeathRecipient() {
1211 @Override
1212 public void binderDied() {
1213 synchronized (mLock) {
1214 user.mOnMediaKeyListener = null;
1215 }
1216 }
1217 }, 0);
1218 } catch (RemoteException e) {
1219 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1220 user.mOnMediaKeyListener = null;
1221 }
1222 }
1223 }
1224 } finally {
1225 Binder.restoreCallingIdentity(token);
1226 }
1227 }
1228
Jaewan Kim50269362016-12-23 11:22:02 +09001229 /**
1230 * Handles the dispatching of the volume button events to one of the
1231 * registered listeners. If there's a volume key long-press listener and
1232 * there's no active global priority session, long-pressess will be sent to the
1233 * long-press listener instead of adjusting volume.
1234 *
1235 * @param keyEvent a non-null KeyEvent whose key code is one of the
1236 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1237 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1238 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1239 * @param stream stream type to adjust volume.
1240 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1241 */
1242 @Override
1243 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1244 if (keyEvent == null ||
1245 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1246 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1247 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1248 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1249 return;
1250 }
1251
1252 final int pid = Binder.getCallingPid();
1253 final int uid = Binder.getCallingUid();
1254 final long token = Binder.clearCallingIdentity();
1255
Jaewan Kimb2781e72017-03-02 09:57:09 +09001256 if (DEBUG_KEY_EVENT) {
Jaewan Kim50269362016-12-23 11:22:02 +09001257 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1258 + keyEvent);
1259 }
1260
1261 try {
1262 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001263 if (isGlobalPriorityActiveLocked()
1264 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001265 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1266 } else {
1267 // TODO: Consider the case when both volume up and down keys are pressed
1268 // at the same time.
1269 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1270 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001271 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001272 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1273 KeyEvent.obtain(keyEvent);
1274 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1275 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001276 mHandler.sendMessageDelayed(
1277 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001278 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1279 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001280 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001281 }
1282 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001283 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001284 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001285 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001286 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001287 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001288 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001289 }
1290 dispatchVolumeKeyLongPressLocked(keyEvent);
1291 }
1292 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001293 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001294 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
1295 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1296 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001297 // Short-press. Should change volume.
1298 dispatchVolumeKeyEventLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001299 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1300 mCurrentFullUserRecord.mInitialDownVolumeStream,
1301 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001302 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1303 } else {
1304 dispatchVolumeKeyLongPressLocked(keyEvent);
1305 }
1306 }
1307 }
1308 }
1309 } finally {
1310 Binder.restoreCallingIdentity(token);
1311 }
1312 }
1313
Jaewan Kim50269362016-12-23 11:22:02 +09001314 private void dispatchVolumeKeyEventLocked(
1315 KeyEvent keyEvent, int stream, boolean musicOnly) {
1316 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1317 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1318 int direction = 0;
1319 boolean isMute = false;
1320 switch (keyEvent.getKeyCode()) {
1321 case KeyEvent.KEYCODE_VOLUME_UP:
1322 direction = AudioManager.ADJUST_RAISE;
1323 break;
1324 case KeyEvent.KEYCODE_VOLUME_DOWN:
1325 direction = AudioManager.ADJUST_LOWER;
1326 break;
1327 case KeyEvent.KEYCODE_VOLUME_MUTE:
1328 isMute = true;
1329 break;
1330 }
1331 if (down || up) {
1332 int flags = AudioManager.FLAG_FROM_KEY;
1333 if (musicOnly) {
1334 // This flag is used when the screen is off to only affect active media.
1335 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1336 } else {
1337 // These flags are consistent with the home screen
1338 if (up) {
1339 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1340 } else {
1341 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1342 }
1343 }
1344 if (direction != 0) {
1345 // If this is action up we want to send a beep for non-music events
1346 if (up) {
1347 direction = 0;
1348 }
1349 dispatchAdjustVolumeLocked(stream, direction, flags);
1350 } else if (isMute) {
1351 if (down && keyEvent.getRepeatCount() == 0) {
1352 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1353 }
1354 }
1355 }
1356 }
1357
1358 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001359 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001360 final long token = Binder.clearCallingIdentity();
1361 try {
1362 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001363 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001364 }
1365 } finally {
1366 Binder.restoreCallingIdentity(token);
1367 }
1368 }
1369
1370 @Override
RoboErik19c95182014-06-23 15:38:48 -07001371 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1372 final int pid = Binder.getCallingPid();
1373 final int uid = Binder.getCallingUid();
1374 final long token = Binder.clearCallingIdentity();
1375 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001376 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001377 mRvc = rvc;
1378 } finally {
1379 Binder.restoreCallingIdentity(token);
1380 }
1381 }
1382
1383 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001384 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001385 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001386 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001387 }
RoboErikde9ba392014-09-26 12:51:01 -07001388 }
1389
1390 @Override
RoboErika278ea72014-04-24 14:49:01 -07001391 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001392 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
RoboErika278ea72014-04-24 14:49:01 -07001393
1394 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1395 pw.println();
1396
1397 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001398 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001399 pw.println("Global priority session is " + mGlobalPrioritySession);
Jaewan Kim101b4d52017-05-18 13:23:11 +09001400 if (mGlobalPrioritySession != null) {
1401 mGlobalPrioritySession.dump(pw, " ");
1402 }
RoboErik4646d282014-05-13 10:13:04 -07001403 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001404 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001405 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001406 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001407 }
Sungsoo Lim875e6972017-11-03 02:22:35 +00001408 mAudioPlayerStateMonitor.dump(getContext(), pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001409 }
1410 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001411
Jaewan Kimceb6b6e2018-01-21 20:56:10 +09001412 @Override
1413 public Bundle createSessionToken(String sessionPackage, String id,
1414 IMediaSession2 sessionBinder) throws RemoteException {
1415 int uid = Binder.getCallingUid();
1416 int pid = Binder.getCallingPid();
1417
1418 MediaSession2Record record;
1419 SessionToken token;
1420 // TODO(jaewan): Add sanity check for the token if calling package is from uid.
1421 synchronized (mLock) {
1422 record = getSessionRecordLocked(sessionPackage, id);
1423 if (record == null) {
1424 record = new MediaSession2Record(getContext(), mSessionDestroyedListener);
1425 mSessions.add(record);
1426 }
1427 token = record.createSessionToken(pid, sessionPackage, id, sessionBinder);
1428 if (token == null) {
1429 Log.d(TAG, "failed to create session token for " + sessionPackage
1430 + " from pid=" + pid + ". Previously " + record);
1431 } else {
1432 Log.d(TAG, "session " + token + " is created");
1433 }
1434 }
1435 return token == null ? null : token.toBundle();
1436 }
1437
1438 // TODO(jaewan): Protect this API with permission
1439 // TODO(jaewan): Add listeners for change in operations..
1440 @Override
1441 public List<Bundle> getSessionTokens(boolean activeSessionOnly,
1442 boolean sessionServiceOnly) throws RemoteException {
1443 List<Bundle> tokens = new ArrayList<>();
1444 synchronized (mLock) {
1445 for (int i = 0; i < mSessions.size(); i++) {
1446 MediaSession2Record record = mSessions.get(i);
1447 boolean isSessionService = (record instanceof MediaSessionService2Record);
1448 boolean isActive = record.getSessionPid() != 0;
1449 if ((!activeSessionOnly && isSessionService)
1450 || (!sessionServiceOnly && isActive)) {
1451 SessionToken token = record.getToken();
1452 if (token != null) {
1453 tokens.add(token.toBundle());
1454 } else {
1455 Log.wtf(TAG, "Null token for record=" + record);
1456 }
1457 }
1458 }
1459 }
1460 return tokens;
1461 }
1462
RoboErik2e7a9162014-06-04 16:53:45 -07001463 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1464 final int uid) {
1465 String packageName = null;
1466 if (componentName != null) {
1467 // If they gave us a component name verify they own the
1468 // package
1469 packageName = componentName.getPackageName();
1470 enforcePackageName(packageName, uid);
1471 }
1472 // Check that they can make calls on behalf of the user and
1473 // get the final user id
1474 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1475 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1476 // Check if they have the permissions or their component is
1477 // enabled for the user they're calling from.
1478 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1479 return resolvedUserId;
1480 }
1481
Jaewan Kim50269362016-12-23 11:22:02 +09001482 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001483 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1484 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001485
RoboErik9c785402014-11-11 16:52:26 -08001486 boolean preferSuggestedStream = false;
1487 if (isValidLocalStreamType(suggestedStream)
1488 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1489 preferSuggestedStream = true;
1490 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001491 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001492 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1493 + flags + ", suggestedStream=" + suggestedStream
1494 + ", preferSuggestedStream=" + preferSuggestedStream);
1495 }
RoboErik9c785402014-11-11 16:52:26 -08001496 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001497 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1498 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001499 if (DEBUG) {
1500 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001501 }
RoboErikb7c014c2014-07-22 15:58:22 -07001502 return;
RoboErik3c45c292014-07-08 16:47:31 -07001503 }
Shibin George19e84042016-06-14 20:42:13 +05301504
1505 // Execute mAudioService.adjustSuggestedStreamVolume() on
1506 // handler thread of MediaSessionService.
1507 // This will release the MediaSessionService.mLock sooner and avoid
1508 // a potential deadlock between MediaSessionService.mLock and
1509 // ActivityManagerService lock.
1510 mHandler.post(new Runnable() {
1511 @Override
1512 public void run() {
1513 try {
1514 String packageName = getContext().getOpPackageName();
1515 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1516 flags, packageName, TAG);
1517 } catch (RemoteException e) {
1518 Log.e(TAG, "Error adjusting default volume.", e);
Hyundo Moon739d6c22017-09-18 17:01:48 +09001519 } catch (IllegalArgumentException e) {
1520 Log.e(TAG, "Cannot adjust volume: direction=" + direction
1521 + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
1522 e);
Shibin George19e84042016-06-14 20:42:13 +05301523 }
1524 }
1525 });
RoboErikb69ffd42014-05-30 14:57:59 -07001526 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001527 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001528 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001529 }
1530 }
1531
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001532 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001533 int action = keyEvent.getAction();
1534 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1535 if (action == KeyEvent.ACTION_DOWN) {
1536 if (keyEvent.getRepeatCount() == 0) {
1537 mVoiceButtonDown = true;
1538 mVoiceButtonHandled = false;
1539 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1540 mVoiceButtonHandled = true;
1541 startVoiceInput(needWakeLock);
1542 }
1543 } else if (action == KeyEvent.ACTION_UP) {
1544 if (mVoiceButtonDown) {
1545 mVoiceButtonDown = false;
1546 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1547 // Resend the down then send this event through
1548 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001549 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1550 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001551 }
1552 }
1553 }
1554 }
1555
Jaewan Kim98003d32017-02-24 18:33:04 +09001556 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001557 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001558 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001559 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001560 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001561 }
1562 if (needWakeLock) {
1563 mKeyEventReceiver.aquireWakeLockLocked();
1564 }
Jaewan Kim50269362016-12-23 11:22:02 +09001565 // 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 -07001566 session.sendMediaButton(keyEvent,
1567 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001568 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001569 getContext().getPackageName());
Jaewan Kima7dce192017-02-16 17:10:54 +09001570 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001571 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001572 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
1573 keyEvent,
Jaewan Kimbd16f452017-02-03 16:21:38 +09001574 new MediaSession.Token(session.getControllerBinder()));
1575 } catch (RemoteException e) {
1576 Log.w(TAG, "Failed to send callback", e);
1577 }
1578 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001579 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1580 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1581 if (needWakeLock) {
1582 mKeyEventReceiver.aquireWakeLockLocked();
1583 }
1584 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1585 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1586 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1587 try {
1588 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1589 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1590 if (DEBUG_KEY_EVENT) {
1591 Log.d(TAG, "Sending " + keyEvent
Jaewan Kim92dea332017-02-02 11:52:08 +09001592 + " to the last known PendingIntent " + receiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001593 }
1594 receiver.send(getContext(),
1595 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1596 mediaButtonIntent, mKeyEventReceiver, mHandler);
1597 if (mCurrentFullUserRecord.mCallback != null) {
1598 ComponentName componentName = mCurrentFullUserRecord
1599 .mLastMediaButtonReceiver.getIntent().getComponent();
1600 if (componentName != null) {
1601 mCurrentFullUserRecord.mCallback
1602 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1603 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001604 }
RoboErikc8f92d12015-01-05 16:48:07 -08001605 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001606 } else {
1607 ComponentName receiver =
1608 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1609 if (DEBUG_KEY_EVENT) {
1610 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1611 + receiver);
1612 }
1613 mediaButtonIntent.setComponent(receiver);
1614 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim92dea332017-02-02 11:52:08 +09001615 UserHandle.of(mCurrentFullUserRecord
1616 .mRestoredMediaButtonReceiverUserId));
Jaewan Kima7dce192017-02-16 17:10:54 +09001617 if (mCurrentFullUserRecord.mCallback != null) {
1618 mCurrentFullUserRecord.mCallback
1619 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1620 keyEvent, receiver);
1621 }
RoboErikb214efb2014-07-24 13:20:30 -07001622 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001623 } catch (CanceledException e) {
1624 Log.i(TAG, "Error sending key event to media button receiver "
1625 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1626 } catch (RemoteException e) {
1627 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001628 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001629 }
1630 }
1631
1632 private void startVoiceInput(boolean needWakeLock) {
1633 Intent voiceIntent = null;
1634 // select which type of search to launch:
1635 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1636 // - device locked or screen off: action is
1637 // ACTION_VOICE_SEARCH_HANDS_FREE
1638 // with EXTRA_SECURE set to true if the device is securely locked
1639 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1640 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1641 if (!isLocked && pm.isScreenOn()) {
1642 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1643 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1644 } else {
1645 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1646 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1647 isLocked && mKeyguardManager.isKeyguardSecure());
1648 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1649 }
1650 // start the search activity
1651 if (needWakeLock) {
1652 mMediaEventWakeLock.acquire();
1653 }
1654 try {
1655 if (voiceIntent != null) {
1656 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1657 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001658 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001659 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1660 }
1661 } catch (ActivityNotFoundException e) {
1662 Log.w(TAG, "No activity for search: " + e);
1663 } finally {
1664 if (needWakeLock) {
1665 mMediaEventWakeLock.release();
1666 }
1667 }
1668 }
1669
1670 private boolean isVoiceKey(int keyCode) {
Jaewan Kimba18d8e2017-05-12 17:37:57 +09001671 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
Jaewan Kimfdb612e2017-07-01 09:23:41 +09001672 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
RoboErik9a9d0b52014-05-20 14:53:39 -07001673 }
1674
Jeff Brown38d3feb2015-03-19 18:26:30 -07001675 private boolean isUserSetupComplete() {
1676 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1677 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1678 }
1679
RoboErik9c785402014-11-11 16:52:26 -08001680 // we only handle public stream types, which are 0-5
1681 private boolean isValidLocalStreamType(int streamType) {
1682 return streamType >= AudioManager.STREAM_VOICE_CALL
1683 && streamType <= AudioManager.STREAM_NOTIFICATION;
1684 }
1685
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001686 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1687 private KeyEvent mKeyEvent;
1688 private boolean mNeedWakeLock;
1689 private boolean mHandled;
1690
1691 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1692 super(mHandler);
1693 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1694 mKeyEvent = keyEvent;
1695 mNeedWakeLock = needWakeLock;
1696 }
1697
1698 @Override
1699 public void run() {
1700 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1701 dispatchMediaKeyEvent();
1702 }
1703
1704 @Override
1705 protected void onReceiveResult(int resultCode, Bundle resultData) {
1706 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1707 mHandled = true;
1708 mHandler.removeCallbacks(this);
1709 return;
1710 }
1711 dispatchMediaKeyEvent();
1712 }
1713
1714 private void dispatchMediaKeyEvent() {
1715 if (mHandled) {
1716 return;
1717 }
1718 mHandled = true;
1719 mHandler.removeCallbacks(this);
1720 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001721 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001722 && isVoiceKey(mKeyEvent.getKeyCode())) {
1723 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1724 } else {
1725 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1726 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001727 }
1728 }
1729 }
1730
RoboErik418c10c2014-05-19 09:25:25 -07001731 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1732
RoboErikb214efb2014-07-24 13:20:30 -07001733 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1734 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001735 private final Handler mHandler;
1736 private int mRefCount = 0;
1737 private int mLastTimeoutId = 0;
1738
1739 public KeyEventWakeLockReceiver(Handler handler) {
1740 super(handler);
1741 mHandler = handler;
1742 }
1743
1744 public void onTimeout() {
1745 synchronized (mLock) {
1746 if (mRefCount == 0) {
1747 // We've already released it, so just return
1748 return;
1749 }
1750 mLastTimeoutId++;
1751 mRefCount = 0;
1752 releaseWakeLockLocked();
1753 }
1754 }
1755
1756 public void aquireWakeLockLocked() {
1757 if (mRefCount == 0) {
1758 mMediaEventWakeLock.acquire();
1759 }
1760 mRefCount++;
1761 mHandler.removeCallbacks(this);
1762 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1763
1764 }
1765
1766 @Override
1767 public void run() {
1768 onTimeout();
1769 }
1770
RoboErik8a2cfc32014-05-16 11:19:38 -07001771 @Override
1772 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001773 if (resultCode < mLastTimeoutId) {
1774 // Ignore results from calls that were before the last
1775 // timeout, just in case.
1776 return;
1777 } else {
1778 synchronized (mLock) {
1779 if (mRefCount > 0) {
1780 mRefCount--;
1781 if (mRefCount == 0) {
1782 releaseWakeLockLocked();
1783 }
1784 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001785 }
1786 }
1787 }
RoboErik418c10c2014-05-19 09:25:25 -07001788
1789 private void releaseWakeLockLocked() {
1790 mMediaEventWakeLock.release();
1791 mHandler.removeCallbacks(this);
1792 }
RoboErikb214efb2014-07-24 13:20:30 -07001793
1794 @Override
1795 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1796 String resultData, Bundle resultExtras) {
1797 onReceiveResult(resultCode, null);
1798 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001799 };
1800
1801 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1802 @Override
1803 public void onReceive(Context context, Intent intent) {
1804 if (intent == null) {
1805 return;
1806 }
1807 Bundle extras = intent.getExtras();
1808 if (extras == null) {
1809 return;
1810 }
1811 synchronized (mLock) {
1812 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1813 && mMediaEventWakeLock.isHeld()) {
1814 mMediaEventWakeLock.release();
1815 }
1816 }
1817 }
1818 };
RoboErik01fe6612014-02-13 14:19:04 -08001819 }
1820
RoboErik2e7a9162014-06-04 16:53:45 -07001821 final class MessageHandler extends Handler {
1822 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001823 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
Jaewan Kim92dea332017-02-02 11:52:08 +09001824 private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
RoboErik2e7a9162014-06-04 16:53:45 -07001825
1826 @Override
1827 public void handleMessage(Message msg) {
1828 switch (msg.what) {
1829 case MSG_SESSIONS_CHANGED:
Jaewan Kim92dea332017-02-02 11:52:08 +09001830 pushSessionsChanged((int) msg.obj);
RoboErik2e7a9162014-06-04 16:53:45 -07001831 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001832 case MSG_VOLUME_INITIAL_DOWN:
1833 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001834 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001835 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1836 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1837 // Mark that the key is already handled.
1838 user.mInitialDownVolumeKeyEvent = null;
1839 }
1840 }
1841 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001842 }
1843 }
1844
Jaewan Kim92dea332017-02-02 11:52:08 +09001845 public void postSessionsChanged(int userId) {
1846 // Use object instead of the arguments when posting message to remove pending requests.
1847 Integer userIdInteger = mIntegerCache.get(userId);
1848 if (userIdInteger == null) {
1849 userIdInteger = Integer.valueOf(userId);
1850 mIntegerCache.put(userId, userIdInteger);
1851 }
1852 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
1853 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
RoboErik2e7a9162014-06-04 16:53:45 -07001854 }
1855 }
RoboErik01fe6612014-02-13 14:19:04 -08001856}