blob: 777eee81473a4fa5bd62fda690f2a2309ffc3d85 [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();
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700242 // Include all profiles even though they aren't yet enabled to handle work profile case.
243 int[] userIds = manager.getProfileIdsWithDisabled(currentUser);
Jaewan Kim8f729082016-06-21 12:36:26 +0900244 mCurrentUserIdList.clear();
245 if (userIds != null && userIds.length > 0) {
246 for (int userId : userIds) {
247 mCurrentUserIdList.add(userId);
RoboErik4646d282014-05-13 10:13:04 -0700248 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900249 } else {
250 // This shouldn't happen.
251 Log.w(TAG, "Failed to get enabled profiles.");
252 mCurrentUserIdList.add(currentUser);
253 }
254 for (int userId : mCurrentUserIdList) {
255 if (mUserRecords.get(userId) == null) {
256 mUserRecords.put(userId, new UserRecord(getContext(), userId));
257 }
RoboErik4646d282014-05-13 10:13:04 -0700258 }
259 }
260 }
261
RoboErik7aef77b2014-08-08 15:56:54 -0700262 private void updateActiveSessionListeners() {
263 synchronized (mLock) {
264 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
265 SessionsListenerRecord listener = mSessionsListeners.get(i);
266 try {
267 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
268 listener.mUserId);
269 } catch (SecurityException e) {
270 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
271 + " is no longer authorized. Disconnecting.");
272 mSessionsListeners.remove(i);
273 try {
274 listener.mListener
275 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
276 } catch (Exception e1) {
277 // ignore
278 }
279 }
280 }
281 }
282 }
283
RoboErik4646d282014-05-13 10:13:04 -0700284 /**
285 * Stop the user and unbind from everything.
286 *
287 * @param user The user to dispose of
288 */
289 private void destroyUserLocked(UserRecord user) {
RoboErik4646d282014-05-13 10:13:04 -0700290 user.destroyLocked();
291 mUserRecords.remove(user.mUserId);
292 }
293
294 /*
295 * When a session is removed several things need to happen.
296 * 1. We need to remove it from the relevant user.
297 * 2. We need to remove it from the priority stack.
298 * 3. We need to remove it from all sessions.
299 * 4. If this is the system priority session we need to clear it.
300 * 5. We need to unlink to death from the cb binder
301 * 6. We need to tell the session to do any final cleanup (onDestroy)
302 */
RoboErik01fe6612014-02-13 14:19:04 -0800303 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900304 if (DEBUG) {
305 Log.d(TAG, "Destroying session : " + session.toString());
306 }
RoboErik4646d282014-05-13 10:13:04 -0700307 int userId = session.getUserId();
308 UserRecord user = mUserRecords.get(userId);
309 if (user != null) {
310 user.removeSessionLocked(session);
311 }
312
RoboErika8f95142014-05-05 14:23:49 -0700313 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700314 mAllSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700315
316 try {
317 session.getCallback().asBinder().unlinkToDeath(session, 0);
318 } catch (Exception e) {
319 // ignore exceptions while destroying a session.
320 }
321 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700322
323 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800324 }
325
326 private void enforcePackageName(String packageName, int uid) {
327 if (TextUtils.isEmpty(packageName)) {
328 throw new IllegalArgumentException("packageName may not be empty");
329 }
330 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
331 final int packageCount = packages.length;
332 for (int i = 0; i < packageCount; i++) {
333 if (packageName.equals(packages[i])) {
334 return;
335 }
336 }
337 throw new IllegalArgumentException("packageName is not owned by the calling process");
338 }
339
RoboErike7880d82014-04-30 12:48:25 -0700340 /**
341 * Checks a caller's authorization to register an IRemoteControlDisplay.
342 * Authorization is granted if one of the following is true:
343 * <ul>
344 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
345 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700346 * <li>the caller's listener is one of the enabled notification listeners
347 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700348 * </ul>
349 */
RoboErika5b02322014-05-07 17:05:49 -0700350 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
351 int resolvedUserId) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500352 if (isCurrentVolumeController(uid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700353 if (getContext()
354 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
355 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700356 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
357 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700358 throw new SecurityException("Missing permission to control media.");
359 }
360 }
361
John Spurlockbe19ed02015-02-22 10:57:55 -0500362 private boolean isCurrentVolumeController(int uid) {
John Spurlockeb69e242015-02-17 17:15:04 -0500363 if (mAudioManagerInternal != null) {
364 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
365 if (vcuid > 0 && uid == vcuid) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500366 return true;
John Spurlockeb69e242015-02-17 17:15:04 -0500367 }
368 }
John Spurlockbe19ed02015-02-22 10:57:55 -0500369 return false;
370 }
371
372 private void enforceSystemUiPermission(String action, int pid, int uid) {
373 if (isCurrentVolumeController(uid)) return;
RoboErik19c95182014-06-23 15:38:48 -0700374 if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
375 pid, uid) != PackageManager.PERMISSION_GRANTED) {
376 throw new SecurityException("Only system ui may " + action);
377 }
378 }
379
RoboErika5b02322014-05-07 17:05:49 -0700380 /**
381 * This checks if the component is an enabled notification listener for the
382 * specified user. Enabled components may only operate on behalf of the user
383 * they're running as.
384 *
385 * @param compName The component that is enabled.
386 * @param userId The user id of the caller.
387 * @param forUserId The user id they're making the request on behalf of.
388 * @return True if the component is enabled, false otherwise
389 */
390 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
391 int forUserId) {
392 if (userId != forUserId) {
393 // You may not access another user's content as an enabled listener.
394 return false;
395 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700396 if (DEBUG) {
397 Log.d(TAG, "Checking if enabled notification listener " + compName);
398 }
RoboErike7880d82014-04-30 12:48:25 -0700399 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700400 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700401 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700402 userId);
RoboErike7880d82014-04-30 12:48:25 -0700403 if (enabledNotifListeners != null) {
404 final String[] components = enabledNotifListeners.split(":");
405 for (int i = 0; i < components.length; i++) {
406 final ComponentName component =
407 ComponentName.unflattenFromString(components[i]);
408 if (component != null) {
409 if (compName.equals(component)) {
410 if (DEBUG) {
411 Log.d(TAG, "ok to get sessions: " + component +
412 " is authorized notification listener");
413 }
414 return true;
415 }
416 }
417 }
418 }
419 if (DEBUG) {
420 Log.d(TAG, "not ok to get sessions, " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700421 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700422 }
423 }
424 return false;
425 }
426
RoboErika5b02322014-05-07 17:05:49 -0700427 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700428 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800429 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700430 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800431 }
432 }
433
RoboErik4646d282014-05-13 10:13:04 -0700434 /*
435 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700436 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700437 * 2. It needs to be added to all sessions.
438 * 3. It needs to be added to the priority stack.
439 * 4. It needs to be added to the relevant user record.
440 */
RoboErika5b02322014-05-07 17:05:49 -0700441 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
442 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700443
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700444 UserRecord user = mUserRecords.get(userId);
445 if (user == null) {
446 Log.wtf(TAG, "Request from invalid user: " + userId);
447 throw new RuntimeException("Session request from invalid user.");
448 }
449
RoboErika5b02322014-05-07 17:05:49 -0700450 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
451 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800452 try {
453 cb.asBinder().linkToDeath(session, 0);
454 } catch (RemoteException e) {
455 throw new RuntimeException("Media Session owner died prematurely.", e);
456 }
RoboErik4646d282014-05-13 10:13:04 -0700457
458 mAllSessions.add(session);
Jaewan Kim8f729082016-06-21 12:36:26 +0900459 mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
RoboErik4646d282014-05-13 10:13:04 -0700460 user.addSessionLocked(session);
461
RoboErik2e7a9162014-06-04 16:53:45 -0700462 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
463
RoboErik01fe6612014-02-13 14:19:04 -0800464 if (DEBUG) {
RoboErika5b02322014-05-07 17:05:49 -0700465 Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800466 }
467 return session;
468 }
469
RoboErik2e7a9162014-06-04 16:53:45 -0700470 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
471 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800472 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700473 return i;
474 }
475 }
476 return -1;
477 }
478
RoboErik2e7a9162014-06-04 16:53:45 -0700479 private void pushSessionsChanged(int userId) {
480 synchronized (mLock) {
481 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
482 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800483 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700484 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700485 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700486 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700487 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700488 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700489 }
RoboErik19c95182014-06-23 15:38:48 -0700490 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700491 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
492 SessionsListenerRecord record = mSessionsListeners.get(i);
493 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
494 try {
495 record.mListener.onActiveSessionsChanged(tokens);
496 } catch (RemoteException e) {
497 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
498 e);
499 mSessionsListeners.remove(i);
500 }
501 }
502 }
503 }
504 }
505
RoboErik19c95182014-06-23 15:38:48 -0700506 private void pushRemoteVolumeUpdateLocked(int userId) {
507 if (mRvc != null) {
508 try {
509 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
510 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
511 } catch (RemoteException e) {
512 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
513 }
514 }
515 }
516
RoboErikb214efb2014-07-24 13:20:30 -0700517 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
518 PendingIntent receiver = record.getMediaButtonReceiver();
519 UserRecord user = mUserRecords.get(record.getUserId());
520 if (receiver != null && user != null) {
521 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800522 ComponentName component = receiver.getIntent().getComponent();
523 if (component != null && record.getPackageName().equals(component.getPackageName())) {
524 Settings.Secure.putStringForUser(mContentResolver,
525 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
526 record.getUserId());
527 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700528 }
529 }
530
RoboErik4646d282014-05-13 10:13:04 -0700531 /**
532 * Information about a particular user. The contents of this object is
533 * guarded by mLock.
534 */
535 final class UserRecord {
536 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700537 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800538 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700539 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800540 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700541
542 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800543 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700544 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800545 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700546 }
547
RoboErik4646d282014-05-13 10:13:04 -0700548 public void destroyLocked() {
549 for (int i = mSessions.size() - 1; i >= 0; i--) {
550 MediaSessionRecord session = mSessions.get(i);
551 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700552 }
553 }
554
RoboErik4646d282014-05-13 10:13:04 -0700555 public ArrayList<MediaSessionRecord> getSessionsLocked() {
556 return mSessions;
557 }
558
559 public void addSessionLocked(MediaSessionRecord session) {
560 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700561 }
562
563 public void removeSessionLocked(MediaSessionRecord session) {
564 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700565 }
566
567 public void dumpLocked(PrintWriter pw, String prefix) {
568 pw.println(prefix + "Record for user " + mUserId);
569 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700570 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800571 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jeff Brown01a500e2014-07-10 22:50:50 -0700572 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700573 pw.println(indent + size + " Sessions:");
574 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700575 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700576 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700577 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700578 }
579 }
RoboErikc8f92d12015-01-05 16:48:07 -0800580
581 private void restoreMediaButtonReceiver() {
582 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
Jaewan Kim8f729082016-06-21 12:36:26 +0900583 Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
RoboErikc8f92d12015-01-05 16:48:07 -0800584 if (!TextUtils.isEmpty(receiverName)) {
585 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
586 if (eventReceiver == null) {
587 // an invalid name was persisted
588 return;
589 }
590 mRestoredMediaButtonReceiver = eventReceiver;
591 }
592 }
RoboErik4646d282014-05-13 10:13:04 -0700593 }
594
RoboErik2e7a9162014-06-04 16:53:45 -0700595 final class SessionsListenerRecord implements IBinder.DeathRecipient {
596 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700597 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700598 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700599 private final int mPid;
600 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700601
RoboErik7aef77b2014-08-08 15:56:54 -0700602 public SessionsListenerRecord(IActiveSessionsListener listener,
603 ComponentName componentName,
604 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700605 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700606 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700607 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700608 mPid = pid;
609 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700610 }
611
612 @Override
613 public void binderDied() {
614 synchronized (mLock) {
615 mSessionsListeners.remove(this);
616 }
617 }
618 }
619
RoboErik7aef77b2014-08-08 15:56:54 -0700620 final class SettingsObserver extends ContentObserver {
621 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
622 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
623
624 private SettingsObserver() {
625 super(null);
626 }
627
628 private void observe() {
629 mContentResolver.registerContentObserver(mSecureSettingsUri,
630 false, this, UserHandle.USER_ALL);
631 }
632
633 @Override
634 public void onChange(boolean selfChange, Uri uri) {
635 updateActiveSessionListeners();
636 }
637 }
638
RoboErik07c70772014-03-20 13:33:52 -0700639 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700640 private static final String EXTRA_WAKELOCK_ACQUIRED =
641 "android.media.AudioService.WAKELOCK_ACQUIRED";
642 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
643
RoboErik9a9d0b52014-05-20 14:53:39 -0700644 private boolean mVoiceButtonDown = false;
645 private boolean mVoiceButtonHandled = false;
646
RoboErik07c70772014-03-20 13:33:52 -0700647 @Override
RoboErika5b02322014-05-07 17:05:49 -0700648 public ISession createSession(String packageName, ISessionCallback cb, String tag,
649 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800650 final int pid = Binder.getCallingPid();
651 final int uid = Binder.getCallingUid();
652 final long token = Binder.clearCallingIdentity();
653 try {
654 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700655 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
656 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800657 if (cb == null) {
658 throw new IllegalArgumentException("Controller callback cannot be null");
659 }
RoboErika5b02322014-05-07 17:05:49 -0700660 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
661 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700662 } finally {
663 Binder.restoreCallingIdentity(token);
664 }
665 }
666
667 @Override
RoboErika5b02322014-05-07 17:05:49 -0700668 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700669 final int pid = Binder.getCallingPid();
670 final int uid = Binder.getCallingUid();
671 final long token = Binder.clearCallingIdentity();
672
673 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700674 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700675 ArrayList<IBinder> binders = new ArrayList<IBinder>();
676 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700677 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700678 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700679 int size = records.size();
680 for (int i = 0; i < size; i++) {
681 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700682 }
683 }
684 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800685 } finally {
686 Binder.restoreCallingIdentity(token);
687 }
688 }
RoboErika278ea72014-04-24 14:49:01 -0700689
RoboErik2e7a9162014-06-04 16:53:45 -0700690 @Override
691 public void addSessionsListener(IActiveSessionsListener listener,
692 ComponentName componentName, int userId) throws RemoteException {
693 final int pid = Binder.getCallingPid();
694 final int uid = Binder.getCallingUid();
695 final long token = Binder.clearCallingIdentity();
696
697 try {
698 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
699 synchronized (mLock) {
700 int index = findIndexOfSessionsListenerLocked(listener);
701 if (index != -1) {
702 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
703 return;
704 }
705 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700706 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700707 try {
708 listener.asBinder().linkToDeath(record, 0);
709 } catch (RemoteException e) {
710 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
711 return;
712 }
713 mSessionsListeners.add(record);
714 }
715 } finally {
716 Binder.restoreCallingIdentity(token);
717 }
718 }
719
720 @Override
721 public void removeSessionsListener(IActiveSessionsListener listener)
722 throws RemoteException {
723 synchronized (mLock) {
724 int index = findIndexOfSessionsListenerLocked(listener);
725 if (index != -1) {
726 SessionsListenerRecord record = mSessionsListeners.remove(index);
727 try {
728 record.mListener.asBinder().unlinkToDeath(record, 0);
729 } catch (Exception e) {
730 // ignore exceptions, the record is being removed
731 }
732 }
733 }
734 }
735
RoboErik8a2cfc32014-05-16 11:19:38 -0700736 /**
737 * Handles the dispatching of the media button events to one of the
738 * registered listeners, or if there was none, broadcast an
739 * ACTION_MEDIA_BUTTON intent to the rest of the system.
740 *
741 * @param keyEvent a non-null KeyEvent whose key code is one of the
742 * supported media buttons
743 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
744 * while this key event is dispatched.
745 */
746 @Override
747 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
748 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
749 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
750 return;
751 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700752
RoboErik8a2cfc32014-05-16 11:19:38 -0700753 final int pid = Binder.getCallingPid();
754 final int uid = Binder.getCallingUid();
755 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700756 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700757 if (DEBUG) {
758 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
759 + keyEvent);
760 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700761 if (!isUserSetupComplete()) {
762 // Global media key handling can have the side-effect of starting new
763 // activities which is undesirable while setup is in progress.
764 Slog.i(TAG, "Not dispatching media key event because user "
765 + "setup is in progress.");
766 return;
767 }
768
RoboErik8a2cfc32014-05-16 11:19:38 -0700769 synchronized (mLock) {
RoboErik870c5a62014-12-02 15:08:26 -0800770 // If we don't have a media button receiver to fall back on
771 // include non-playing sessions for dispatching
Jaewan Kim8f729082016-06-21 12:36:26 +0900772 boolean useNotPlayingSessions = true;
773 for (int userId : mCurrentUserIdList) {
774 UserRecord ur = mUserRecords.get(userId);
775 if (ur.mLastMediaButtonReceiver != null
776 || ur.mRestoredMediaButtonReceiver != null) {
777 useNotPlayingSessions = false;
778 break;
779 }
780 }
781
782 if (DEBUG) {
783 Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
784 + useNotPlayingSessions);
785 }
786 MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
787 mCurrentUserIdList, useNotPlayingSessions);
RoboErik9a9d0b52014-05-20 14:53:39 -0700788 if (isVoiceKey(keyEvent.getKeyCode())) {
789 handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700790 } else {
RoboErik9a9d0b52014-05-20 14:53:39 -0700791 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700792 }
793 }
794 } finally {
795 Binder.restoreCallingIdentity(token);
796 }
797 }
798
RoboErika278ea72014-04-24 14:49:01 -0700799 @Override
RoboErik7c82ced2014-12-04 17:39:08 -0800800 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -0700801 final long token = Binder.clearCallingIdentity();
802 try {
803 synchronized (mLock) {
804 MediaSessionRecord session = mPriorityStack
Jaewan Kim8f729082016-06-21 12:36:26 +0900805 .getDefaultVolumeSession(mCurrentUserIdList);
RoboErik1ff5b162014-07-15 17:23:18 -0700806 dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
RoboErikb69ffd42014-05-30 14:57:59 -0700807 }
808 } finally {
809 Binder.restoreCallingIdentity(token);
810 }
811 }
812
813 @Override
RoboErik19c95182014-06-23 15:38:48 -0700814 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
815 final int pid = Binder.getCallingPid();
816 final int uid = Binder.getCallingUid();
817 final long token = Binder.clearCallingIdentity();
818 try {
John Spurlockeb69e242015-02-17 17:15:04 -0500819 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -0700820 mRvc = rvc;
821 } finally {
822 Binder.restoreCallingIdentity(token);
823 }
824 }
825
826 @Override
RoboErikde9ba392014-09-26 12:51:01 -0700827 public boolean isGlobalPriorityActive() {
828 return mPriorityStack.isGlobalPriorityActive();
829 }
830
831 @Override
RoboErika278ea72014-04-24 14:49:01 -0700832 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
833 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
834 != PackageManager.PERMISSION_GRANTED) {
835 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
836 + Binder.getCallingPid()
837 + ", uid=" + Binder.getCallingUid());
838 return;
839 }
840
841 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
842 pw.println();
843
844 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -0800845 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErik4646d282014-05-13 10:13:04 -0700846 int count = mAllSessions.size();
RoboErika8f95142014-05-05 14:23:49 -0700847 pw.println(count + " Sessions:");
RoboErika278ea72014-04-24 14:49:01 -0700848 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -0700849 mAllSessions.get(i).dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700850 pw.println();
RoboErika278ea72014-04-24 14:49:01 -0700851 }
RoboErika5b02322014-05-07 17:05:49 -0700852 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -0700853
RoboErik4646d282014-05-13 10:13:04 -0700854 pw.println("User Records:");
855 count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -0700856 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -0800857 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -0700858 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700859 }
860 }
861 }
RoboErik8a2cfc32014-05-16 11:19:38 -0700862
RoboErik2e7a9162014-06-04 16:53:45 -0700863 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
864 final int uid) {
865 String packageName = null;
866 if (componentName != null) {
867 // If they gave us a component name verify they own the
868 // package
869 packageName = componentName.getPackageName();
870 enforcePackageName(packageName, uid);
871 }
872 // Check that they can make calls on behalf of the user and
873 // get the final user id
874 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
875 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
876 // Check if they have the permissions or their component is
877 // enabled for the user they're calling from.
878 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
879 return resolvedUserId;
880 }
881
RoboErik1ff5b162014-07-15 17:23:18 -0700882 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
RoboErikb69ffd42014-05-30 14:57:59 -0700883 MediaSessionRecord session) {
RoboErikb69ffd42014-05-30 14:57:59 -0700884 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700885 String description = session == null ? null : session.toString();
886 Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
887 + flags + ", suggestedStream=" + suggestedStream);
RoboErikb69ffd42014-05-30 14:57:59 -0700888
889 }
RoboErik9c785402014-11-11 16:52:26 -0800890 boolean preferSuggestedStream = false;
891 if (isValidLocalStreamType(suggestedStream)
892 && AudioSystem.isStreamActive(suggestedStream, 0)) {
893 preferSuggestedStream = true;
894 }
895 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -0700896 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
897 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -0700898 if (DEBUG) {
899 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -0700900 }
RoboErikb7c014c2014-07-22 15:58:22 -0700901 return;
RoboErik3c45c292014-07-08 16:47:31 -0700902 }
RoboErik0791e172014-06-08 10:52:32 -0700903 try {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800904 String packageName = getContext().getOpPackageName();
John Spurlockee5ad722015-03-03 16:17:21 -0500905 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
John Spurlock90874332015-03-10 16:00:54 -0400906 flags, packageName, TAG);
RoboErik0791e172014-06-08 10:52:32 -0700907 } catch (RemoteException e) {
908 Log.e(TAG, "Error adjusting default volume.", e);
RoboErikb69ffd42014-05-30 14:57:59 -0700909 }
910 } else {
RoboErik0dac35a2014-08-12 15:48:49 -0700911 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +0900912 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -0700913 }
914 }
915
RoboErik9a9d0b52014-05-20 14:53:39 -0700916 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
917 MediaSessionRecord session) {
918 if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
919 // If the phone app has priority just give it the event
920 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
921 return;
922 }
923 int action = keyEvent.getAction();
924 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
925 if (action == KeyEvent.ACTION_DOWN) {
926 if (keyEvent.getRepeatCount() == 0) {
927 mVoiceButtonDown = true;
928 mVoiceButtonHandled = false;
929 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
930 mVoiceButtonHandled = true;
931 startVoiceInput(needWakeLock);
932 }
933 } else if (action == KeyEvent.ACTION_UP) {
934 if (mVoiceButtonDown) {
935 mVoiceButtonDown = false;
936 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
937 // Resend the down then send this event through
938 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
939 dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
940 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
941 }
942 }
943 }
944 }
945
946 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
947 MediaSessionRecord session) {
948 if (session != null) {
949 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700950 Log.d(TAG, "Sending media key to " + session.toString());
RoboErik9a9d0b52014-05-20 14:53:39 -0700951 }
952 if (needWakeLock) {
953 mKeyEventReceiver.aquireWakeLockLocked();
954 }
955 // If we don't need a wakelock use -1 as the id so we
956 // won't release it later
957 session.sendMediaButton(keyEvent,
958 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +0900959 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +0900960 getContext().getPackageName());
RoboErik9a9d0b52014-05-20 14:53:39 -0700961 } else {
RoboErikb214efb2014-07-24 13:20:30 -0700962 // Launch the last PendingIntent we had with priority
Jaewan Kim8f729082016-06-21 12:36:26 +0900963 for (int userId : mCurrentUserIdList) {
964 UserRecord user = mUserRecords.get(userId);
965 if (user.mLastMediaButtonReceiver == null
966 && user.mRestoredMediaButtonReceiver == null) {
967 continue;
968 }
RoboErikb214efb2014-07-24 13:20:30 -0700969 if (DEBUG) {
RoboErikc8f92d12015-01-05 16:48:07 -0800970 Log.d(TAG, "Sending media key to last known PendingIntent "
971 + user.mLastMediaButtonReceiver + " or restored Intent "
972 + user.mRestoredMediaButtonReceiver);
RoboErikb214efb2014-07-24 13:20:30 -0700973 }
974 if (needWakeLock) {
975 mKeyEventReceiver.aquireWakeLockLocked();
976 }
977 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Insun Kang2054db32016-04-07 15:34:34 +0900978 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RoboErikb214efb2014-07-24 13:20:30 -0700979 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
980 try {
RoboErikc8f92d12015-01-05 16:48:07 -0800981 if (user.mLastMediaButtonReceiver != null) {
982 user.mLastMediaButtonReceiver.send(getContext(),
983 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
riddle_hsu02ed0122015-10-20 16:00:15 +0800984 mediaButtonIntent, mKeyEventReceiver, mHandler);
RoboErikc8f92d12015-01-05 16:48:07 -0800985 } else {
986 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
987 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim8f729082016-06-21 12:36:26 +0900988 UserHandle.of(userId));
RoboErikc8f92d12015-01-05 16:48:07 -0800989 }
RoboErikb214efb2014-07-24 13:20:30 -0700990 } catch (CanceledException e) {
991 Log.i(TAG, "Error sending key event to media button receiver "
992 + user.mLastMediaButtonReceiver, e);
993 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900994 return;
RoboErik9a9d0b52014-05-20 14:53:39 -0700995 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900996 if (DEBUG) {
997 Log.d(TAG, "Sending media key ordered broadcast");
998 }
999 if (needWakeLock) {
1000 mMediaEventWakeLock.acquire();
1001 }
1002 // Fallback to legacy behavior
1003 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1004 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1005 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1006 if (needWakeLock) {
1007 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1008 }
1009 // Send broadcast only to the full user.
1010 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1011 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001012 }
1013 }
1014
1015 private void startVoiceInput(boolean needWakeLock) {
1016 Intent voiceIntent = null;
1017 // select which type of search to launch:
1018 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1019 // - device locked or screen off: action is
1020 // ACTION_VOICE_SEARCH_HANDS_FREE
1021 // with EXTRA_SECURE set to true if the device is securely locked
1022 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1023 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1024 if (!isLocked && pm.isScreenOn()) {
1025 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1026 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1027 } else {
1028 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1029 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1030 isLocked && mKeyguardManager.isKeyguardSecure());
1031 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1032 }
1033 // start the search activity
1034 if (needWakeLock) {
1035 mMediaEventWakeLock.acquire();
1036 }
1037 try {
1038 if (voiceIntent != null) {
1039 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1040 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001041 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001042 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1043 }
1044 } catch (ActivityNotFoundException e) {
1045 Log.w(TAG, "No activity for search: " + e);
1046 } finally {
1047 if (needWakeLock) {
1048 mMediaEventWakeLock.release();
1049 }
1050 }
1051 }
1052
1053 private boolean isVoiceKey(int keyCode) {
1054 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1055 }
1056
Jeff Brown38d3feb2015-03-19 18:26:30 -07001057 private boolean isUserSetupComplete() {
1058 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1059 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1060 }
1061
RoboErik9c785402014-11-11 16:52:26 -08001062 // we only handle public stream types, which are 0-5
1063 private boolean isValidLocalStreamType(int streamType) {
1064 return streamType >= AudioManager.STREAM_VOICE_CALL
1065 && streamType <= AudioManager.STREAM_NOTIFICATION;
1066 }
1067
RoboErik418c10c2014-05-19 09:25:25 -07001068 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1069
RoboErikb214efb2014-07-24 13:20:30 -07001070 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1071 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001072 private final Handler mHandler;
1073 private int mRefCount = 0;
1074 private int mLastTimeoutId = 0;
1075
1076 public KeyEventWakeLockReceiver(Handler handler) {
1077 super(handler);
1078 mHandler = handler;
1079 }
1080
1081 public void onTimeout() {
1082 synchronized (mLock) {
1083 if (mRefCount == 0) {
1084 // We've already released it, so just return
1085 return;
1086 }
1087 mLastTimeoutId++;
1088 mRefCount = 0;
1089 releaseWakeLockLocked();
1090 }
1091 }
1092
1093 public void aquireWakeLockLocked() {
1094 if (mRefCount == 0) {
1095 mMediaEventWakeLock.acquire();
1096 }
1097 mRefCount++;
1098 mHandler.removeCallbacks(this);
1099 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1100
1101 }
1102
1103 @Override
1104 public void run() {
1105 onTimeout();
1106 }
1107
RoboErik8a2cfc32014-05-16 11:19:38 -07001108 @Override
1109 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001110 if (resultCode < mLastTimeoutId) {
1111 // Ignore results from calls that were before the last
1112 // timeout, just in case.
1113 return;
1114 } else {
1115 synchronized (mLock) {
1116 if (mRefCount > 0) {
1117 mRefCount--;
1118 if (mRefCount == 0) {
1119 releaseWakeLockLocked();
1120 }
1121 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001122 }
1123 }
1124 }
RoboErik418c10c2014-05-19 09:25:25 -07001125
1126 private void releaseWakeLockLocked() {
1127 mMediaEventWakeLock.release();
1128 mHandler.removeCallbacks(this);
1129 }
RoboErikb214efb2014-07-24 13:20:30 -07001130
1131 @Override
1132 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1133 String resultData, Bundle resultExtras) {
1134 onReceiveResult(resultCode, null);
1135 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001136 };
1137
1138 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1139 @Override
1140 public void onReceive(Context context, Intent intent) {
1141 if (intent == null) {
1142 return;
1143 }
1144 Bundle extras = intent.getExtras();
1145 if (extras == null) {
1146 return;
1147 }
1148 synchronized (mLock) {
1149 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1150 && mMediaEventWakeLock.isHeld()) {
1151 mMediaEventWakeLock.release();
1152 }
1153 }
1154 }
1155 };
RoboErik01fe6612014-02-13 14:19:04 -08001156 }
1157
RoboErik2e7a9162014-06-04 16:53:45 -07001158 final class MessageHandler extends Handler {
1159 private static final int MSG_SESSIONS_CHANGED = 1;
1160
1161 @Override
1162 public void handleMessage(Message msg) {
1163 switch (msg.what) {
1164 case MSG_SESSIONS_CHANGED:
1165 pushSessionsChanged(msg.arg1);
1166 break;
1167 }
1168 }
1169
1170 public void post(int what, int arg1, int arg2) {
1171 obtainMessage(what, arg1, arg2).sendToTarget();
1172 }
1173 }
RoboErik01fe6612014-02-13 14:19:04 -08001174}