blob: a3f09c03ae74f2e2df39115f24c7604778391672 [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;
RoboErik8a2cfc32014-05-16 11:19:38 -070020import android.app.Activity;
RoboErike7880d82014-04-30 12:48:25 -070021import android.app.ActivityManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070022import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070023import android.app.PendingIntent;
24import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070025import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070026import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070027import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070028import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080029import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070030import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070031import android.content.pm.PackageManager;
RoboErik7aef77b2014-08-08 15:56:54 -070032import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070033import android.media.AudioManager;
John Spurlockeb69e242015-02-17 17:15:04 -050034import android.media.AudioManagerInternal;
RoboErik94c716e2014-09-14 13:54:31 -070035import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070036import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070037import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070038import android.media.session.IActiveSessionsListener;
RoboErik07c70772014-03-20 13:33:52 -070039import android.media.session.ISession;
40import android.media.session.ISessionCallback;
41import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070042import android.media.session.MediaSession;
RoboErik7aef77b2014-08-08 15:56:54 -070043import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080044import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070045import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080046import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070047import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070048import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070049import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090050import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080051import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070052import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070053import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070054import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090055import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070056import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070057import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080058import android.text.TextUtils;
59import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070060import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070061import android.util.SparseArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070062import android.view.KeyEvent;
RoboErik01fe6612014-02-13 14:19:04 -080063
John Spurlockeb69e242015-02-17 17:15:04 -050064import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080065import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070066import com.android.server.Watchdog;
67import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080068
RoboErika278ea72014-04-24 14:49:01 -070069import java.io.FileDescriptor;
70import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080071import java.util.ArrayList;
Jaewan Kim8f729082016-06-21 12:36:26 +090072import java.util.Arrays;
RoboErike7880d82014-04-30 12:48:25 -070073import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080074
75/**
76 * System implementation of MediaSessionManager
77 */
RoboErika278ea72014-04-24 14:49:01 -070078public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080079 private static final String TAG = "MediaSessionService";
80 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
81
RoboErik418c10c2014-05-19 09:25:25 -070082 private static final int WAKELOCK_TIMEOUT = 5000;
83
RoboErik2610d712015-01-07 11:10:23 -080084 /* package */final IBinder mICallback = new Binder();
85
RoboErik01fe6612014-02-13 14:19:04 -080086 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070087 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080088
RoboErik4646d282014-05-13 10:13:04 -070089 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
90 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070091 private final ArrayList<SessionsListenerRecord> mSessionsListeners
92 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -080093 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -070094 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -070095 private final PowerManager.WakeLock mMediaEventWakeLock;
RoboErik01fe6612014-02-13 14:19:04 -080096
RoboErik9a9d0b52014-05-20 14:53:39 -070097 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -070098 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -050099 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700100 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700101 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -0700102
Jaewan Kim8f729082016-06-21 12:36:26 +0900103 // List of user IDs running in the foreground.
104 // Multiple users can be in the foreground if the work profile is on.
105 private final List<Integer> mCurrentUserIdList = new ArrayList<>();
RoboErike7880d82014-04-30 12:48:25 -0700106
RoboErik19c95182014-06-23 15:38:48 -0700107 // Used to notify system UI when remote volume was changed. TODO find a
108 // better way to handle this.
109 private IRemoteVolumeController mRvc;
110
RoboErik01fe6612014-02-13 14:19:04 -0800111 public MediaSessionService(Context context) {
112 super(context);
113 mSessionManagerImpl = new SessionManagerImpl();
RoboErika8f95142014-05-05 14:23:49 -0700114 mPriorityStack = new MediaSessionStack();
RoboErik8a2cfc32014-05-16 11:19:38 -0700115 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
116 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
RoboErik01fe6612014-02-13 14:19:04 -0800117 }
118
119 @Override
120 public void onStart() {
121 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700122 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700123 mKeyguardManager =
124 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700125 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500126 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700127 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700128 mSettingsObserver = new SettingsObserver();
129 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800130
131 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700132 }
133
134 private IAudioService getAudioService() {
135 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
136 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700137 }
138
RoboErika8f95142014-05-05 14:23:49 -0700139 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700140 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700141 if (!mAllSessions.contains(record)) {
142 Log.d(TAG, "Unknown session updated. Ignoring.");
143 return;
144 }
RoboErika8f95142014-05-05 14:23:49 -0700145 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700146 }
RoboErik2e7a9162014-06-04 16:53:45 -0700147 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700148 }
149
RoboErik9c5b7cb2015-01-15 15:09:09 -0800150 /**
151 * Tells the system UI that volume has changed on a remote session.
152 */
153 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
154 if (mRvc == null) {
155 return;
156 }
157 try {
158 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
159 } catch (Exception e) {
160 Log.wtf(TAG, "Error sending volume change to system UI.", e);
161 }
162 }
163
RoboErika8f95142014-05-05 14:23:49 -0700164 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700165 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700166 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700167 if (!mAllSessions.contains(record)) {
168 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
169 return;
170 }
RoboErik2e7a9162014-06-04 16:53:45 -0700171 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
172 }
173 if (updateSessions) {
174 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700175 }
176 }
177
RoboErik19c95182014-06-23 15:38:48 -0700178 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
179 synchronized (mLock) {
180 if (!mAllSessions.contains(record)) {
181 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
182 return;
183 }
184 pushRemoteVolumeUpdateLocked(record.getUserId());
185 }
186 }
187
RoboErika278ea72014-04-24 14:49:01 -0700188 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900189 public void onStartUser(int userId) {
190 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700191 updateUser();
192 }
193
194 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900195 public void onSwitchUser(int userId) {
196 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700197 updateUser();
198 }
199
200 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900201 public void onStopUser(int userId) {
202 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700203 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900204 UserRecord user = mUserRecords.get(userId);
RoboErik4646d282014-05-13 10:13:04 -0700205 if (user != null) {
206 destroyUserLocked(user);
207 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900208 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700209 }
210 }
211
212 @Override
RoboErika278ea72014-04-24 14:49:01 -0700213 public void monitor() {
214 synchronized (mLock) {
215 // Check for deadlock
216 }
217 }
218
RoboErik4646d282014-05-13 10:13:04 -0700219 protected void enforcePhoneStatePermission(int pid, int uid) {
220 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
221 != PackageManager.PERMISSION_GRANTED) {
222 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
223 }
224 }
225
RoboErik01fe6612014-02-13 14:19:04 -0800226 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700227 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800228 destroySessionLocked(session);
229 }
230 }
231
232 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700233 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800234 destroySessionLocked(session);
235 }
236 }
237
RoboErik4646d282014-05-13 10:13:04 -0700238 private void updateUser() {
239 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900240 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
241 int currentUser = ActivityManager.getCurrentUser();
242 int[] userIds = manager.getEnabledProfileIds(currentUser);
243 mCurrentUserIdList.clear();
244 if (userIds != null && userIds.length > 0) {
245 for (int userId : userIds) {
246 mCurrentUserIdList.add(userId);
RoboErik4646d282014-05-13 10:13:04 -0700247 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900248 } else {
249 // This shouldn't happen.
250 Log.w(TAG, "Failed to get enabled profiles.");
251 mCurrentUserIdList.add(currentUser);
252 }
253 for (int userId : mCurrentUserIdList) {
254 if (mUserRecords.get(userId) == null) {
255 mUserRecords.put(userId, new UserRecord(getContext(), userId));
256 }
RoboErik4646d282014-05-13 10:13:04 -0700257 }
258 }
259 }
260
RoboErik7aef77b2014-08-08 15:56:54 -0700261 private void updateActiveSessionListeners() {
262 synchronized (mLock) {
263 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
264 SessionsListenerRecord listener = mSessionsListeners.get(i);
265 try {
266 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
267 listener.mUserId);
268 } catch (SecurityException e) {
269 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
270 + " is no longer authorized. Disconnecting.");
271 mSessionsListeners.remove(i);
272 try {
273 listener.mListener
274 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
275 } catch (Exception e1) {
276 // ignore
277 }
278 }
279 }
280 }
281 }
282
RoboErik4646d282014-05-13 10:13:04 -0700283 /**
284 * Stop the user and unbind from everything.
285 *
286 * @param user The user to dispose of
287 */
288 private void destroyUserLocked(UserRecord user) {
RoboErik4646d282014-05-13 10:13:04 -0700289 user.destroyLocked();
290 mUserRecords.remove(user.mUserId);
291 }
292
293 /*
294 * When a session is removed several things need to happen.
295 * 1. We need to remove it from the relevant user.
296 * 2. We need to remove it from the priority stack.
297 * 3. We need to remove it from all sessions.
298 * 4. If this is the system priority session we need to clear it.
299 * 5. We need to unlink to death from the cb binder
300 * 6. We need to tell the session to do any final cleanup (onDestroy)
301 */
RoboErik01fe6612014-02-13 14:19:04 -0800302 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900303 if (DEBUG) {
304 Log.d(TAG, "Destroying session : " + session.toString());
305 }
RoboErik4646d282014-05-13 10:13:04 -0700306 int userId = session.getUserId();
307 UserRecord user = mUserRecords.get(userId);
308 if (user != null) {
309 user.removeSessionLocked(session);
310 }
311
RoboErika8f95142014-05-05 14:23:49 -0700312 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700313 mAllSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700314
315 try {
316 session.getCallback().asBinder().unlinkToDeath(session, 0);
317 } catch (Exception e) {
318 // ignore exceptions while destroying a session.
319 }
320 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700321
322 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800323 }
324
325 private void enforcePackageName(String packageName, int uid) {
326 if (TextUtils.isEmpty(packageName)) {
327 throw new IllegalArgumentException("packageName may not be empty");
328 }
329 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
330 final int packageCount = packages.length;
331 for (int i = 0; i < packageCount; i++) {
332 if (packageName.equals(packages[i])) {
333 return;
334 }
335 }
336 throw new IllegalArgumentException("packageName is not owned by the calling process");
337 }
338
RoboErike7880d82014-04-30 12:48:25 -0700339 /**
340 * Checks a caller's authorization to register an IRemoteControlDisplay.
341 * Authorization is granted if one of the following is true:
342 * <ul>
343 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
344 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700345 * <li>the caller's listener is one of the enabled notification listeners
346 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700347 * </ul>
348 */
RoboErika5b02322014-05-07 17:05:49 -0700349 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
350 int resolvedUserId) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500351 if (isCurrentVolumeController(uid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700352 if (getContext()
353 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
354 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700355 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
356 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700357 throw new SecurityException("Missing permission to control media.");
358 }
359 }
360
John Spurlockbe19ed02015-02-22 10:57:55 -0500361 private boolean isCurrentVolumeController(int uid) {
John Spurlockeb69e242015-02-17 17:15:04 -0500362 if (mAudioManagerInternal != null) {
363 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
364 if (vcuid > 0 && uid == vcuid) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500365 return true;
John Spurlockeb69e242015-02-17 17:15:04 -0500366 }
367 }
John Spurlockbe19ed02015-02-22 10:57:55 -0500368 return false;
369 }
370
371 private void enforceSystemUiPermission(String action, int pid, int uid) {
372 if (isCurrentVolumeController(uid)) return;
RoboErik19c95182014-06-23 15:38:48 -0700373 if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
374 pid, uid) != PackageManager.PERMISSION_GRANTED) {
375 throw new SecurityException("Only system ui may " + action);
376 }
377 }
378
RoboErika5b02322014-05-07 17:05:49 -0700379 /**
380 * This checks if the component is an enabled notification listener for the
381 * specified user. Enabled components may only operate on behalf of the user
382 * they're running as.
383 *
384 * @param compName The component that is enabled.
385 * @param userId The user id of the caller.
386 * @param forUserId The user id they're making the request on behalf of.
387 * @return True if the component is enabled, false otherwise
388 */
389 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
390 int forUserId) {
391 if (userId != forUserId) {
392 // You may not access another user's content as an enabled listener.
393 return false;
394 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700395 if (DEBUG) {
396 Log.d(TAG, "Checking if enabled notification listener " + compName);
397 }
RoboErike7880d82014-04-30 12:48:25 -0700398 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700399 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700400 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700401 userId);
RoboErike7880d82014-04-30 12:48:25 -0700402 if (enabledNotifListeners != null) {
403 final String[] components = enabledNotifListeners.split(":");
404 for (int i = 0; i < components.length; i++) {
405 final ComponentName component =
406 ComponentName.unflattenFromString(components[i]);
407 if (component != null) {
408 if (compName.equals(component)) {
409 if (DEBUG) {
410 Log.d(TAG, "ok to get sessions: " + component +
411 " is authorized notification listener");
412 }
413 return true;
414 }
415 }
416 }
417 }
418 if (DEBUG) {
419 Log.d(TAG, "not ok to get sessions, " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700420 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700421 }
422 }
423 return false;
424 }
425
RoboErika5b02322014-05-07 17:05:49 -0700426 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700427 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800428 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700429 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800430 }
431 }
432
RoboErik4646d282014-05-13 10:13:04 -0700433 /*
434 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700435 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700436 * 2. It needs to be added to all sessions.
437 * 3. It needs to be added to the priority stack.
438 * 4. It needs to be added to the relevant user record.
439 */
RoboErika5b02322014-05-07 17:05:49 -0700440 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
441 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700442
RoboErika5b02322014-05-07 17:05:49 -0700443 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
444 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800445 try {
446 cb.asBinder().linkToDeath(session, 0);
447 } catch (RemoteException e) {
448 throw new RuntimeException("Media Session owner died prematurely.", e);
449 }
RoboErik4646d282014-05-13 10:13:04 -0700450
451 mAllSessions.add(session);
Jaewan Kim8f729082016-06-21 12:36:26 +0900452 mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
RoboErik4646d282014-05-13 10:13:04 -0700453
Jaewan Kim8f729082016-06-21 12:36:26 +0900454 UserRecord user = mUserRecords.get(userId);
RoboErik4646d282014-05-13 10:13:04 -0700455 user.addSessionLocked(session);
456
RoboErik2e7a9162014-06-04 16:53:45 -0700457 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
458
RoboErik01fe6612014-02-13 14:19:04 -0800459 if (DEBUG) {
RoboErika5b02322014-05-07 17:05:49 -0700460 Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800461 }
462 return session;
463 }
464
RoboErik2e7a9162014-06-04 16:53:45 -0700465 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
466 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800467 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700468 return i;
469 }
470 }
471 return -1;
472 }
473
RoboErik2e7a9162014-06-04 16:53:45 -0700474 private void pushSessionsChanged(int userId) {
475 synchronized (mLock) {
476 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
477 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800478 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700479 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700480 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700481 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700482 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700483 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700484 }
RoboErik19c95182014-06-23 15:38:48 -0700485 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700486 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
487 SessionsListenerRecord record = mSessionsListeners.get(i);
488 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
489 try {
490 record.mListener.onActiveSessionsChanged(tokens);
491 } catch (RemoteException e) {
492 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
493 e);
494 mSessionsListeners.remove(i);
495 }
496 }
497 }
498 }
499 }
500
RoboErik19c95182014-06-23 15:38:48 -0700501 private void pushRemoteVolumeUpdateLocked(int userId) {
502 if (mRvc != null) {
503 try {
504 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
505 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
506 } catch (RemoteException e) {
507 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
508 }
509 }
510 }
511
RoboErikb214efb2014-07-24 13:20:30 -0700512 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
513 PendingIntent receiver = record.getMediaButtonReceiver();
514 UserRecord user = mUserRecords.get(record.getUserId());
515 if (receiver != null && user != null) {
516 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800517 ComponentName component = receiver.getIntent().getComponent();
518 if (component != null && record.getPackageName().equals(component.getPackageName())) {
519 Settings.Secure.putStringForUser(mContentResolver,
520 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
521 record.getUserId());
522 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700523 }
524 }
525
RoboErik4646d282014-05-13 10:13:04 -0700526 /**
527 * Information about a particular user. The contents of this object is
528 * guarded by mLock.
529 */
530 final class UserRecord {
531 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700532 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800533 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700534 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800535 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700536
537 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800538 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700539 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800540 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700541 }
542
RoboErik4646d282014-05-13 10:13:04 -0700543 public void destroyLocked() {
544 for (int i = mSessions.size() - 1; i >= 0; i--) {
545 MediaSessionRecord session = mSessions.get(i);
546 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700547 }
548 }
549
RoboErik4646d282014-05-13 10:13:04 -0700550 public ArrayList<MediaSessionRecord> getSessionsLocked() {
551 return mSessions;
552 }
553
554 public void addSessionLocked(MediaSessionRecord session) {
555 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700556 }
557
558 public void removeSessionLocked(MediaSessionRecord session) {
559 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700560 }
561
562 public void dumpLocked(PrintWriter pw, String prefix) {
563 pw.println(prefix + "Record for user " + mUserId);
564 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700565 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800566 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jeff Brown01a500e2014-07-10 22:50:50 -0700567 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700568 pw.println(indent + size + " Sessions:");
569 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700570 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700571 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700572 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700573 }
574 }
RoboErikc8f92d12015-01-05 16:48:07 -0800575
576 private void restoreMediaButtonReceiver() {
577 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
Jaewan Kim8f729082016-06-21 12:36:26 +0900578 Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
RoboErikc8f92d12015-01-05 16:48:07 -0800579 if (!TextUtils.isEmpty(receiverName)) {
580 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
581 if (eventReceiver == null) {
582 // an invalid name was persisted
583 return;
584 }
585 mRestoredMediaButtonReceiver = eventReceiver;
586 }
587 }
RoboErik4646d282014-05-13 10:13:04 -0700588 }
589
RoboErik2e7a9162014-06-04 16:53:45 -0700590 final class SessionsListenerRecord implements IBinder.DeathRecipient {
591 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700592 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700593 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700594 private final int mPid;
595 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700596
RoboErik7aef77b2014-08-08 15:56:54 -0700597 public SessionsListenerRecord(IActiveSessionsListener listener,
598 ComponentName componentName,
599 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700600 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700601 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700602 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700603 mPid = pid;
604 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700605 }
606
607 @Override
608 public void binderDied() {
609 synchronized (mLock) {
610 mSessionsListeners.remove(this);
611 }
612 }
613 }
614
RoboErik7aef77b2014-08-08 15:56:54 -0700615 final class SettingsObserver extends ContentObserver {
616 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
617 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
618
619 private SettingsObserver() {
620 super(null);
621 }
622
623 private void observe() {
624 mContentResolver.registerContentObserver(mSecureSettingsUri,
625 false, this, UserHandle.USER_ALL);
626 }
627
628 @Override
629 public void onChange(boolean selfChange, Uri uri) {
630 updateActiveSessionListeners();
631 }
632 }
633
RoboErik07c70772014-03-20 13:33:52 -0700634 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700635 private static final String EXTRA_WAKELOCK_ACQUIRED =
636 "android.media.AudioService.WAKELOCK_ACQUIRED";
637 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
638
RoboErik9a9d0b52014-05-20 14:53:39 -0700639 private boolean mVoiceButtonDown = false;
640 private boolean mVoiceButtonHandled = false;
641
RoboErik07c70772014-03-20 13:33:52 -0700642 @Override
RoboErika5b02322014-05-07 17:05:49 -0700643 public ISession createSession(String packageName, ISessionCallback cb, String tag,
644 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800645 final int pid = Binder.getCallingPid();
646 final int uid = Binder.getCallingUid();
647 final long token = Binder.clearCallingIdentity();
648 try {
649 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700650 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
651 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800652 if (cb == null) {
653 throw new IllegalArgumentException("Controller callback cannot be null");
654 }
RoboErika5b02322014-05-07 17:05:49 -0700655 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
656 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700657 } finally {
658 Binder.restoreCallingIdentity(token);
659 }
660 }
661
662 @Override
RoboErika5b02322014-05-07 17:05:49 -0700663 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700664 final int pid = Binder.getCallingPid();
665 final int uid = Binder.getCallingUid();
666 final long token = Binder.clearCallingIdentity();
667
668 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700669 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700670 ArrayList<IBinder> binders = new ArrayList<IBinder>();
671 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700672 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700673 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700674 int size = records.size();
675 for (int i = 0; i < size; i++) {
676 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700677 }
678 }
679 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800680 } finally {
681 Binder.restoreCallingIdentity(token);
682 }
683 }
RoboErika278ea72014-04-24 14:49:01 -0700684
RoboErik2e7a9162014-06-04 16:53:45 -0700685 @Override
686 public void addSessionsListener(IActiveSessionsListener listener,
687 ComponentName componentName, int userId) throws RemoteException {
688 final int pid = Binder.getCallingPid();
689 final int uid = Binder.getCallingUid();
690 final long token = Binder.clearCallingIdentity();
691
692 try {
693 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
694 synchronized (mLock) {
695 int index = findIndexOfSessionsListenerLocked(listener);
696 if (index != -1) {
697 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
698 return;
699 }
700 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700701 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700702 try {
703 listener.asBinder().linkToDeath(record, 0);
704 } catch (RemoteException e) {
705 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
706 return;
707 }
708 mSessionsListeners.add(record);
709 }
710 } finally {
711 Binder.restoreCallingIdentity(token);
712 }
713 }
714
715 @Override
716 public void removeSessionsListener(IActiveSessionsListener listener)
717 throws RemoteException {
718 synchronized (mLock) {
719 int index = findIndexOfSessionsListenerLocked(listener);
720 if (index != -1) {
721 SessionsListenerRecord record = mSessionsListeners.remove(index);
722 try {
723 record.mListener.asBinder().unlinkToDeath(record, 0);
724 } catch (Exception e) {
725 // ignore exceptions, the record is being removed
726 }
727 }
728 }
729 }
730
RoboErik8a2cfc32014-05-16 11:19:38 -0700731 /**
732 * Handles the dispatching of the media button events to one of the
733 * registered listeners, or if there was none, broadcast an
734 * ACTION_MEDIA_BUTTON intent to the rest of the system.
735 *
736 * @param keyEvent a non-null KeyEvent whose key code is one of the
737 * supported media buttons
738 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
739 * while this key event is dispatched.
740 */
741 @Override
742 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
743 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
744 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
745 return;
746 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700747
RoboErik8a2cfc32014-05-16 11:19:38 -0700748 final int pid = Binder.getCallingPid();
749 final int uid = Binder.getCallingUid();
750 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700751 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700752 if (DEBUG) {
753 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
754 + keyEvent);
755 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700756 if (!isUserSetupComplete()) {
757 // Global media key handling can have the side-effect of starting new
758 // activities which is undesirable while setup is in progress.
759 Slog.i(TAG, "Not dispatching media key event because user "
760 + "setup is in progress.");
761 return;
762 }
763
RoboErik8a2cfc32014-05-16 11:19:38 -0700764 synchronized (mLock) {
RoboErik870c5a62014-12-02 15:08:26 -0800765 // If we don't have a media button receiver to fall back on
766 // include non-playing sessions for dispatching
Jaewan Kim8f729082016-06-21 12:36:26 +0900767 boolean useNotPlayingSessions = true;
768 for (int userId : mCurrentUserIdList) {
769 UserRecord ur = mUserRecords.get(userId);
770 if (ur.mLastMediaButtonReceiver != null
771 || ur.mRestoredMediaButtonReceiver != null) {
772 useNotPlayingSessions = false;
773 break;
774 }
775 }
776
777 if (DEBUG) {
778 Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
779 + useNotPlayingSessions);
780 }
781 MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
782 mCurrentUserIdList, useNotPlayingSessions);
RoboErik9a9d0b52014-05-20 14:53:39 -0700783 if (isVoiceKey(keyEvent.getKeyCode())) {
784 handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700785 } else {
RoboErik9a9d0b52014-05-20 14:53:39 -0700786 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700787 }
788 }
789 } finally {
790 Binder.restoreCallingIdentity(token);
791 }
792 }
793
RoboErika278ea72014-04-24 14:49:01 -0700794 @Override
RoboErik7c82ced2014-12-04 17:39:08 -0800795 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -0700796 final long token = Binder.clearCallingIdentity();
797 try {
798 synchronized (mLock) {
799 MediaSessionRecord session = mPriorityStack
Jaewan Kim8f729082016-06-21 12:36:26 +0900800 .getDefaultVolumeSession(mCurrentUserIdList);
RoboErik1ff5b162014-07-15 17:23:18 -0700801 dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
RoboErikb69ffd42014-05-30 14:57:59 -0700802 }
803 } finally {
804 Binder.restoreCallingIdentity(token);
805 }
806 }
807
808 @Override
RoboErik19c95182014-06-23 15:38:48 -0700809 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
810 final int pid = Binder.getCallingPid();
811 final int uid = Binder.getCallingUid();
812 final long token = Binder.clearCallingIdentity();
813 try {
John Spurlockeb69e242015-02-17 17:15:04 -0500814 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -0700815 mRvc = rvc;
816 } finally {
817 Binder.restoreCallingIdentity(token);
818 }
819 }
820
821 @Override
RoboErikde9ba392014-09-26 12:51:01 -0700822 public boolean isGlobalPriorityActive() {
823 return mPriorityStack.isGlobalPriorityActive();
824 }
825
826 @Override
RoboErika278ea72014-04-24 14:49:01 -0700827 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
828 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
829 != PackageManager.PERMISSION_GRANTED) {
830 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
831 + Binder.getCallingPid()
832 + ", uid=" + Binder.getCallingUid());
833 return;
834 }
835
836 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
837 pw.println();
838
839 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -0800840 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErik4646d282014-05-13 10:13:04 -0700841 int count = mAllSessions.size();
RoboErika8f95142014-05-05 14:23:49 -0700842 pw.println(count + " Sessions:");
RoboErika278ea72014-04-24 14:49:01 -0700843 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -0700844 mAllSessions.get(i).dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700845 pw.println();
RoboErika278ea72014-04-24 14:49:01 -0700846 }
RoboErika5b02322014-05-07 17:05:49 -0700847 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -0700848
RoboErik4646d282014-05-13 10:13:04 -0700849 pw.println("User Records:");
850 count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -0700851 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -0800852 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -0700853 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700854 }
855 }
856 }
RoboErik8a2cfc32014-05-16 11:19:38 -0700857
RoboErik2e7a9162014-06-04 16:53:45 -0700858 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
859 final int uid) {
860 String packageName = null;
861 if (componentName != null) {
862 // If they gave us a component name verify they own the
863 // package
864 packageName = componentName.getPackageName();
865 enforcePackageName(packageName, uid);
866 }
867 // Check that they can make calls on behalf of the user and
868 // get the final user id
869 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
870 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
871 // Check if they have the permissions or their component is
872 // enabled for the user they're calling from.
873 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
874 return resolvedUserId;
875 }
876
RoboErik1ff5b162014-07-15 17:23:18 -0700877 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
RoboErikb69ffd42014-05-30 14:57:59 -0700878 MediaSessionRecord session) {
RoboErikb69ffd42014-05-30 14:57:59 -0700879 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700880 String description = session == null ? null : session.toString();
881 Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
882 + flags + ", suggestedStream=" + suggestedStream);
RoboErikb69ffd42014-05-30 14:57:59 -0700883
884 }
RoboErik9c785402014-11-11 16:52:26 -0800885 boolean preferSuggestedStream = false;
886 if (isValidLocalStreamType(suggestedStream)
887 && AudioSystem.isStreamActive(suggestedStream, 0)) {
888 preferSuggestedStream = true;
889 }
890 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -0700891 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
892 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -0700893 if (DEBUG) {
894 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -0700895 }
RoboErikb7c014c2014-07-22 15:58:22 -0700896 return;
RoboErik3c45c292014-07-08 16:47:31 -0700897 }
RoboErik0791e172014-06-08 10:52:32 -0700898 try {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800899 String packageName = getContext().getOpPackageName();
John Spurlockee5ad722015-03-03 16:17:21 -0500900 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
John Spurlock90874332015-03-10 16:00:54 -0400901 flags, packageName, TAG);
RoboErik0791e172014-06-08 10:52:32 -0700902 } catch (RemoteException e) {
903 Log.e(TAG, "Error adjusting default volume.", e);
RoboErikb69ffd42014-05-30 14:57:59 -0700904 }
905 } else {
RoboErik0dac35a2014-08-12 15:48:49 -0700906 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +0900907 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -0700908 }
909 }
910
RoboErik9a9d0b52014-05-20 14:53:39 -0700911 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
912 MediaSessionRecord session) {
913 if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
914 // If the phone app has priority just give it the event
915 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
916 return;
917 }
918 int action = keyEvent.getAction();
919 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
920 if (action == KeyEvent.ACTION_DOWN) {
921 if (keyEvent.getRepeatCount() == 0) {
922 mVoiceButtonDown = true;
923 mVoiceButtonHandled = false;
924 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
925 mVoiceButtonHandled = true;
926 startVoiceInput(needWakeLock);
927 }
928 } else if (action == KeyEvent.ACTION_UP) {
929 if (mVoiceButtonDown) {
930 mVoiceButtonDown = false;
931 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
932 // Resend the down then send this event through
933 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
934 dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
935 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
936 }
937 }
938 }
939 }
940
941 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
942 MediaSessionRecord session) {
943 if (session != null) {
944 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700945 Log.d(TAG, "Sending media key to " + session.toString());
RoboErik9a9d0b52014-05-20 14:53:39 -0700946 }
947 if (needWakeLock) {
948 mKeyEventReceiver.aquireWakeLockLocked();
949 }
950 // If we don't need a wakelock use -1 as the id so we
951 // won't release it later
952 session.sendMediaButton(keyEvent,
953 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +0900954 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +0900955 getContext().getPackageName());
RoboErik9a9d0b52014-05-20 14:53:39 -0700956 } else {
RoboErikb214efb2014-07-24 13:20:30 -0700957 // Launch the last PendingIntent we had with priority
Jaewan Kim8f729082016-06-21 12:36:26 +0900958 for (int userId : mCurrentUserIdList) {
959 UserRecord user = mUserRecords.get(userId);
960 if (user.mLastMediaButtonReceiver == null
961 && user.mRestoredMediaButtonReceiver == null) {
962 continue;
963 }
RoboErikb214efb2014-07-24 13:20:30 -0700964 if (DEBUG) {
RoboErikc8f92d12015-01-05 16:48:07 -0800965 Log.d(TAG, "Sending media key to last known PendingIntent "
966 + user.mLastMediaButtonReceiver + " or restored Intent "
967 + user.mRestoredMediaButtonReceiver);
RoboErikb214efb2014-07-24 13:20:30 -0700968 }
969 if (needWakeLock) {
970 mKeyEventReceiver.aquireWakeLockLocked();
971 }
972 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Insun Kang2054db32016-04-07 15:34:34 +0900973 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RoboErikb214efb2014-07-24 13:20:30 -0700974 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
975 try {
RoboErikc8f92d12015-01-05 16:48:07 -0800976 if (user.mLastMediaButtonReceiver != null) {
977 user.mLastMediaButtonReceiver.send(getContext(),
978 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
riddle_hsu02ed0122015-10-20 16:00:15 +0800979 mediaButtonIntent, mKeyEventReceiver, mHandler);
RoboErikc8f92d12015-01-05 16:48:07 -0800980 } else {
981 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
982 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim8f729082016-06-21 12:36:26 +0900983 UserHandle.of(userId));
RoboErikc8f92d12015-01-05 16:48:07 -0800984 }
RoboErikb214efb2014-07-24 13:20:30 -0700985 } catch (CanceledException e) {
986 Log.i(TAG, "Error sending key event to media button receiver "
987 + user.mLastMediaButtonReceiver, e);
988 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900989 return;
RoboErik9a9d0b52014-05-20 14:53:39 -0700990 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900991 if (DEBUG) {
992 Log.d(TAG, "Sending media key ordered broadcast");
993 }
994 if (needWakeLock) {
995 mMediaEventWakeLock.acquire();
996 }
997 // Fallback to legacy behavior
998 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
999 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1000 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1001 if (needWakeLock) {
1002 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1003 }
1004 // Send broadcast only to the full user.
1005 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1006 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001007 }
1008 }
1009
1010 private void startVoiceInput(boolean needWakeLock) {
1011 Intent voiceIntent = null;
1012 // select which type of search to launch:
1013 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1014 // - device locked or screen off: action is
1015 // ACTION_VOICE_SEARCH_HANDS_FREE
1016 // with EXTRA_SECURE set to true if the device is securely locked
1017 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1018 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1019 if (!isLocked && pm.isScreenOn()) {
1020 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1021 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1022 } else {
1023 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1024 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1025 isLocked && mKeyguardManager.isKeyguardSecure());
1026 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1027 }
1028 // start the search activity
1029 if (needWakeLock) {
1030 mMediaEventWakeLock.acquire();
1031 }
1032 try {
1033 if (voiceIntent != null) {
1034 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1035 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001036 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001037 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1038 }
1039 } catch (ActivityNotFoundException e) {
1040 Log.w(TAG, "No activity for search: " + e);
1041 } finally {
1042 if (needWakeLock) {
1043 mMediaEventWakeLock.release();
1044 }
1045 }
1046 }
1047
1048 private boolean isVoiceKey(int keyCode) {
1049 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1050 }
1051
Jeff Brown38d3feb2015-03-19 18:26:30 -07001052 private boolean isUserSetupComplete() {
1053 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1054 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1055 }
1056
RoboErik9c785402014-11-11 16:52:26 -08001057 // we only handle public stream types, which are 0-5
1058 private boolean isValidLocalStreamType(int streamType) {
1059 return streamType >= AudioManager.STREAM_VOICE_CALL
1060 && streamType <= AudioManager.STREAM_NOTIFICATION;
1061 }
1062
RoboErik418c10c2014-05-19 09:25:25 -07001063 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1064
RoboErikb214efb2014-07-24 13:20:30 -07001065 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1066 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001067 private final Handler mHandler;
1068 private int mRefCount = 0;
1069 private int mLastTimeoutId = 0;
1070
1071 public KeyEventWakeLockReceiver(Handler handler) {
1072 super(handler);
1073 mHandler = handler;
1074 }
1075
1076 public void onTimeout() {
1077 synchronized (mLock) {
1078 if (mRefCount == 0) {
1079 // We've already released it, so just return
1080 return;
1081 }
1082 mLastTimeoutId++;
1083 mRefCount = 0;
1084 releaseWakeLockLocked();
1085 }
1086 }
1087
1088 public void aquireWakeLockLocked() {
1089 if (mRefCount == 0) {
1090 mMediaEventWakeLock.acquire();
1091 }
1092 mRefCount++;
1093 mHandler.removeCallbacks(this);
1094 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1095
1096 }
1097
1098 @Override
1099 public void run() {
1100 onTimeout();
1101 }
1102
RoboErik8a2cfc32014-05-16 11:19:38 -07001103 @Override
1104 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001105 if (resultCode < mLastTimeoutId) {
1106 // Ignore results from calls that were before the last
1107 // timeout, just in case.
1108 return;
1109 } else {
1110 synchronized (mLock) {
1111 if (mRefCount > 0) {
1112 mRefCount--;
1113 if (mRefCount == 0) {
1114 releaseWakeLockLocked();
1115 }
1116 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001117 }
1118 }
1119 }
RoboErik418c10c2014-05-19 09:25:25 -07001120
1121 private void releaseWakeLockLocked() {
1122 mMediaEventWakeLock.release();
1123 mHandler.removeCallbacks(this);
1124 }
RoboErikb214efb2014-07-24 13:20:30 -07001125
1126 @Override
1127 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1128 String resultData, Bundle resultExtras) {
1129 onReceiveResult(resultCode, null);
1130 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001131 };
1132
1133 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1134 @Override
1135 public void onReceive(Context context, Intent intent) {
1136 if (intent == null) {
1137 return;
1138 }
1139 Bundle extras = intent.getExtras();
1140 if (extras == null) {
1141 return;
1142 }
1143 synchronized (mLock) {
1144 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1145 && mMediaEventWakeLock.isHeld()) {
1146 mMediaEventWakeLock.release();
1147 }
1148 }
1149 }
1150 };
RoboErik01fe6612014-02-13 14:19:04 -08001151 }
1152
RoboErik2e7a9162014-06-04 16:53:45 -07001153 final class MessageHandler extends Handler {
1154 private static final int MSG_SESSIONS_CHANGED = 1;
1155
1156 @Override
1157 public void handleMessage(Message msg) {
1158 switch (msg.what) {
1159 case MSG_SESSIONS_CHANGED:
1160 pushSessionsChanged(msg.arg1);
1161 break;
1162 }
1163 }
1164
1165 public void post(int what, int arg1, int arg2) {
1166 obtainMessage(what, arg1, arg2).sendToTarget();
1167 }
1168 }
RoboErik01fe6612014-02-13 14:19:04 -08001169}