blob: 9ab685d508c4a3f584bd729050121be088a540fe [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 Kim6e2b01c2017-01-19 16:33:14 -080040import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090041import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070042import android.media.session.ISession;
43import android.media.session.ISessionCallback;
44import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070045import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080046import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070047import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080048import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070049import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080050import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070051import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070052import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070053import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090054import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080055import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070056import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070057import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070058import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090059import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070060import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070061import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080062import android.text.TextUtils;
63import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070064import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070065import android.util.SparseArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070066import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090067import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080068
John Spurlockeb69e242015-02-17 17:15:04 -050069import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080070import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070071import com.android.server.Watchdog;
72import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080073
RoboErika278ea72014-04-24 14:49:01 -070074import java.io.FileDescriptor;
75import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080076import java.util.ArrayList;
Jaewan Kim8f729082016-06-21 12:36:26 +090077import java.util.Arrays;
RoboErike7880d82014-04-30 12:48:25 -070078import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080079
80/**
81 * System implementation of MediaSessionManager
82 */
RoboErika278ea72014-04-24 14:49:01 -070083public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080084 private static final String TAG = "MediaSessionService";
85 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090086 // Leave log for key event always.
87 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080088
RoboErik418c10c2014-05-19 09:25:25 -070089 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080090 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070091
RoboErik2610d712015-01-07 11:10:23 -080092 /* package */final IBinder mICallback = new Binder();
93
RoboErik01fe6612014-02-13 14:19:04 -080094 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070095 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080096
RoboErik4646d282014-05-13 10:13:04 -070097 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070098 private final ArrayList<SessionsListenerRecord> mSessionsListeners
99 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800100 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700101 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700102 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900103 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800104
RoboErik9a9d0b52014-05-20 14:53:39 -0700105 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700106 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -0500107 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700108 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700109 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -0700110
Jaewan Kim8f729082016-06-21 12:36:26 +0900111 // List of user IDs running in the foreground.
112 // Multiple users can be in the foreground if the work profile is on.
113 private final List<Integer> mCurrentUserIdList = new ArrayList<>();
RoboErike7880d82014-04-30 12:48:25 -0700114
RoboErik19c95182014-06-23 15:38:48 -0700115 // Used to notify system UI when remote volume was changed. TODO find a
116 // better way to handle this.
117 private IRemoteVolumeController mRvc;
118
RoboErik01fe6612014-02-13 14:19:04 -0800119 public MediaSessionService(Context context) {
120 super(context);
121 mSessionManagerImpl = new SessionManagerImpl();
RoboErika8f95142014-05-05 14:23:49 -0700122 mPriorityStack = new MediaSessionStack();
RoboErik8a2cfc32014-05-16 11:19:38 -0700123 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
124 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900125 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
RoboErik01fe6612014-02-13 14:19:04 -0800126 }
127
128 @Override
129 public void onStart() {
130 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700131 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700132 mKeyguardManager =
133 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700134 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500135 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700136 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700137 mSettingsObserver = new SettingsObserver();
138 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800139
140 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700141 }
142
143 private IAudioService getAudioService() {
144 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
145 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700146 }
147
RoboErika8f95142014-05-05 14:23:49 -0700148 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700149 synchronized (mLock) {
Jaewan Kime0ca3f32017-02-16 15:52:39 +0900150 UserRecord user = mUserRecords.get(record.getUserId());
151 if (user == null || !user.mSessions.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700152 Log.d(TAG, "Unknown session updated. Ignoring.");
153 return;
154 }
RoboErika8f95142014-05-05 14:23:49 -0700155 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700156 }
RoboErik2e7a9162014-06-04 16:53:45 -0700157 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700158 }
159
RoboErik9c5b7cb2015-01-15 15:09:09 -0800160 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900161 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800162 */
163 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900164 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800165 return;
166 }
167 try {
168 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
169 } catch (Exception e) {
170 Log.wtf(TAG, "Error sending volume change to system UI.", e);
171 }
172 }
173
RoboErika8f95142014-05-05 14:23:49 -0700174 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700175 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700176 synchronized (mLock) {
Jaewan Kime0ca3f32017-02-16 15:52:39 +0900177 UserRecord user = mUserRecords.get(record.getUserId());
178 if (user == null || !user.mSessions.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700179 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
180 return;
181 }
RoboErik2e7a9162014-06-04 16:53:45 -0700182 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
183 }
184 if (updateSessions) {
185 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700186 }
187 }
188
RoboErik19c95182014-06-23 15:38:48 -0700189 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
190 synchronized (mLock) {
Jaewan Kime0ca3f32017-02-16 15:52:39 +0900191 UserRecord user = mUserRecords.get(record.getUserId());
192 if (user == null || !user.mSessions.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700193 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
194 return;
195 }
196 pushRemoteVolumeUpdateLocked(record.getUserId());
197 }
198 }
199
RoboErika278ea72014-04-24 14:49:01 -0700200 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900201 public void onStartUser(int userId) {
202 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700203 updateUser();
204 }
205
206 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900207 public void onSwitchUser(int userId) {
208 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700209 updateUser();
210 }
211
212 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900213 public void onStopUser(int userId) {
214 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700215 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900216 UserRecord user = mUserRecords.get(userId);
RoboErik4646d282014-05-13 10:13:04 -0700217 if (user != null) {
218 destroyUserLocked(user);
219 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900220 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700221 }
222 }
223
224 @Override
RoboErika278ea72014-04-24 14:49:01 -0700225 public void monitor() {
226 synchronized (mLock) {
227 // Check for deadlock
228 }
229 }
230
RoboErik4646d282014-05-13 10:13:04 -0700231 protected void enforcePhoneStatePermission(int pid, int uid) {
232 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
233 != PackageManager.PERMISSION_GRANTED) {
234 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
235 }
236 }
237
RoboErik01fe6612014-02-13 14:19:04 -0800238 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700239 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800240 destroySessionLocked(session);
241 }
242 }
243
244 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700245 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800246 destroySessionLocked(session);
247 }
248 }
249
RoboErik4646d282014-05-13 10:13:04 -0700250 private void updateUser() {
251 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900252 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
253 int currentUser = ActivityManager.getCurrentUser();
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700254 // Include all profiles even though they aren't yet enabled to handle work profile case.
255 int[] userIds = manager.getProfileIdsWithDisabled(currentUser);
Jaewan Kim8f729082016-06-21 12:36:26 +0900256 mCurrentUserIdList.clear();
257 if (userIds != null && userIds.length > 0) {
258 for (int userId : userIds) {
259 mCurrentUserIdList.add(userId);
RoboErik4646d282014-05-13 10:13:04 -0700260 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900261 } else {
262 // This shouldn't happen.
263 Log.w(TAG, "Failed to get enabled profiles.");
264 mCurrentUserIdList.add(currentUser);
265 }
266 for (int userId : mCurrentUserIdList) {
267 if (mUserRecords.get(userId) == null) {
268 mUserRecords.put(userId, new UserRecord(getContext(), userId));
269 }
RoboErik4646d282014-05-13 10:13:04 -0700270 }
271 }
272 }
273
RoboErik7aef77b2014-08-08 15:56:54 -0700274 private void updateActiveSessionListeners() {
275 synchronized (mLock) {
276 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
277 SessionsListenerRecord listener = mSessionsListeners.get(i);
278 try {
279 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
280 listener.mUserId);
281 } catch (SecurityException e) {
282 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
283 + " is no longer authorized. Disconnecting.");
284 mSessionsListeners.remove(i);
285 try {
286 listener.mListener
287 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
288 } catch (Exception e1) {
289 // ignore
290 }
291 }
292 }
293 }
294 }
295
RoboErik4646d282014-05-13 10:13:04 -0700296 /**
297 * Stop the user and unbind from everything.
298 *
299 * @param user The user to dispose of
300 */
301 private void destroyUserLocked(UserRecord user) {
RoboErik4646d282014-05-13 10:13:04 -0700302 user.destroyLocked();
303 mUserRecords.remove(user.mUserId);
304 }
305
306 /*
307 * When a session is removed several things need to happen.
308 * 1. We need to remove it from the relevant user.
309 * 2. We need to remove it from the priority stack.
310 * 3. We need to remove it from all sessions.
311 * 4. If this is the system priority session we need to clear it.
312 * 5. We need to unlink to death from the cb binder
313 * 6. We need to tell the session to do any final cleanup (onDestroy)
314 */
RoboErik01fe6612014-02-13 14:19:04 -0800315 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900316 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900317 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900318 }
RoboErik4646d282014-05-13 10:13:04 -0700319 int userId = session.getUserId();
320 UserRecord user = mUserRecords.get(userId);
321 if (user != null) {
322 user.removeSessionLocked(session);
323 }
324
RoboErika8f95142014-05-05 14:23:49 -0700325 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700326
327 try {
328 session.getCallback().asBinder().unlinkToDeath(session, 0);
329 } catch (Exception e) {
330 // ignore exceptions while destroying a session.
331 }
332 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700333
334 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800335 }
336
337 private void enforcePackageName(String packageName, int uid) {
338 if (TextUtils.isEmpty(packageName)) {
339 throw new IllegalArgumentException("packageName may not be empty");
340 }
341 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
342 final int packageCount = packages.length;
343 for (int i = 0; i < packageCount; i++) {
344 if (packageName.equals(packages[i])) {
345 return;
346 }
347 }
348 throw new IllegalArgumentException("packageName is not owned by the calling process");
349 }
350
RoboErike7880d82014-04-30 12:48:25 -0700351 /**
352 * Checks a caller's authorization to register an IRemoteControlDisplay.
353 * Authorization is granted if one of the following is true:
354 * <ul>
355 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
356 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700357 * <li>the caller's listener is one of the enabled notification listeners
358 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700359 * </ul>
360 */
RoboErika5b02322014-05-07 17:05:49 -0700361 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
362 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500363 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700364 if (getContext()
365 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
366 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700367 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
368 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700369 throw new SecurityException("Missing permission to control media.");
370 }
371 }
372
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500373 private boolean isCurrentVolumeController(int uid, int pid) {
374 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
375 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500376 }
377
378 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500379 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700380 throw new SecurityException("Only system ui may " + action);
381 }
382 }
383
RoboErika5b02322014-05-07 17:05:49 -0700384 /**
385 * This checks if the component is an enabled notification listener for the
386 * specified user. Enabled components may only operate on behalf of the user
387 * they're running as.
388 *
389 * @param compName The component that is enabled.
390 * @param userId The user id of the caller.
391 * @param forUserId The user id they're making the request on behalf of.
392 * @return True if the component is enabled, false otherwise
393 */
394 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
395 int forUserId) {
396 if (userId != forUserId) {
397 // You may not access another user's content as an enabled listener.
398 return false;
399 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700400 if (DEBUG) {
401 Log.d(TAG, "Checking if enabled notification listener " + compName);
402 }
RoboErike7880d82014-04-30 12:48:25 -0700403 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700404 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700405 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700406 userId);
RoboErike7880d82014-04-30 12:48:25 -0700407 if (enabledNotifListeners != null) {
408 final String[] components = enabledNotifListeners.split(":");
409 for (int i = 0; i < components.length; i++) {
410 final ComponentName component =
411 ComponentName.unflattenFromString(components[i]);
412 if (component != null) {
413 if (compName.equals(component)) {
414 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900415 Log.d(TAG, "ok to get sessions. " + component +
RoboErike7880d82014-04-30 12:48:25 -0700416 " is authorized notification listener");
417 }
418 return true;
419 }
420 }
421 }
422 }
423 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900424 Log.d(TAG, "not ok to get sessions. " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700425 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700426 }
427 }
428 return false;
429 }
430
RoboErika5b02322014-05-07 17:05:49 -0700431 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700432 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800433 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700434 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800435 }
436 }
437
RoboErik4646d282014-05-13 10:13:04 -0700438 /*
439 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700440 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700441 * 2. It needs to be added to all sessions.
442 * 3. It needs to be added to the priority stack.
443 * 4. It needs to be added to the relevant user record.
444 */
RoboErika5b02322014-05-07 17:05:49 -0700445 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
446 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700447
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700448 UserRecord user = mUserRecords.get(userId);
449 if (user == null) {
450 Log.wtf(TAG, "Request from invalid user: " + userId);
451 throw new RuntimeException("Session request from invalid user.");
452 }
453
RoboErika5b02322014-05-07 17:05:49 -0700454 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
455 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800456 try {
457 cb.asBinder().linkToDeath(session, 0);
458 } catch (RemoteException e) {
459 throw new RuntimeException("Media Session owner died prematurely.", e);
460 }
RoboErik4646d282014-05-13 10:13:04 -0700461
Jaewan Kim8f729082016-06-21 12:36:26 +0900462 mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
RoboErik4646d282014-05-13 10:13:04 -0700463 user.addSessionLocked(session);
464
RoboErik2e7a9162014-06-04 16:53:45 -0700465 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
466
RoboErik01fe6612014-02-13 14:19:04 -0800467 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900468 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800469 }
470 return session;
471 }
472
RoboErik2e7a9162014-06-04 16:53:45 -0700473 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
474 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800475 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700476 return i;
477 }
478 }
479 return -1;
480 }
481
RoboErik2e7a9162014-06-04 16:53:45 -0700482 private void pushSessionsChanged(int userId) {
483 synchronized (mLock) {
484 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
485 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800486 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700487 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700488 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700489 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700490 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700491 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700492 }
RoboErik19c95182014-06-23 15:38:48 -0700493 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700494 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
495 SessionsListenerRecord record = mSessionsListeners.get(i);
496 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
497 try {
498 record.mListener.onActiveSessionsChanged(tokens);
499 } catch (RemoteException e) {
500 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
501 e);
502 mSessionsListeners.remove(i);
503 }
504 }
505 }
506 }
507 }
508
RoboErik19c95182014-06-23 15:38:48 -0700509 private void pushRemoteVolumeUpdateLocked(int userId) {
510 if (mRvc != null) {
511 try {
512 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
513 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
514 } catch (RemoteException e) {
515 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
516 }
517 }
518 }
519
RoboErikb214efb2014-07-24 13:20:30 -0700520 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
521 PendingIntent receiver = record.getMediaButtonReceiver();
522 UserRecord user = mUserRecords.get(record.getUserId());
523 if (receiver != null && user != null) {
524 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800525 ComponentName component = receiver.getIntent().getComponent();
526 if (component != null && record.getPackageName().equals(component.getPackageName())) {
527 Settings.Secure.putStringForUser(mContentResolver,
528 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
529 record.getUserId());
530 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700531 }
532 }
533
Jaewan Kim50269362016-12-23 11:22:02 +0900534 private String getCallingPackageName(int uid) {
535 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
536 if (packages != null && packages.length > 0) {
537 return packages[0];
538 }
539 return "";
540 }
541
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900542 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
543 // Only consider full user.
544 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
545 try {
546 user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
547 } catch (RemoteException e) {
548 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
549 }
550 }
551
RoboErik4646d282014-05-13 10:13:04 -0700552 /**
553 * Information about a particular user. The contents of this object is
554 * guarded by mLock.
555 */
556 final class UserRecord {
557 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700558 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800559 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700560 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800561 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700562
Jaewan Kim50269362016-12-23 11:22:02 +0900563 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
564 private int mOnVolumeKeyLongPressListenerUid;
565 private KeyEvent mInitialDownVolumeKeyEvent;
566 private int mInitialDownVolumeStream;
567 private boolean mInitialDownMusicOnly;
568
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800569 private IOnMediaKeyListener mOnMediaKeyListener;
570 private int mOnMediaKeyListenerUid;
571
RoboErik4646d282014-05-13 10:13:04 -0700572 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800573 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700574 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800575 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700576 }
577
RoboErik4646d282014-05-13 10:13:04 -0700578 public void destroyLocked() {
579 for (int i = mSessions.size() - 1; i >= 0; i--) {
580 MediaSessionRecord session = mSessions.get(i);
581 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700582 }
583 }
584
RoboErik4646d282014-05-13 10:13:04 -0700585 public ArrayList<MediaSessionRecord> getSessionsLocked() {
586 return mSessions;
587 }
588
589 public void addSessionLocked(MediaSessionRecord session) {
590 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700591 }
592
593 public void removeSessionLocked(MediaSessionRecord session) {
594 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700595 }
596
597 public void dumpLocked(PrintWriter pw, String prefix) {
598 pw.println(prefix + "Record for user " + mUserId);
599 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700600 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800601 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jaewan Kim50269362016-12-23 11:22:02 +0900602 pw.println(indent + "Volume key long-press listener:" + mOnVolumeKeyLongPressListener);
603 pw.println(indent + "Volume key long-press listener package:" +
604 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800605 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
606 pw.println(indent + "Media key listener package: " +
607 getCallingPackageName(mOnMediaKeyListenerUid));
Jeff Brown01a500e2014-07-10 22:50:50 -0700608 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700609 pw.println(indent + size + " Sessions:");
610 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700611 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700612 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700613 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700614 }
615 }
RoboErikc8f92d12015-01-05 16:48:07 -0800616
617 private void restoreMediaButtonReceiver() {
618 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
Jaewan Kim8f729082016-06-21 12:36:26 +0900619 Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
RoboErikc8f92d12015-01-05 16:48:07 -0800620 if (!TextUtils.isEmpty(receiverName)) {
621 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
622 if (eventReceiver == null) {
623 // an invalid name was persisted
624 return;
625 }
626 mRestoredMediaButtonReceiver = eventReceiver;
627 }
628 }
RoboErik4646d282014-05-13 10:13:04 -0700629 }
630
RoboErik2e7a9162014-06-04 16:53:45 -0700631 final class SessionsListenerRecord implements IBinder.DeathRecipient {
632 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700633 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700634 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700635 private final int mPid;
636 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700637
RoboErik7aef77b2014-08-08 15:56:54 -0700638 public SessionsListenerRecord(IActiveSessionsListener listener,
639 ComponentName componentName,
640 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700641 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700642 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700643 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700644 mPid = pid;
645 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700646 }
647
648 @Override
649 public void binderDied() {
650 synchronized (mLock) {
651 mSessionsListeners.remove(this);
652 }
653 }
654 }
655
RoboErik7aef77b2014-08-08 15:56:54 -0700656 final class SettingsObserver extends ContentObserver {
657 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
658 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
659
660 private SettingsObserver() {
661 super(null);
662 }
663
664 private void observe() {
665 mContentResolver.registerContentObserver(mSecureSettingsUri,
666 false, this, UserHandle.USER_ALL);
667 }
668
669 @Override
670 public void onChange(boolean selfChange, Uri uri) {
671 updateActiveSessionListeners();
672 }
673 }
674
RoboErik07c70772014-03-20 13:33:52 -0700675 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700676 private static final String EXTRA_WAKELOCK_ACQUIRED =
677 "android.media.AudioService.WAKELOCK_ACQUIRED";
678 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
679
RoboErik9a9d0b52014-05-20 14:53:39 -0700680 private boolean mVoiceButtonDown = false;
681 private boolean mVoiceButtonHandled = false;
682
RoboErik07c70772014-03-20 13:33:52 -0700683 @Override
RoboErika5b02322014-05-07 17:05:49 -0700684 public ISession createSession(String packageName, ISessionCallback cb, String tag,
685 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800686 final int pid = Binder.getCallingPid();
687 final int uid = Binder.getCallingUid();
688 final long token = Binder.clearCallingIdentity();
689 try {
690 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700691 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
692 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800693 if (cb == null) {
694 throw new IllegalArgumentException("Controller callback cannot be null");
695 }
RoboErika5b02322014-05-07 17:05:49 -0700696 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
697 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700698 } finally {
699 Binder.restoreCallingIdentity(token);
700 }
701 }
702
703 @Override
RoboErika5b02322014-05-07 17:05:49 -0700704 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700705 final int pid = Binder.getCallingPid();
706 final int uid = Binder.getCallingUid();
707 final long token = Binder.clearCallingIdentity();
708
709 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700710 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700711 ArrayList<IBinder> binders = new ArrayList<IBinder>();
712 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700713 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700714 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700715 int size = records.size();
716 for (int i = 0; i < size; i++) {
717 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700718 }
719 }
720 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800721 } finally {
722 Binder.restoreCallingIdentity(token);
723 }
724 }
RoboErika278ea72014-04-24 14:49:01 -0700725
RoboErik2e7a9162014-06-04 16:53:45 -0700726 @Override
727 public void addSessionsListener(IActiveSessionsListener listener,
728 ComponentName componentName, int userId) throws RemoteException {
729 final int pid = Binder.getCallingPid();
730 final int uid = Binder.getCallingUid();
731 final long token = Binder.clearCallingIdentity();
732
733 try {
734 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
735 synchronized (mLock) {
736 int index = findIndexOfSessionsListenerLocked(listener);
737 if (index != -1) {
738 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
739 return;
740 }
741 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700742 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700743 try {
744 listener.asBinder().linkToDeath(record, 0);
745 } catch (RemoteException e) {
746 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
747 return;
748 }
749 mSessionsListeners.add(record);
750 }
751 } finally {
752 Binder.restoreCallingIdentity(token);
753 }
754 }
755
756 @Override
757 public void removeSessionsListener(IActiveSessionsListener listener)
758 throws RemoteException {
759 synchronized (mLock) {
760 int index = findIndexOfSessionsListenerLocked(listener);
761 if (index != -1) {
762 SessionsListenerRecord record = mSessionsListeners.remove(index);
763 try {
764 record.mListener.asBinder().unlinkToDeath(record, 0);
765 } catch (Exception e) {
766 // ignore exceptions, the record is being removed
767 }
768 }
769 }
770 }
771
RoboErik8a2cfc32014-05-16 11:19:38 -0700772 /**
773 * Handles the dispatching of the media button events to one of the
774 * registered listeners, or if there was none, broadcast an
775 * ACTION_MEDIA_BUTTON intent to the rest of the system.
776 *
777 * @param keyEvent a non-null KeyEvent whose key code is one of the
778 * supported media buttons
779 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
780 * while this key event is dispatched.
781 */
782 @Override
783 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
784 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
785 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
786 return;
787 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700788
RoboErik8a2cfc32014-05-16 11:19:38 -0700789 final int pid = Binder.getCallingPid();
790 final int uid = Binder.getCallingUid();
791 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700792 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700793 if (DEBUG) {
794 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
795 + keyEvent);
796 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700797 if (!isUserSetupComplete()) {
798 // Global media key handling can have the side-effect of starting new
799 // activities which is undesirable while setup is in progress.
800 Slog.i(TAG, "Not dispatching media key event because user "
801 + "setup is in progress.");
802 return;
803 }
804
RoboErik8a2cfc32014-05-16 11:19:38 -0700805 synchronized (mLock) {
Jaewan Kim51255012017-02-24 16:19:14 +0900806 boolean isGlobalPriorityActive = mPriorityStack.isGlobalPriorityActive();
807 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
808 // Prevent dispatching key event through reflection while the global
809 // priority session is active.
810 Slog.i(TAG, "Only the system can dispatch media key event "
811 + "to the global priority session.");
812 return;
813 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900814 if (!isGlobalPriorityActive) {
815 // Only consider full user.
816 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
817 if (user.mOnMediaKeyListener != null) {
818 if (DEBUG_KEY_EVENT) {
819 Log.d(TAG, "Send " + keyEvent + " to media key listener");
820 }
821 try {
822 user.mOnMediaKeyListener.onMediaKey(keyEvent,
823 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
824 return;
825 } catch (RemoteException e) {
826 Log.w(TAG, "Failed to send " + keyEvent + " to media key listener");
827 }
828 }
829 }
Jaewan Kim51255012017-02-24 16:19:14 +0900830 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800831 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700832 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +0900833 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700834 }
835 }
836 } finally {
837 Binder.restoreCallingIdentity(token);
838 }
839 }
840
RoboErika278ea72014-04-24 14:49:01 -0700841 @Override
Jaewan Kim50269362016-12-23 11:22:02 +0900842 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
843 final int pid = Binder.getCallingPid();
844 final int uid = Binder.getCallingUid();
845 final long token = Binder.clearCallingIdentity();
846 try {
847 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
848 if (getContext().checkPermission(
849 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
850 != PackageManager.PERMISSION_GRANTED) {
851 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
852 " permission.");
853 }
854
855 synchronized (mLock) {
856 UserRecord user = mUserRecords.get(UserHandle.getUserId(uid));
857 if (user.mOnVolumeKeyLongPressListener != null &&
858 user.mOnVolumeKeyLongPressListenerUid != uid) {
859 Log.w(TAG, "Volume key long-press listener cannot be reset by another app");
860 return;
861 }
862
863 user.mOnVolumeKeyLongPressListener = listener;
864 user.mOnVolumeKeyLongPressListenerUid = uid;
865
866 Log.d(TAG, "Volume key long-press listener "
867 + listener + " is set by " + getCallingPackageName(uid));
868
869 if (user.mOnVolumeKeyLongPressListener != null) {
870 try {
871 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
872 new IBinder.DeathRecipient() {
873 @Override
874 public void binderDied() {
875 synchronized (mLock) {
876 user.mOnVolumeKeyLongPressListener = null;
877 }
878 }
879 }, 0);
880 } catch (RemoteException e) {
881 Log.w(TAG, "Failed to set death recipient "
882 + user.mOnVolumeKeyLongPressListener);
883 user.mOnVolumeKeyLongPressListener = null;
884 }
885 }
886 }
887 } finally {
888 Binder.restoreCallingIdentity(token);
889 }
890 }
891
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800892 @Override
893 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
894 final int pid = Binder.getCallingPid();
895 final int uid = Binder.getCallingUid();
896 final long token = Binder.clearCallingIdentity();
897 try {
898 // Enforce SET_MEDIA_KEY_LISTENER permission.
899 if (getContext().checkPermission(
900 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
901 != PackageManager.PERMISSION_GRANTED) {
902 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
903 " permission.");
904 }
905
906 synchronized (mLock) {
907 int userId = UserHandle.getUserId(uid);
908 UserRecord user = mUserRecords.get(userId);
909 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
910 Log.w(TAG, "Media key listener cannot be reset by another app");
911 return;
912 }
913
914 user.mOnMediaKeyListener = listener;
915 user.mOnMediaKeyListenerUid = uid;
916
917 Log.d(TAG, "Media key listener " + user.mOnMediaKeyListener
918 + " is set by " + getCallingPackageName(uid));
919
920 if (user.mOnMediaKeyListener != null) {
921 try {
922 user.mOnMediaKeyListener.asBinder().linkToDeath(
923 new IBinder.DeathRecipient() {
924 @Override
925 public void binderDied() {
926 synchronized (mLock) {
927 user.mOnMediaKeyListener = null;
928 }
929 }
930 }, 0);
931 } catch (RemoteException e) {
932 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
933 user.mOnMediaKeyListener = null;
934 }
935 }
936 }
937 } finally {
938 Binder.restoreCallingIdentity(token);
939 }
940 }
941
Jaewan Kim50269362016-12-23 11:22:02 +0900942 /**
943 * Handles the dispatching of the volume button events to one of the
944 * registered listeners. If there's a volume key long-press listener and
945 * there's no active global priority session, long-pressess will be sent to the
946 * long-press listener instead of adjusting volume.
947 *
948 * @param keyEvent a non-null KeyEvent whose key code is one of the
949 * {@link KeyEvent#KEYCODE_VOLUME_UP},
950 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
951 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
952 * @param stream stream type to adjust volume.
953 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
954 */
955 @Override
956 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
957 if (keyEvent == null ||
958 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
959 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
960 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
961 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
962 return;
963 }
964
965 final int pid = Binder.getCallingPid();
966 final int uid = Binder.getCallingUid();
967 final long token = Binder.clearCallingIdentity();
968
969 if (DEBUG) {
970 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
971 + keyEvent);
972 }
973
974 try {
975 synchronized (mLock) {
976 // Only consider full user.
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900977 int userId = mCurrentUserIdList.get(0);
978 UserRecord user = mUserRecords.get(userId);
Jaewan Kim50269362016-12-23 11:22:02 +0900979
980 if (mPriorityStack.isGlobalPriorityActive()
981 || user.mOnVolumeKeyLongPressListener == null) {
982 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
983 } else {
984 // TODO: Consider the case when both volume up and down keys are pressed
985 // at the same time.
986 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
987 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900988 // Keeps the copy of the KeyEvent because it can be reused.
989 user.mInitialDownVolumeKeyEvent = KeyEvent.obtain(keyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +0900990 user.mInitialDownVolumeStream = stream;
991 user.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900992 mHandler.sendMessageDelayed(
993 mHandler.obtainMessage(
994 MessageHandler.MSG_VOLUME_INITIAL_DOWN, userId, 0),
995 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +0900996 }
997 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900998 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kim50269362016-12-23 11:22:02 +0900999 if (user.mInitialDownVolumeKeyEvent != null) {
1000 dispatchVolumeKeyLongPressLocked(
1001 user.mInitialDownVolumeKeyEvent);
1002 // Mark that the key is already handled.
1003 user.mInitialDownVolumeKeyEvent = null;
1004 }
1005 dispatchVolumeKeyLongPressLocked(keyEvent);
1006 }
1007 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001008 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kim50269362016-12-23 11:22:02 +09001009 if (user.mInitialDownVolumeKeyEvent != null
1010 && user.mInitialDownVolumeKeyEvent.getDownTime()
1011 == keyEvent.getDownTime()) {
1012 // Short-press. Should change volume.
1013 dispatchVolumeKeyEventLocked(
1014 user.mInitialDownVolumeKeyEvent,
1015 user.mInitialDownVolumeStream,
1016 user.mInitialDownMusicOnly);
1017 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1018 } else {
1019 dispatchVolumeKeyLongPressLocked(keyEvent);
1020 }
1021 }
1022 }
1023 }
1024 } finally {
1025 Binder.restoreCallingIdentity(token);
1026 }
1027 }
1028
Jaewan Kim50269362016-12-23 11:22:02 +09001029 private void dispatchVolumeKeyEventLocked(
1030 KeyEvent keyEvent, int stream, boolean musicOnly) {
1031 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1032 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1033 int direction = 0;
1034 boolean isMute = false;
1035 switch (keyEvent.getKeyCode()) {
1036 case KeyEvent.KEYCODE_VOLUME_UP:
1037 direction = AudioManager.ADJUST_RAISE;
1038 break;
1039 case KeyEvent.KEYCODE_VOLUME_DOWN:
1040 direction = AudioManager.ADJUST_LOWER;
1041 break;
1042 case KeyEvent.KEYCODE_VOLUME_MUTE:
1043 isMute = true;
1044 break;
1045 }
1046 if (down || up) {
1047 int flags = AudioManager.FLAG_FROM_KEY;
1048 if (musicOnly) {
1049 // This flag is used when the screen is off to only affect active media.
1050 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1051 } else {
1052 // These flags are consistent with the home screen
1053 if (up) {
1054 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1055 } else {
1056 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1057 }
1058 }
1059 if (direction != 0) {
1060 // If this is action up we want to send a beep for non-music events
1061 if (up) {
1062 direction = 0;
1063 }
1064 dispatchAdjustVolumeLocked(stream, direction, flags);
1065 } else if (isMute) {
1066 if (down && keyEvent.getRepeatCount() == 0) {
1067 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1068 }
1069 }
1070 }
1071 }
1072
1073 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001074 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001075 final long token = Binder.clearCallingIdentity();
1076 try {
1077 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001078 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001079 }
1080 } finally {
1081 Binder.restoreCallingIdentity(token);
1082 }
1083 }
1084
1085 @Override
RoboErik19c95182014-06-23 15:38:48 -07001086 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1087 final int pid = Binder.getCallingPid();
1088 final int uid = Binder.getCallingUid();
1089 final long token = Binder.clearCallingIdentity();
1090 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001091 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001092 mRvc = rvc;
1093 } finally {
1094 Binder.restoreCallingIdentity(token);
1095 }
1096 }
1097
1098 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001099 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001100 synchronized (mLock) {
1101 return mPriorityStack.isGlobalPriorityActive();
1102 }
RoboErikde9ba392014-09-26 12:51:01 -07001103 }
1104
1105 @Override
RoboErika278ea72014-04-24 14:49:01 -07001106 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1107 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
1108 != PackageManager.PERMISSION_GRANTED) {
1109 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
1110 + Binder.getCallingPid()
1111 + ", uid=" + Binder.getCallingUid());
1112 return;
1113 }
1114
1115 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1116 pw.println();
1117
1118 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001119 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErika5b02322014-05-07 17:05:49 -07001120 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -07001121
RoboErik4646d282014-05-13 10:13:04 -07001122 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001123 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001124 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -08001125 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -07001126 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001127 }
1128 }
1129 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001130
RoboErik2e7a9162014-06-04 16:53:45 -07001131 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1132 final int uid) {
1133 String packageName = null;
1134 if (componentName != null) {
1135 // If they gave us a component name verify they own the
1136 // package
1137 packageName = componentName.getPackageName();
1138 enforcePackageName(packageName, uid);
1139 }
1140 // Check that they can make calls on behalf of the user and
1141 // get the final user id
1142 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1143 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1144 // Check if they have the permissions or their component is
1145 // enabled for the user they're calling from.
1146 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1147 return resolvedUserId;
1148 }
1149
Jaewan Kim50269362016-12-23 11:22:02 +09001150 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
1151 MediaSessionRecord session = mPriorityStack.getDefaultVolumeSession(mCurrentUserIdList);
1152
RoboErik9c785402014-11-11 16:52:26 -08001153 boolean preferSuggestedStream = false;
1154 if (isValidLocalStreamType(suggestedStream)
1155 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1156 preferSuggestedStream = true;
1157 }
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001158 if (DEBUG) {
1159 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1160 + flags + ", suggestedStream=" + suggestedStream
1161 + ", preferSuggestedStream=" + preferSuggestedStream);
1162 }
RoboErik9c785402014-11-11 16:52:26 -08001163 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001164 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1165 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001166 if (DEBUG) {
1167 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001168 }
RoboErikb7c014c2014-07-22 15:58:22 -07001169 return;
RoboErik3c45c292014-07-08 16:47:31 -07001170 }
Shibin George19e84042016-06-14 20:42:13 +05301171
1172 // Execute mAudioService.adjustSuggestedStreamVolume() on
1173 // handler thread of MediaSessionService.
1174 // This will release the MediaSessionService.mLock sooner and avoid
1175 // a potential deadlock between MediaSessionService.mLock and
1176 // ActivityManagerService lock.
1177 mHandler.post(new Runnable() {
1178 @Override
1179 public void run() {
1180 try {
1181 String packageName = getContext().getOpPackageName();
1182 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1183 flags, packageName, TAG);
1184 } catch (RemoteException e) {
1185 Log.e(TAG, "Error adjusting default volume.", e);
1186 }
1187 }
1188 });
RoboErikb69ffd42014-05-30 14:57:59 -07001189 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001190 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001191 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001192 }
1193 }
1194
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001195 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001196 int action = keyEvent.getAction();
1197 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1198 if (action == KeyEvent.ACTION_DOWN) {
1199 if (keyEvent.getRepeatCount() == 0) {
1200 mVoiceButtonDown = true;
1201 mVoiceButtonHandled = false;
1202 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1203 mVoiceButtonHandled = true;
1204 startVoiceInput(needWakeLock);
1205 }
1206 } else if (action == KeyEvent.ACTION_UP) {
1207 if (mVoiceButtonDown) {
1208 mVoiceButtonDown = false;
1209 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1210 // Resend the down then send this event through
1211 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001212 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1213 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001214 }
1215 }
1216 }
1217 }
1218
Jaewan Kim98003d32017-02-24 18:33:04 +09001219 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001220 // If we don't have a media button receiver to fall back on
1221 // include non-playing sessions for dispatching.
1222 boolean useNotPlayingSessions = true;
1223 for (int userId : mCurrentUserIdList) {
1224 UserRecord ur = mUserRecords.get(userId);
1225 if (ur.mLastMediaButtonReceiver != null
1226 || ur.mRestoredMediaButtonReceiver != null) {
1227 useNotPlayingSessions = false;
1228 break;
1229 }
1230 }
1231 if (DEBUG) {
1232 Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
1233 + useNotPlayingSessions);
1234 }
1235
1236 MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
1237 mCurrentUserIdList, useNotPlayingSessions);
RoboErik9a9d0b52014-05-20 14:53:39 -07001238 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001239 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001240 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001241 }
1242 if (needWakeLock) {
1243 mKeyEventReceiver.aquireWakeLockLocked();
1244 }
Jaewan Kim50269362016-12-23 11:22:02 +09001245 // 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 -07001246 session.sendMediaButton(keyEvent,
1247 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001248 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001249 getContext().getPackageName());
RoboErik9a9d0b52014-05-20 14:53:39 -07001250 } else {
RoboErikb214efb2014-07-24 13:20:30 -07001251 // Launch the last PendingIntent we had with priority
Jaewan Kim8f729082016-06-21 12:36:26 +09001252 for (int userId : mCurrentUserIdList) {
1253 UserRecord user = mUserRecords.get(userId);
1254 if (user.mLastMediaButtonReceiver == null
1255 && user.mRestoredMediaButtonReceiver == null) {
1256 continue;
1257 }
RoboErikb214efb2014-07-24 13:20:30 -07001258 if (needWakeLock) {
1259 mKeyEventReceiver.aquireWakeLockLocked();
1260 }
1261 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Insun Kang2054db32016-04-07 15:34:34 +09001262 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RoboErikb214efb2014-07-24 13:20:30 -07001263 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1264 try {
RoboErikc8f92d12015-01-05 16:48:07 -08001265 if (user.mLastMediaButtonReceiver != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001266 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001267 Log.d(TAG, "Sending " + keyEvent
1268 + " to the last known pendingIntent "
1269 + user.mLastMediaButtonReceiver);
1270 }
RoboErikc8f92d12015-01-05 16:48:07 -08001271 user.mLastMediaButtonReceiver.send(getContext(),
1272 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
riddle_hsu02ed0122015-10-20 16:00:15 +08001273 mediaButtonIntent, mKeyEventReceiver, mHandler);
RoboErikc8f92d12015-01-05 16:48:07 -08001274 } else {
Jaewan Kim50269362016-12-23 11:22:02 +09001275 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001276 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1277 + user.mRestoredMediaButtonReceiver);
1278 }
RoboErikc8f92d12015-01-05 16:48:07 -08001279 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
1280 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim8f729082016-06-21 12:36:26 +09001281 UserHandle.of(userId));
RoboErikc8f92d12015-01-05 16:48:07 -08001282 }
RoboErikb214efb2014-07-24 13:20:30 -07001283 } catch (CanceledException e) {
1284 Log.i(TAG, "Error sending key event to media button receiver "
1285 + user.mLastMediaButtonReceiver, e);
1286 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001287 return;
RoboErik9a9d0b52014-05-20 14:53:39 -07001288 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001289 if (DEBUG) {
1290 Log.d(TAG, "Sending media key ordered broadcast");
1291 }
1292 if (needWakeLock) {
1293 mMediaEventWakeLock.acquire();
1294 }
1295 // Fallback to legacy behavior
1296 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1297 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1298 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1299 if (needWakeLock) {
1300 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1301 }
1302 // Send broadcast only to the full user.
1303 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1304 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001305 }
1306 }
1307
1308 private void startVoiceInput(boolean needWakeLock) {
1309 Intent voiceIntent = null;
1310 // select which type of search to launch:
1311 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1312 // - device locked or screen off: action is
1313 // ACTION_VOICE_SEARCH_HANDS_FREE
1314 // with EXTRA_SECURE set to true if the device is securely locked
1315 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1316 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1317 if (!isLocked && pm.isScreenOn()) {
1318 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1319 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1320 } else {
1321 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1322 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1323 isLocked && mKeyguardManager.isKeyguardSecure());
1324 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1325 }
1326 // start the search activity
1327 if (needWakeLock) {
1328 mMediaEventWakeLock.acquire();
1329 }
1330 try {
1331 if (voiceIntent != null) {
1332 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1333 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001334 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001335 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1336 }
1337 } catch (ActivityNotFoundException e) {
1338 Log.w(TAG, "No activity for search: " + e);
1339 } finally {
1340 if (needWakeLock) {
1341 mMediaEventWakeLock.release();
1342 }
1343 }
1344 }
1345
1346 private boolean isVoiceKey(int keyCode) {
1347 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1348 }
1349
Jeff Brown38d3feb2015-03-19 18:26:30 -07001350 private boolean isUserSetupComplete() {
1351 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1352 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1353 }
1354
RoboErik9c785402014-11-11 16:52:26 -08001355 // we only handle public stream types, which are 0-5
1356 private boolean isValidLocalStreamType(int streamType) {
1357 return streamType >= AudioManager.STREAM_VOICE_CALL
1358 && streamType <= AudioManager.STREAM_NOTIFICATION;
1359 }
1360
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001361 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1362 private KeyEvent mKeyEvent;
1363 private boolean mNeedWakeLock;
1364 private boolean mHandled;
1365
1366 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1367 super(mHandler);
1368 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1369 mKeyEvent = keyEvent;
1370 mNeedWakeLock = needWakeLock;
1371 }
1372
1373 @Override
1374 public void run() {
1375 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1376 dispatchMediaKeyEvent();
1377 }
1378
1379 @Override
1380 protected void onReceiveResult(int resultCode, Bundle resultData) {
1381 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1382 mHandled = true;
1383 mHandler.removeCallbacks(this);
1384 return;
1385 }
1386 dispatchMediaKeyEvent();
1387 }
1388
1389 private void dispatchMediaKeyEvent() {
1390 if (mHandled) {
1391 return;
1392 }
1393 mHandled = true;
1394 mHandler.removeCallbacks(this);
1395 synchronized (mLock) {
Jaewan Kim98003d32017-02-24 18:33:04 +09001396 if (!mPriorityStack.isGlobalPriorityActive()
1397 && isVoiceKey(mKeyEvent.getKeyCode())) {
1398 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1399 } else {
1400 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1401 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001402 }
1403 }
1404 }
1405
RoboErik418c10c2014-05-19 09:25:25 -07001406 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1407
RoboErikb214efb2014-07-24 13:20:30 -07001408 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1409 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001410 private final Handler mHandler;
1411 private int mRefCount = 0;
1412 private int mLastTimeoutId = 0;
1413
1414 public KeyEventWakeLockReceiver(Handler handler) {
1415 super(handler);
1416 mHandler = handler;
1417 }
1418
1419 public void onTimeout() {
1420 synchronized (mLock) {
1421 if (mRefCount == 0) {
1422 // We've already released it, so just return
1423 return;
1424 }
1425 mLastTimeoutId++;
1426 mRefCount = 0;
1427 releaseWakeLockLocked();
1428 }
1429 }
1430
1431 public void aquireWakeLockLocked() {
1432 if (mRefCount == 0) {
1433 mMediaEventWakeLock.acquire();
1434 }
1435 mRefCount++;
1436 mHandler.removeCallbacks(this);
1437 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1438
1439 }
1440
1441 @Override
1442 public void run() {
1443 onTimeout();
1444 }
1445
RoboErik8a2cfc32014-05-16 11:19:38 -07001446 @Override
1447 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001448 if (resultCode < mLastTimeoutId) {
1449 // Ignore results from calls that were before the last
1450 // timeout, just in case.
1451 return;
1452 } else {
1453 synchronized (mLock) {
1454 if (mRefCount > 0) {
1455 mRefCount--;
1456 if (mRefCount == 0) {
1457 releaseWakeLockLocked();
1458 }
1459 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001460 }
1461 }
1462 }
RoboErik418c10c2014-05-19 09:25:25 -07001463
1464 private void releaseWakeLockLocked() {
1465 mMediaEventWakeLock.release();
1466 mHandler.removeCallbacks(this);
1467 }
RoboErikb214efb2014-07-24 13:20:30 -07001468
1469 @Override
1470 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1471 String resultData, Bundle resultExtras) {
1472 onReceiveResult(resultCode, null);
1473 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001474 };
1475
1476 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1477 @Override
1478 public void onReceive(Context context, Intent intent) {
1479 if (intent == null) {
1480 return;
1481 }
1482 Bundle extras = intent.getExtras();
1483 if (extras == null) {
1484 return;
1485 }
1486 synchronized (mLock) {
1487 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1488 && mMediaEventWakeLock.isHeld()) {
1489 mMediaEventWakeLock.release();
1490 }
1491 }
1492 }
1493 };
RoboErik01fe6612014-02-13 14:19:04 -08001494 }
1495
RoboErik2e7a9162014-06-04 16:53:45 -07001496 final class MessageHandler extends Handler {
1497 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001498 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
RoboErik2e7a9162014-06-04 16:53:45 -07001499
1500 @Override
1501 public void handleMessage(Message msg) {
1502 switch (msg.what) {
1503 case MSG_SESSIONS_CHANGED:
1504 pushSessionsChanged(msg.arg1);
1505 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001506 case MSG_VOLUME_INITIAL_DOWN:
1507 synchronized (mLock) {
1508 UserRecord user = mUserRecords.get((int) msg.arg1);
1509 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1510 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1511 // Mark that the key is already handled.
1512 user.mInitialDownVolumeKeyEvent = null;
1513 }
1514 }
1515 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001516 }
1517 }
1518
1519 public void post(int what, int arg1, int arg2) {
1520 obtainMessage(what, arg1, arg2).sendToTarget();
1521 }
1522 }
RoboErik01fe6612014-02-13 14:19:04 -08001523}