blob: ed558aa67be3882259ccd310653d8340f619eb39 [file] [log] [blame]
RoboErik01fe6612014-02-13 14:19:04 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.media;
18
RoboErika278ea72014-04-24 14:49:01 -070019import android.Manifest;
Jaewan Kim50269362016-12-23 11:22:02 +090020import android.annotation.NonNull;
RoboErik8a2cfc32014-05-16 11:19:38 -070021import android.app.Activity;
RoboErike7880d82014-04-30 12:48:25 -070022import android.app.ActivityManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070023import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070024import android.app.PendingIntent;
25import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070026import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070027import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070028import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070029import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080030import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070031import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070032import android.content.pm.PackageManager;
RoboErik7aef77b2014-08-08 15:56:54 -070033import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070034import android.media.AudioManager;
John Spurlockeb69e242015-02-17 17:15:04 -050035import android.media.AudioManagerInternal;
RoboErik94c716e2014-09-14 13:54:31 -070036import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070037import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070038import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070039import android.media.session.IActiveSessionsListener;
Jaewan Kimbd16f452017-02-03 16:21:38 +090040import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080041import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090042import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070043import android.media.session.ISession;
44import android.media.session.ISessionCallback;
45import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070046import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080047import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070048import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080049import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070050import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080051import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070052import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070053import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070054import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090055import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080056import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070057import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070058import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070059import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090060import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070061import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070062import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080063import android.text.TextUtils;
64import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070065import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070066import android.util.SparseArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070067import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090068import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080069
John Spurlockeb69e242015-02-17 17:15:04 -050070import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080071import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070072import com.android.server.Watchdog;
73import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080074
RoboErika278ea72014-04-24 14:49:01 -070075import java.io.FileDescriptor;
76import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080077import java.util.ArrayList;
Jaewan Kim8f729082016-06-21 12:36:26 +090078import java.util.Arrays;
RoboErike7880d82014-04-30 12:48:25 -070079import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080080
81/**
82 * System implementation of MediaSessionManager
83 */
RoboErika278ea72014-04-24 14:49:01 -070084public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080085 private static final String TAG = "MediaSessionService";
86 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090087 // Leave log for key event always.
88 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080089
RoboErik418c10c2014-05-19 09:25:25 -070090 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080091 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070092
RoboErik2610d712015-01-07 11:10:23 -080093 /* package */final IBinder mICallback = new Binder();
94
RoboErik01fe6612014-02-13 14:19:04 -080095 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070096 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080097
RoboErik4646d282014-05-13 10:13:04 -070098 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070099 private final ArrayList<SessionsListenerRecord> mSessionsListeners
100 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800101 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700102 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700103 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900104 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800105
RoboErik9a9d0b52014-05-20 14:53:39 -0700106 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700107 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -0500108 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700109 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700110 private SettingsObserver mSettingsObserver;
Jaewan Kimbd16f452017-02-03 16:21:38 +0900111 private ICallback mCallback;
RoboErik9a9d0b52014-05-20 14:53:39 -0700112
Jaewan Kim8f729082016-06-21 12:36:26 +0900113 // List of user IDs running in the foreground.
114 // Multiple users can be in the foreground if the work profile is on.
115 private final List<Integer> mCurrentUserIdList = new ArrayList<>();
RoboErike7880d82014-04-30 12:48:25 -0700116
RoboErik19c95182014-06-23 15:38:48 -0700117 // Used to notify system UI when remote volume was changed. TODO find a
118 // better way to handle this.
119 private IRemoteVolumeController mRvc;
120
RoboErik01fe6612014-02-13 14:19:04 -0800121 public MediaSessionService(Context context) {
122 super(context);
123 mSessionManagerImpl = new SessionManagerImpl();
RoboErika8f95142014-05-05 14:23:49 -0700124 mPriorityStack = new MediaSessionStack();
RoboErik8a2cfc32014-05-16 11:19:38 -0700125 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
126 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900127 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
RoboErik01fe6612014-02-13 14:19:04 -0800128 }
129
130 @Override
131 public void onStart() {
132 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700133 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700134 mKeyguardManager =
135 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700136 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500137 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700138 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700139 mSettingsObserver = new SettingsObserver();
140 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800141
142 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700143 }
144
145 private IAudioService getAudioService() {
146 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
147 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700148 }
149
RoboErika8f95142014-05-05 14:23:49 -0700150 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700151 synchronized (mLock) {
Jaewan Kime0ca3f32017-02-16 15:52:39 +0900152 UserRecord user = mUserRecords.get(record.getUserId());
153 if (user == null || !user.mSessions.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700154 Log.d(TAG, "Unknown session updated. Ignoring.");
155 return;
156 }
RoboErika8f95142014-05-05 14:23:49 -0700157 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700158 }
RoboErik2e7a9162014-06-04 16:53:45 -0700159 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700160 }
161
RoboErik9c5b7cb2015-01-15 15:09:09 -0800162 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900163 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800164 */
165 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900166 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800167 return;
168 }
169 try {
170 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
171 } catch (Exception e) {
172 Log.wtf(TAG, "Error sending volume change to system UI.", e);
173 }
174 }
175
RoboErika8f95142014-05-05 14:23:49 -0700176 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700177 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700178 synchronized (mLock) {
Jaewan Kime0ca3f32017-02-16 15:52:39 +0900179 UserRecord user = mUserRecords.get(record.getUserId());
180 if (user == null || !user.mSessions.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700181 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
182 return;
183 }
RoboErik2e7a9162014-06-04 16:53:45 -0700184 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
185 }
186 if (updateSessions) {
187 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700188 }
189 }
190
RoboErik19c95182014-06-23 15:38:48 -0700191 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
192 synchronized (mLock) {
Jaewan Kime0ca3f32017-02-16 15:52:39 +0900193 UserRecord user = mUserRecords.get(record.getUserId());
194 if (user == null || !user.mSessions.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700195 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
196 return;
197 }
198 pushRemoteVolumeUpdateLocked(record.getUserId());
199 }
200 }
201
RoboErika278ea72014-04-24 14:49:01 -0700202 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900203 public void onStartUser(int userId) {
204 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700205 updateUser();
206 }
207
208 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900209 public void onSwitchUser(int userId) {
210 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700211 updateUser();
212 }
213
214 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900215 public void onStopUser(int userId) {
216 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700217 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900218 UserRecord user = mUserRecords.get(userId);
RoboErik4646d282014-05-13 10:13:04 -0700219 if (user != null) {
220 destroyUserLocked(user);
221 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900222 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700223 }
224 }
225
226 @Override
RoboErika278ea72014-04-24 14:49:01 -0700227 public void monitor() {
228 synchronized (mLock) {
229 // Check for deadlock
230 }
231 }
232
RoboErik4646d282014-05-13 10:13:04 -0700233 protected void enforcePhoneStatePermission(int pid, int uid) {
234 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
235 != PackageManager.PERMISSION_GRANTED) {
236 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
237 }
238 }
239
RoboErik01fe6612014-02-13 14:19:04 -0800240 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700241 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800242 destroySessionLocked(session);
243 }
244 }
245
246 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700247 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800248 destroySessionLocked(session);
249 }
250 }
251
RoboErik4646d282014-05-13 10:13:04 -0700252 private void updateUser() {
253 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900254 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
255 int currentUser = ActivityManager.getCurrentUser();
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700256 // Include all profiles even though they aren't yet enabled to handle work profile case.
257 int[] userIds = manager.getProfileIdsWithDisabled(currentUser);
Jaewan Kim8f729082016-06-21 12:36:26 +0900258 mCurrentUserIdList.clear();
259 if (userIds != null && userIds.length > 0) {
260 for (int userId : userIds) {
261 mCurrentUserIdList.add(userId);
RoboErik4646d282014-05-13 10:13:04 -0700262 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900263 } else {
264 // This shouldn't happen.
265 Log.w(TAG, "Failed to get enabled profiles.");
266 mCurrentUserIdList.add(currentUser);
267 }
268 for (int userId : mCurrentUserIdList) {
269 if (mUserRecords.get(userId) == null) {
270 mUserRecords.put(userId, new UserRecord(getContext(), userId));
271 }
RoboErik4646d282014-05-13 10:13:04 -0700272 }
273 }
274 }
275
RoboErik7aef77b2014-08-08 15:56:54 -0700276 private void updateActiveSessionListeners() {
277 synchronized (mLock) {
278 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
279 SessionsListenerRecord listener = mSessionsListeners.get(i);
280 try {
281 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
282 listener.mUserId);
283 } catch (SecurityException e) {
284 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
285 + " is no longer authorized. Disconnecting.");
286 mSessionsListeners.remove(i);
287 try {
288 listener.mListener
289 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
290 } catch (Exception e1) {
291 // ignore
292 }
293 }
294 }
295 }
296 }
297
RoboErik4646d282014-05-13 10:13:04 -0700298 /**
299 * Stop the user and unbind from everything.
300 *
301 * @param user The user to dispose of
302 */
303 private void destroyUserLocked(UserRecord user) {
RoboErik4646d282014-05-13 10:13:04 -0700304 user.destroyLocked();
305 mUserRecords.remove(user.mUserId);
306 }
307
308 /*
309 * When a session is removed several things need to happen.
310 * 1. We need to remove it from the relevant user.
311 * 2. We need to remove it from the priority stack.
312 * 3. We need to remove it from all sessions.
313 * 4. If this is the system priority session we need to clear it.
314 * 5. We need to unlink to death from the cb binder
315 * 6. We need to tell the session to do any final cleanup (onDestroy)
316 */
RoboErik01fe6612014-02-13 14:19:04 -0800317 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900318 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900319 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900320 }
RoboErik4646d282014-05-13 10:13:04 -0700321 int userId = session.getUserId();
322 UserRecord user = mUserRecords.get(userId);
323 if (user != null) {
324 user.removeSessionLocked(session);
325 }
326
RoboErika8f95142014-05-05 14:23:49 -0700327 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700328
329 try {
330 session.getCallback().asBinder().unlinkToDeath(session, 0);
331 } catch (Exception e) {
332 // ignore exceptions while destroying a session.
333 }
334 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700335
336 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800337 }
338
339 private void enforcePackageName(String packageName, int uid) {
340 if (TextUtils.isEmpty(packageName)) {
341 throw new IllegalArgumentException("packageName may not be empty");
342 }
343 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
344 final int packageCount = packages.length;
345 for (int i = 0; i < packageCount; i++) {
346 if (packageName.equals(packages[i])) {
347 return;
348 }
349 }
350 throw new IllegalArgumentException("packageName is not owned by the calling process");
351 }
352
RoboErike7880d82014-04-30 12:48:25 -0700353 /**
354 * Checks a caller's authorization to register an IRemoteControlDisplay.
355 * Authorization is granted if one of the following is true:
356 * <ul>
357 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
358 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700359 * <li>the caller's listener is one of the enabled notification listeners
360 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700361 * </ul>
362 */
RoboErika5b02322014-05-07 17:05:49 -0700363 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
364 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500365 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700366 if (getContext()
367 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
368 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700369 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
370 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700371 throw new SecurityException("Missing permission to control media.");
372 }
373 }
374
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500375 private boolean isCurrentVolumeController(int uid, int pid) {
376 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
377 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500378 }
379
380 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500381 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700382 throw new SecurityException("Only system ui may " + action);
383 }
384 }
385
RoboErika5b02322014-05-07 17:05:49 -0700386 /**
387 * This checks if the component is an enabled notification listener for the
388 * specified user. Enabled components may only operate on behalf of the user
389 * they're running as.
390 *
391 * @param compName The component that is enabled.
392 * @param userId The user id of the caller.
393 * @param forUserId The user id they're making the request on behalf of.
394 * @return True if the component is enabled, false otherwise
395 */
396 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
397 int forUserId) {
398 if (userId != forUserId) {
399 // You may not access another user's content as an enabled listener.
400 return false;
401 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700402 if (DEBUG) {
403 Log.d(TAG, "Checking if enabled notification listener " + compName);
404 }
RoboErike7880d82014-04-30 12:48:25 -0700405 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700406 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700407 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700408 userId);
RoboErike7880d82014-04-30 12:48:25 -0700409 if (enabledNotifListeners != null) {
410 final String[] components = enabledNotifListeners.split(":");
411 for (int i = 0; i < components.length; i++) {
412 final ComponentName component =
413 ComponentName.unflattenFromString(components[i]);
414 if (component != null) {
415 if (compName.equals(component)) {
416 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900417 Log.d(TAG, "ok to get sessions. " + component +
RoboErike7880d82014-04-30 12:48:25 -0700418 " is authorized notification listener");
419 }
420 return true;
421 }
422 }
423 }
424 }
425 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900426 Log.d(TAG, "not ok to get sessions. " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700427 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700428 }
429 }
430 return false;
431 }
432
RoboErika5b02322014-05-07 17:05:49 -0700433 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700434 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800435 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700436 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800437 }
438 }
439
RoboErik4646d282014-05-13 10:13:04 -0700440 /*
441 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700442 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700443 * 2. It needs to be added to all sessions.
444 * 3. It needs to be added to the priority stack.
445 * 4. It needs to be added to the relevant user record.
446 */
RoboErika5b02322014-05-07 17:05:49 -0700447 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
448 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700449
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700450 UserRecord user = mUserRecords.get(userId);
451 if (user == null) {
452 Log.wtf(TAG, "Request from invalid user: " + userId);
453 throw new RuntimeException("Session request from invalid user.");
454 }
455
RoboErika5b02322014-05-07 17:05:49 -0700456 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
457 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800458 try {
459 cb.asBinder().linkToDeath(session, 0);
460 } catch (RemoteException e) {
461 throw new RuntimeException("Media Session owner died prematurely.", e);
462 }
RoboErik4646d282014-05-13 10:13:04 -0700463
Jaewan Kim8f729082016-06-21 12:36:26 +0900464 mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
RoboErik4646d282014-05-13 10:13:04 -0700465 user.addSessionLocked(session);
466
RoboErik2e7a9162014-06-04 16:53:45 -0700467 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
468
RoboErik01fe6612014-02-13 14:19:04 -0800469 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900470 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800471 }
472 return session;
473 }
474
RoboErik2e7a9162014-06-04 16:53:45 -0700475 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
476 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800477 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700478 return i;
479 }
480 }
481 return -1;
482 }
483
RoboErik2e7a9162014-06-04 16:53:45 -0700484 private void pushSessionsChanged(int userId) {
485 synchronized (mLock) {
486 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
487 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800488 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700489 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700490 }
Jaewan Kimbd16f452017-02-03 16:21:38 +0900491 pushAddressedPlayerChangedLocked();
Jeff Browndba34ba2014-06-24 20:46:03 -0700492 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700493 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700494 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700495 }
RoboErik19c95182014-06-23 15:38:48 -0700496 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700497 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
498 SessionsListenerRecord record = mSessionsListeners.get(i);
499 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
500 try {
501 record.mListener.onActiveSessionsChanged(tokens);
502 } catch (RemoteException e) {
503 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
504 e);
505 mSessionsListeners.remove(i);
506 }
507 }
508 }
509 }
510 }
511
RoboErik19c95182014-06-23 15:38:48 -0700512 private void pushRemoteVolumeUpdateLocked(int userId) {
513 if (mRvc != null) {
514 try {
515 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
516 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
517 } catch (RemoteException e) {
518 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
519 }
520 }
521 }
522
Jaewan Kimbd16f452017-02-03 16:21:38 +0900523 private MediaSessionRecord getMediaButtonSessionLocked() {
524 // If we don't have a media button receiver to fall back on
525 // include non-playing sessions for dispatching.
526 boolean useNotPlayingSessions = true;
527 for (int userId : mCurrentUserIdList) {
528 UserRecord ur = mUserRecords.get(userId);
529 if (ur.mLastMediaButtonReceiver != null
530 || ur.mRestoredMediaButtonReceiver != null) {
531 useNotPlayingSessions = false;
532 break;
533 }
534 }
535 return mPriorityStack.getDefaultMediaButtonSession(
536 mCurrentUserIdList, useNotPlayingSessions);
537 }
538
539 private void pushAddressedPlayerChangedLocked() {
540 if (mCallback == null) {
541 return;
542 }
543 try {
544 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
545 if (mediaButtonSession != null) {
546 mCallback.onAddressedPlayerChangedToMediaSession(
547 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
548 } else {
549 for (int userId : mCurrentUserIdList) {
550 UserRecord user = mUserRecords.get(userId);
551 if (user.mLastMediaButtonReceiver == null
552 && user.mRestoredMediaButtonReceiver == null) {
553 continue;
554 }
555 ComponentName componentName = user.mLastMediaButtonReceiver != null
556 ? user.mLastMediaButtonReceiver.getIntent().getComponent()
557 : user.mRestoredMediaButtonReceiver;
558 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(componentName);
559 return;
560 }
561 }
562 } catch (RemoteException e) {
563 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
564 }
565 }
566
567 // Remember media button receiver and keep it in the persistent storage.
568 // This should be called whenever there's no media session to receive media button event.
RoboErikb214efb2014-07-24 13:20:30 -0700569 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
570 PendingIntent receiver = record.getMediaButtonReceiver();
571 UserRecord user = mUserRecords.get(record.getUserId());
572 if (receiver != null && user != null) {
573 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800574 ComponentName component = receiver.getIntent().getComponent();
575 if (component != null && record.getPackageName().equals(component.getPackageName())) {
576 Settings.Secure.putStringForUser(mContentResolver,
577 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
578 record.getUserId());
579 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700580 }
581 }
582
Jaewan Kim50269362016-12-23 11:22:02 +0900583 private String getCallingPackageName(int uid) {
584 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
585 if (packages != null && packages.length > 0) {
586 return packages[0];
587 }
588 return "";
589 }
590
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900591 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
592 // Only consider full user.
593 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
594 try {
595 user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
596 } catch (RemoteException e) {
597 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
598 }
599 }
600
RoboErik4646d282014-05-13 10:13:04 -0700601 /**
602 * Information about a particular user. The contents of this object is
603 * guarded by mLock.
604 */
605 final class UserRecord {
606 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700607 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800608 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700609 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800610 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700611
Jaewan Kim50269362016-12-23 11:22:02 +0900612 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
613 private int mOnVolumeKeyLongPressListenerUid;
614 private KeyEvent mInitialDownVolumeKeyEvent;
615 private int mInitialDownVolumeStream;
616 private boolean mInitialDownMusicOnly;
617
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800618 private IOnMediaKeyListener mOnMediaKeyListener;
619 private int mOnMediaKeyListenerUid;
620
RoboErik4646d282014-05-13 10:13:04 -0700621 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800622 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700623 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800624 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700625 }
626
RoboErik4646d282014-05-13 10:13:04 -0700627 public void destroyLocked() {
628 for (int i = mSessions.size() - 1; i >= 0; i--) {
629 MediaSessionRecord session = mSessions.get(i);
630 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700631 }
632 }
633
RoboErik4646d282014-05-13 10:13:04 -0700634 public ArrayList<MediaSessionRecord> getSessionsLocked() {
635 return mSessions;
636 }
637
638 public void addSessionLocked(MediaSessionRecord session) {
639 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700640 }
641
642 public void removeSessionLocked(MediaSessionRecord session) {
643 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700644 }
645
646 public void dumpLocked(PrintWriter pw, String prefix) {
647 pw.println(prefix + "Record for user " + mUserId);
648 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700649 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800650 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jaewan Kim50269362016-12-23 11:22:02 +0900651 pw.println(indent + "Volume key long-press listener:" + mOnVolumeKeyLongPressListener);
652 pw.println(indent + "Volume key long-press listener package:" +
653 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800654 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
655 pw.println(indent + "Media key listener package: " +
656 getCallingPackageName(mOnMediaKeyListenerUid));
Jeff Brown01a500e2014-07-10 22:50:50 -0700657 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700658 pw.println(indent + size + " Sessions:");
659 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700660 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700661 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700662 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700663 }
664 }
RoboErikc8f92d12015-01-05 16:48:07 -0800665
666 private void restoreMediaButtonReceiver() {
667 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
Jaewan Kim8f729082016-06-21 12:36:26 +0900668 Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
RoboErikc8f92d12015-01-05 16:48:07 -0800669 if (!TextUtils.isEmpty(receiverName)) {
670 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
671 if (eventReceiver == null) {
672 // an invalid name was persisted
673 return;
674 }
675 mRestoredMediaButtonReceiver = eventReceiver;
676 }
677 }
RoboErik4646d282014-05-13 10:13:04 -0700678 }
679
RoboErik2e7a9162014-06-04 16:53:45 -0700680 final class SessionsListenerRecord implements IBinder.DeathRecipient {
681 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700682 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700683 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700684 private final int mPid;
685 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700686
RoboErik7aef77b2014-08-08 15:56:54 -0700687 public SessionsListenerRecord(IActiveSessionsListener listener,
688 ComponentName componentName,
689 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700690 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700691 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700692 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700693 mPid = pid;
694 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700695 }
696
697 @Override
698 public void binderDied() {
699 synchronized (mLock) {
700 mSessionsListeners.remove(this);
701 }
702 }
703 }
704
RoboErik7aef77b2014-08-08 15:56:54 -0700705 final class SettingsObserver extends ContentObserver {
706 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
707 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
708
709 private SettingsObserver() {
710 super(null);
711 }
712
713 private void observe() {
714 mContentResolver.registerContentObserver(mSecureSettingsUri,
715 false, this, UserHandle.USER_ALL);
716 }
717
718 @Override
719 public void onChange(boolean selfChange, Uri uri) {
720 updateActiveSessionListeners();
721 }
722 }
723
RoboErik07c70772014-03-20 13:33:52 -0700724 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700725 private static final String EXTRA_WAKELOCK_ACQUIRED =
726 "android.media.AudioService.WAKELOCK_ACQUIRED";
727 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
728
RoboErik9a9d0b52014-05-20 14:53:39 -0700729 private boolean mVoiceButtonDown = false;
730 private boolean mVoiceButtonHandled = false;
731
RoboErik07c70772014-03-20 13:33:52 -0700732 @Override
RoboErika5b02322014-05-07 17:05:49 -0700733 public ISession createSession(String packageName, ISessionCallback cb, String tag,
734 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800735 final int pid = Binder.getCallingPid();
736 final int uid = Binder.getCallingUid();
737 final long token = Binder.clearCallingIdentity();
738 try {
739 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700740 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
741 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800742 if (cb == null) {
743 throw new IllegalArgumentException("Controller callback cannot be null");
744 }
RoboErika5b02322014-05-07 17:05:49 -0700745 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
746 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700747 } finally {
748 Binder.restoreCallingIdentity(token);
749 }
750 }
751
752 @Override
RoboErika5b02322014-05-07 17:05:49 -0700753 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700754 final int pid = Binder.getCallingPid();
755 final int uid = Binder.getCallingUid();
756 final long token = Binder.clearCallingIdentity();
757
758 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700759 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700760 ArrayList<IBinder> binders = new ArrayList<IBinder>();
761 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700762 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700763 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700764 int size = records.size();
765 for (int i = 0; i < size; i++) {
766 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700767 }
768 }
769 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800770 } finally {
771 Binder.restoreCallingIdentity(token);
772 }
773 }
RoboErika278ea72014-04-24 14:49:01 -0700774
RoboErik2e7a9162014-06-04 16:53:45 -0700775 @Override
776 public void addSessionsListener(IActiveSessionsListener listener,
777 ComponentName componentName, int userId) throws RemoteException {
778 final int pid = Binder.getCallingPid();
779 final int uid = Binder.getCallingUid();
780 final long token = Binder.clearCallingIdentity();
781
782 try {
783 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
784 synchronized (mLock) {
785 int index = findIndexOfSessionsListenerLocked(listener);
786 if (index != -1) {
787 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
788 return;
789 }
790 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700791 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700792 try {
793 listener.asBinder().linkToDeath(record, 0);
794 } catch (RemoteException e) {
795 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
796 return;
797 }
798 mSessionsListeners.add(record);
799 }
800 } finally {
801 Binder.restoreCallingIdentity(token);
802 }
803 }
804
805 @Override
806 public void removeSessionsListener(IActiveSessionsListener listener)
807 throws RemoteException {
808 synchronized (mLock) {
809 int index = findIndexOfSessionsListenerLocked(listener);
810 if (index != -1) {
811 SessionsListenerRecord record = mSessionsListeners.remove(index);
812 try {
813 record.mListener.asBinder().unlinkToDeath(record, 0);
814 } catch (Exception e) {
815 // ignore exceptions, the record is being removed
816 }
817 }
818 }
819 }
820
RoboErik8a2cfc32014-05-16 11:19:38 -0700821 /**
822 * Handles the dispatching of the media button events to one of the
823 * registered listeners, or if there was none, broadcast an
824 * ACTION_MEDIA_BUTTON intent to the rest of the system.
825 *
826 * @param keyEvent a non-null KeyEvent whose key code is one of the
827 * supported media buttons
828 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
829 * while this key event is dispatched.
830 */
831 @Override
832 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
833 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
834 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
835 return;
836 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700837
RoboErik8a2cfc32014-05-16 11:19:38 -0700838 final int pid = Binder.getCallingPid();
839 final int uid = Binder.getCallingUid();
840 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700841 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700842 if (DEBUG) {
843 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
844 + keyEvent);
845 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700846 if (!isUserSetupComplete()) {
847 // Global media key handling can have the side-effect of starting new
848 // activities which is undesirable while setup is in progress.
849 Slog.i(TAG, "Not dispatching media key event because user "
850 + "setup is in progress.");
851 return;
852 }
853
RoboErik8a2cfc32014-05-16 11:19:38 -0700854 synchronized (mLock) {
Jaewan Kim51255012017-02-24 16:19:14 +0900855 boolean isGlobalPriorityActive = mPriorityStack.isGlobalPriorityActive();
856 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
857 // Prevent dispatching key event through reflection while the global
858 // priority session is active.
859 Slog.i(TAG, "Only the system can dispatch media key event "
860 + "to the global priority session.");
861 return;
862 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900863 if (!isGlobalPriorityActive) {
864 // Only consider full user.
865 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
866 if (user.mOnMediaKeyListener != null) {
867 if (DEBUG_KEY_EVENT) {
868 Log.d(TAG, "Send " + keyEvent + " to media key listener");
869 }
870 try {
871 user.mOnMediaKeyListener.onMediaKey(keyEvent,
872 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
873 return;
874 } catch (RemoteException e) {
875 Log.w(TAG, "Failed to send " + keyEvent + " to media key listener");
876 }
877 }
878 }
Jaewan Kim51255012017-02-24 16:19:14 +0900879 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800880 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700881 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +0900882 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700883 }
884 }
885 } finally {
886 Binder.restoreCallingIdentity(token);
887 }
888 }
889
RoboErika278ea72014-04-24 14:49:01 -0700890 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +0900891 public void setCallback(ICallback callback) {
892 final int pid = Binder.getCallingPid();
893 final int uid = Binder.getCallingUid();
894 final long token = Binder.clearCallingIdentity();
895 try {
896 if (uid != Process.BLUETOOTH_UID) {
897 throw new SecurityException("Only Bluetooth service processes can set"
898 + " Callback");
899 }
900 synchronized (mLock) {
901 Log.d(TAG, "Callback + " + mCallback
902 + " is set by " + getCallingPackageName(uid));
903 mCallback = callback;
904 if (mCallback == null) {
905 return;
906 }
907 try {
908 mCallback.asBinder().linkToDeath(
909 new IBinder.DeathRecipient() {
910 @Override
911 public void binderDied() {
912 synchronized (mLock) {
913 mCallback = null;
914 }
915 }
916 }, 0);
917 pushAddressedPlayerChangedLocked();
918 } catch (RemoteException e) {
919 Log.w(TAG, "Failed to set callback", e);
920 mCallback = null;
921 }
922 }
923 } finally {
924 Binder.restoreCallingIdentity(token);
925 }
926 }
927
928 @Override
Jaewan Kim50269362016-12-23 11:22:02 +0900929 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
930 final int pid = Binder.getCallingPid();
931 final int uid = Binder.getCallingUid();
932 final long token = Binder.clearCallingIdentity();
933 try {
934 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
935 if (getContext().checkPermission(
936 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
937 != PackageManager.PERMISSION_GRANTED) {
938 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
939 " permission.");
940 }
941
942 synchronized (mLock) {
943 UserRecord user = mUserRecords.get(UserHandle.getUserId(uid));
944 if (user.mOnVolumeKeyLongPressListener != null &&
945 user.mOnVolumeKeyLongPressListenerUid != uid) {
946 Log.w(TAG, "Volume key long-press listener cannot be reset by another app");
947 return;
948 }
949
950 user.mOnVolumeKeyLongPressListener = listener;
951 user.mOnVolumeKeyLongPressListenerUid = uid;
952
953 Log.d(TAG, "Volume key long-press listener "
954 + listener + " is set by " + getCallingPackageName(uid));
955
956 if (user.mOnVolumeKeyLongPressListener != null) {
957 try {
958 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
959 new IBinder.DeathRecipient() {
960 @Override
961 public void binderDied() {
962 synchronized (mLock) {
963 user.mOnVolumeKeyLongPressListener = null;
964 }
965 }
966 }, 0);
967 } catch (RemoteException e) {
968 Log.w(TAG, "Failed to set death recipient "
969 + user.mOnVolumeKeyLongPressListener);
970 user.mOnVolumeKeyLongPressListener = null;
971 }
972 }
973 }
974 } finally {
975 Binder.restoreCallingIdentity(token);
976 }
977 }
978
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800979 @Override
980 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
981 final int pid = Binder.getCallingPid();
982 final int uid = Binder.getCallingUid();
983 final long token = Binder.clearCallingIdentity();
984 try {
985 // Enforce SET_MEDIA_KEY_LISTENER permission.
986 if (getContext().checkPermission(
987 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
988 != PackageManager.PERMISSION_GRANTED) {
989 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
990 " permission.");
991 }
992
993 synchronized (mLock) {
994 int userId = UserHandle.getUserId(uid);
995 UserRecord user = mUserRecords.get(userId);
996 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
997 Log.w(TAG, "Media key listener cannot be reset by another app");
998 return;
999 }
1000
1001 user.mOnMediaKeyListener = listener;
1002 user.mOnMediaKeyListenerUid = uid;
1003
1004 Log.d(TAG, "Media key listener " + user.mOnMediaKeyListener
1005 + " is set by " + getCallingPackageName(uid));
1006
1007 if (user.mOnMediaKeyListener != null) {
1008 try {
1009 user.mOnMediaKeyListener.asBinder().linkToDeath(
1010 new IBinder.DeathRecipient() {
1011 @Override
1012 public void binderDied() {
1013 synchronized (mLock) {
1014 user.mOnMediaKeyListener = null;
1015 }
1016 }
1017 }, 0);
1018 } catch (RemoteException e) {
1019 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1020 user.mOnMediaKeyListener = null;
1021 }
1022 }
1023 }
1024 } finally {
1025 Binder.restoreCallingIdentity(token);
1026 }
1027 }
1028
Jaewan Kim50269362016-12-23 11:22:02 +09001029 /**
1030 * Handles the dispatching of the volume button events to one of the
1031 * registered listeners. If there's a volume key long-press listener and
1032 * there's no active global priority session, long-pressess will be sent to the
1033 * long-press listener instead of adjusting volume.
1034 *
1035 * @param keyEvent a non-null KeyEvent whose key code is one of the
1036 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1037 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1038 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1039 * @param stream stream type to adjust volume.
1040 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1041 */
1042 @Override
1043 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1044 if (keyEvent == null ||
1045 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1046 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1047 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1048 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1049 return;
1050 }
1051
1052 final int pid = Binder.getCallingPid();
1053 final int uid = Binder.getCallingUid();
1054 final long token = Binder.clearCallingIdentity();
1055
Jaewan Kimb2781e72017-03-02 09:57:09 +09001056 if (DEBUG_KEY_EVENT) {
Jaewan Kim50269362016-12-23 11:22:02 +09001057 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1058 + keyEvent);
1059 }
1060
1061 try {
1062 synchronized (mLock) {
1063 // Only consider full user.
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001064 int userId = mCurrentUserIdList.get(0);
1065 UserRecord user = mUserRecords.get(userId);
Jaewan Kim50269362016-12-23 11:22:02 +09001066
1067 if (mPriorityStack.isGlobalPriorityActive()
1068 || user.mOnVolumeKeyLongPressListener == null) {
1069 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1070 } else {
1071 // TODO: Consider the case when both volume up and down keys are pressed
1072 // at the same time.
1073 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1074 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001075 // Keeps the copy of the KeyEvent because it can be reused.
1076 user.mInitialDownVolumeKeyEvent = KeyEvent.obtain(keyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001077 user.mInitialDownVolumeStream = stream;
1078 user.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001079 mHandler.sendMessageDelayed(
1080 mHandler.obtainMessage(
1081 MessageHandler.MSG_VOLUME_INITIAL_DOWN, userId, 0),
1082 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001083 }
1084 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001085 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kim50269362016-12-23 11:22:02 +09001086 if (user.mInitialDownVolumeKeyEvent != null) {
1087 dispatchVolumeKeyLongPressLocked(
1088 user.mInitialDownVolumeKeyEvent);
1089 // Mark that the key is already handled.
1090 user.mInitialDownVolumeKeyEvent = null;
1091 }
1092 dispatchVolumeKeyLongPressLocked(keyEvent);
1093 }
1094 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001095 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kim50269362016-12-23 11:22:02 +09001096 if (user.mInitialDownVolumeKeyEvent != null
1097 && user.mInitialDownVolumeKeyEvent.getDownTime()
1098 == keyEvent.getDownTime()) {
1099 // Short-press. Should change volume.
1100 dispatchVolumeKeyEventLocked(
1101 user.mInitialDownVolumeKeyEvent,
1102 user.mInitialDownVolumeStream,
1103 user.mInitialDownMusicOnly);
1104 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1105 } else {
1106 dispatchVolumeKeyLongPressLocked(keyEvent);
1107 }
1108 }
1109 }
1110 }
1111 } finally {
1112 Binder.restoreCallingIdentity(token);
1113 }
1114 }
1115
Jaewan Kim50269362016-12-23 11:22:02 +09001116 private void dispatchVolumeKeyEventLocked(
1117 KeyEvent keyEvent, int stream, boolean musicOnly) {
1118 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1119 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1120 int direction = 0;
1121 boolean isMute = false;
1122 switch (keyEvent.getKeyCode()) {
1123 case KeyEvent.KEYCODE_VOLUME_UP:
1124 direction = AudioManager.ADJUST_RAISE;
1125 break;
1126 case KeyEvent.KEYCODE_VOLUME_DOWN:
1127 direction = AudioManager.ADJUST_LOWER;
1128 break;
1129 case KeyEvent.KEYCODE_VOLUME_MUTE:
1130 isMute = true;
1131 break;
1132 }
1133 if (down || up) {
1134 int flags = AudioManager.FLAG_FROM_KEY;
1135 if (musicOnly) {
1136 // This flag is used when the screen is off to only affect active media.
1137 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1138 } else {
1139 // These flags are consistent with the home screen
1140 if (up) {
1141 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1142 } else {
1143 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1144 }
1145 }
1146 if (direction != 0) {
1147 // If this is action up we want to send a beep for non-music events
1148 if (up) {
1149 direction = 0;
1150 }
1151 dispatchAdjustVolumeLocked(stream, direction, flags);
1152 } else if (isMute) {
1153 if (down && keyEvent.getRepeatCount() == 0) {
1154 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1155 }
1156 }
1157 }
1158 }
1159
1160 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001161 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001162 final long token = Binder.clearCallingIdentity();
1163 try {
1164 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001165 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001166 }
1167 } finally {
1168 Binder.restoreCallingIdentity(token);
1169 }
1170 }
1171
1172 @Override
RoboErik19c95182014-06-23 15:38:48 -07001173 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1174 final int pid = Binder.getCallingPid();
1175 final int uid = Binder.getCallingUid();
1176 final long token = Binder.clearCallingIdentity();
1177 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001178 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001179 mRvc = rvc;
1180 } finally {
1181 Binder.restoreCallingIdentity(token);
1182 }
1183 }
1184
1185 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001186 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001187 synchronized (mLock) {
1188 return mPriorityStack.isGlobalPriorityActive();
1189 }
RoboErikde9ba392014-09-26 12:51:01 -07001190 }
1191
1192 @Override
RoboErika278ea72014-04-24 14:49:01 -07001193 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1194 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
1195 != PackageManager.PERMISSION_GRANTED) {
1196 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
1197 + Binder.getCallingPid()
1198 + ", uid=" + Binder.getCallingUid());
1199 return;
1200 }
1201
1202 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1203 pw.println();
1204
1205 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001206 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErika5b02322014-05-07 17:05:49 -07001207 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -07001208
RoboErik4646d282014-05-13 10:13:04 -07001209 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001210 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001211 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -08001212 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -07001213 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001214 }
1215 }
1216 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001217
RoboErik2e7a9162014-06-04 16:53:45 -07001218 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1219 final int uid) {
1220 String packageName = null;
1221 if (componentName != null) {
1222 // If they gave us a component name verify they own the
1223 // package
1224 packageName = componentName.getPackageName();
1225 enforcePackageName(packageName, uid);
1226 }
1227 // Check that they can make calls on behalf of the user and
1228 // get the final user id
1229 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1230 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1231 // Check if they have the permissions or their component is
1232 // enabled for the user they're calling from.
1233 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1234 return resolvedUserId;
1235 }
1236
Jaewan Kim50269362016-12-23 11:22:02 +09001237 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
1238 MediaSessionRecord session = mPriorityStack.getDefaultVolumeSession(mCurrentUserIdList);
1239
RoboErik9c785402014-11-11 16:52:26 -08001240 boolean preferSuggestedStream = false;
1241 if (isValidLocalStreamType(suggestedStream)
1242 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1243 preferSuggestedStream = true;
1244 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001245 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001246 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1247 + flags + ", suggestedStream=" + suggestedStream
1248 + ", preferSuggestedStream=" + preferSuggestedStream);
1249 }
RoboErik9c785402014-11-11 16:52:26 -08001250 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001251 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1252 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001253 if (DEBUG) {
1254 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001255 }
RoboErikb7c014c2014-07-22 15:58:22 -07001256 return;
RoboErik3c45c292014-07-08 16:47:31 -07001257 }
Shibin George19e84042016-06-14 20:42:13 +05301258
1259 // Execute mAudioService.adjustSuggestedStreamVolume() on
1260 // handler thread of MediaSessionService.
1261 // This will release the MediaSessionService.mLock sooner and avoid
1262 // a potential deadlock between MediaSessionService.mLock and
1263 // ActivityManagerService lock.
1264 mHandler.post(new Runnable() {
1265 @Override
1266 public void run() {
1267 try {
1268 String packageName = getContext().getOpPackageName();
1269 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1270 flags, packageName, TAG);
1271 } catch (RemoteException e) {
1272 Log.e(TAG, "Error adjusting default volume.", e);
1273 }
1274 }
1275 });
RoboErikb69ffd42014-05-30 14:57:59 -07001276 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001277 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001278 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001279 }
1280 }
1281
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001282 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001283 int action = keyEvent.getAction();
1284 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1285 if (action == KeyEvent.ACTION_DOWN) {
1286 if (keyEvent.getRepeatCount() == 0) {
1287 mVoiceButtonDown = true;
1288 mVoiceButtonHandled = false;
1289 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1290 mVoiceButtonHandled = true;
1291 startVoiceInput(needWakeLock);
1292 }
1293 } else if (action == KeyEvent.ACTION_UP) {
1294 if (mVoiceButtonDown) {
1295 mVoiceButtonDown = false;
1296 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1297 // Resend the down then send this event through
1298 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001299 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1300 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001301 }
1302 }
1303 }
1304 }
1305
Jaewan Kim98003d32017-02-24 18:33:04 +09001306 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001307 MediaSessionRecord session = getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001308 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001309 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001310 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001311 }
1312 if (needWakeLock) {
1313 mKeyEventReceiver.aquireWakeLockLocked();
1314 }
Jaewan Kim50269362016-12-23 11:22:02 +09001315 // 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 -07001316 session.sendMediaButton(keyEvent,
1317 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001318 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001319 getContext().getPackageName());
Jaewan Kimbd16f452017-02-03 16:21:38 +09001320 if (mCallback != null) {
1321 try {
1322 mCallback.onMediaKeyEventDispatchedToMediaSession(keyEvent,
1323 new MediaSession.Token(session.getControllerBinder()));
1324 } catch (RemoteException e) {
1325 Log.w(TAG, "Failed to send callback", e);
1326 }
1327 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001328 } else {
RoboErikb214efb2014-07-24 13:20:30 -07001329 // Launch the last PendingIntent we had with priority
Jaewan Kim8f729082016-06-21 12:36:26 +09001330 for (int userId : mCurrentUserIdList) {
1331 UserRecord user = mUserRecords.get(userId);
1332 if (user.mLastMediaButtonReceiver == null
1333 && user.mRestoredMediaButtonReceiver == null) {
1334 continue;
1335 }
RoboErikb214efb2014-07-24 13:20:30 -07001336 if (needWakeLock) {
1337 mKeyEventReceiver.aquireWakeLockLocked();
1338 }
1339 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Insun Kang2054db32016-04-07 15:34:34 +09001340 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RoboErikb214efb2014-07-24 13:20:30 -07001341 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1342 try {
RoboErikc8f92d12015-01-05 16:48:07 -08001343 if (user.mLastMediaButtonReceiver != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001344 PendingIntent receiver = user.mLastMediaButtonReceiver;
Jaewan Kim50269362016-12-23 11:22:02 +09001345 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001346 Log.d(TAG, "Sending " + keyEvent
Jaewan Kimbd16f452017-02-03 16:21:38 +09001347 + " to the last known pendingIntent " + receiver);
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001348 }
Jaewan Kimbd16f452017-02-03 16:21:38 +09001349 receiver.send(getContext(),
RoboErikc8f92d12015-01-05 16:48:07 -08001350 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
riddle_hsu02ed0122015-10-20 16:00:15 +08001351 mediaButtonIntent, mKeyEventReceiver, mHandler);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001352 if (mCallback != null) {
1353 ComponentName componentName =
1354 user.mLastMediaButtonReceiver.getIntent().getComponent();
1355 if (componentName != null) {
1356 mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver(
1357 keyEvent, componentName);
1358 }
1359 }
RoboErikc8f92d12015-01-05 16:48:07 -08001360 } else {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001361 ComponentName receiver = user.mRestoredMediaButtonReceiver;
Jaewan Kim50269362016-12-23 11:22:02 +09001362 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001363 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
Jaewan Kimbd16f452017-02-03 16:21:38 +09001364 + receiver);
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001365 }
Jaewan Kimbd16f452017-02-03 16:21:38 +09001366 mediaButtonIntent.setComponent(receiver);
RoboErikc8f92d12015-01-05 16:48:07 -08001367 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim8f729082016-06-21 12:36:26 +09001368 UserHandle.of(userId));
Jaewan Kimbd16f452017-02-03 16:21:38 +09001369 if (mCallback != null) {
1370 mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver(
1371 keyEvent, receiver);
1372 }
RoboErikc8f92d12015-01-05 16:48:07 -08001373 }
RoboErikb214efb2014-07-24 13:20:30 -07001374 } catch (CanceledException e) {
1375 Log.i(TAG, "Error sending key event to media button receiver "
1376 + user.mLastMediaButtonReceiver, e);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001377 } catch (RemoteException e) {
1378 Log.w(TAG, "Failed to send callback", e);
RoboErikb214efb2014-07-24 13:20:30 -07001379 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001380 return;
RoboErik9a9d0b52014-05-20 14:53:39 -07001381 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001382 if (DEBUG) {
1383 Log.d(TAG, "Sending media key ordered broadcast");
1384 }
1385 if (needWakeLock) {
1386 mMediaEventWakeLock.acquire();
1387 }
1388 // Fallback to legacy behavior
1389 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1390 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1391 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1392 if (needWakeLock) {
1393 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1394 }
1395 // Send broadcast only to the full user.
1396 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1397 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001398 }
1399 }
1400
1401 private void startVoiceInput(boolean needWakeLock) {
1402 Intent voiceIntent = null;
1403 // select which type of search to launch:
1404 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1405 // - device locked or screen off: action is
1406 // ACTION_VOICE_SEARCH_HANDS_FREE
1407 // with EXTRA_SECURE set to true if the device is securely locked
1408 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1409 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1410 if (!isLocked && pm.isScreenOn()) {
1411 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1412 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1413 } else {
1414 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1415 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1416 isLocked && mKeyguardManager.isKeyguardSecure());
1417 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1418 }
1419 // start the search activity
1420 if (needWakeLock) {
1421 mMediaEventWakeLock.acquire();
1422 }
1423 try {
1424 if (voiceIntent != null) {
1425 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1426 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001427 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001428 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1429 }
1430 } catch (ActivityNotFoundException e) {
1431 Log.w(TAG, "No activity for search: " + e);
1432 } finally {
1433 if (needWakeLock) {
1434 mMediaEventWakeLock.release();
1435 }
1436 }
1437 }
1438
1439 private boolean isVoiceKey(int keyCode) {
1440 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1441 }
1442
Jeff Brown38d3feb2015-03-19 18:26:30 -07001443 private boolean isUserSetupComplete() {
1444 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1445 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1446 }
1447
RoboErik9c785402014-11-11 16:52:26 -08001448 // we only handle public stream types, which are 0-5
1449 private boolean isValidLocalStreamType(int streamType) {
1450 return streamType >= AudioManager.STREAM_VOICE_CALL
1451 && streamType <= AudioManager.STREAM_NOTIFICATION;
1452 }
1453
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001454 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1455 private KeyEvent mKeyEvent;
1456 private boolean mNeedWakeLock;
1457 private boolean mHandled;
1458
1459 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1460 super(mHandler);
1461 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1462 mKeyEvent = keyEvent;
1463 mNeedWakeLock = needWakeLock;
1464 }
1465
1466 @Override
1467 public void run() {
1468 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1469 dispatchMediaKeyEvent();
1470 }
1471
1472 @Override
1473 protected void onReceiveResult(int resultCode, Bundle resultData) {
1474 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1475 mHandled = true;
1476 mHandler.removeCallbacks(this);
1477 return;
1478 }
1479 dispatchMediaKeyEvent();
1480 }
1481
1482 private void dispatchMediaKeyEvent() {
1483 if (mHandled) {
1484 return;
1485 }
1486 mHandled = true;
1487 mHandler.removeCallbacks(this);
1488 synchronized (mLock) {
Jaewan Kim98003d32017-02-24 18:33:04 +09001489 if (!mPriorityStack.isGlobalPriorityActive()
1490 && isVoiceKey(mKeyEvent.getKeyCode())) {
1491 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1492 } else {
1493 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1494 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001495 }
1496 }
1497 }
1498
RoboErik418c10c2014-05-19 09:25:25 -07001499 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1500
RoboErikb214efb2014-07-24 13:20:30 -07001501 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1502 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001503 private final Handler mHandler;
1504 private int mRefCount = 0;
1505 private int mLastTimeoutId = 0;
1506
1507 public KeyEventWakeLockReceiver(Handler handler) {
1508 super(handler);
1509 mHandler = handler;
1510 }
1511
1512 public void onTimeout() {
1513 synchronized (mLock) {
1514 if (mRefCount == 0) {
1515 // We've already released it, so just return
1516 return;
1517 }
1518 mLastTimeoutId++;
1519 mRefCount = 0;
1520 releaseWakeLockLocked();
1521 }
1522 }
1523
1524 public void aquireWakeLockLocked() {
1525 if (mRefCount == 0) {
1526 mMediaEventWakeLock.acquire();
1527 }
1528 mRefCount++;
1529 mHandler.removeCallbacks(this);
1530 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1531
1532 }
1533
1534 @Override
1535 public void run() {
1536 onTimeout();
1537 }
1538
RoboErik8a2cfc32014-05-16 11:19:38 -07001539 @Override
1540 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001541 if (resultCode < mLastTimeoutId) {
1542 // Ignore results from calls that were before the last
1543 // timeout, just in case.
1544 return;
1545 } else {
1546 synchronized (mLock) {
1547 if (mRefCount > 0) {
1548 mRefCount--;
1549 if (mRefCount == 0) {
1550 releaseWakeLockLocked();
1551 }
1552 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001553 }
1554 }
1555 }
RoboErik418c10c2014-05-19 09:25:25 -07001556
1557 private void releaseWakeLockLocked() {
1558 mMediaEventWakeLock.release();
1559 mHandler.removeCallbacks(this);
1560 }
RoboErikb214efb2014-07-24 13:20:30 -07001561
1562 @Override
1563 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1564 String resultData, Bundle resultExtras) {
1565 onReceiveResult(resultCode, null);
1566 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001567 };
1568
1569 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1570 @Override
1571 public void onReceive(Context context, Intent intent) {
1572 if (intent == null) {
1573 return;
1574 }
1575 Bundle extras = intent.getExtras();
1576 if (extras == null) {
1577 return;
1578 }
1579 synchronized (mLock) {
1580 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1581 && mMediaEventWakeLock.isHeld()) {
1582 mMediaEventWakeLock.release();
1583 }
1584 }
1585 }
1586 };
RoboErik01fe6612014-02-13 14:19:04 -08001587 }
1588
RoboErik2e7a9162014-06-04 16:53:45 -07001589 final class MessageHandler extends Handler {
1590 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001591 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
RoboErik2e7a9162014-06-04 16:53:45 -07001592
1593 @Override
1594 public void handleMessage(Message msg) {
1595 switch (msg.what) {
1596 case MSG_SESSIONS_CHANGED:
1597 pushSessionsChanged(msg.arg1);
1598 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001599 case MSG_VOLUME_INITIAL_DOWN:
1600 synchronized (mLock) {
1601 UserRecord user = mUserRecords.get((int) msg.arg1);
1602 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1603 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1604 // Mark that the key is already handled.
1605 user.mInitialDownVolumeKeyEvent = null;
1606 }
1607 }
1608 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001609 }
1610 }
1611
1612 public void post(int what, int arg1, int arg2) {
1613 obtainMessage(what, arg1, arg2).sendToTarget();
1614 }
1615 }
RoboErik01fe6612014-02-13 14:19:04 -08001616}