blob: 34f6aa7f73ab130dfc1949f16e313f48b4cd59b4 [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);
Jaewan Kim5e1476e2016-07-19 22:25:39 +090081 // Leave log for media key event always.
82 private static final boolean DEBUG_MEDIA_KEY_EVENT = DEBUG || true;
RoboErik01fe6612014-02-13 14:19:04 -080083
RoboErik418c10c2014-05-19 09:25:25 -070084 private static final int WAKELOCK_TIMEOUT = 5000;
85
RoboErik2610d712015-01-07 11:10:23 -080086 /* package */final IBinder mICallback = new Binder();
87
RoboErik01fe6612014-02-13 14:19:04 -080088 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070089 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080090
RoboErik4646d282014-05-13 10:13:04 -070091 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
92 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070093 private final ArrayList<SessionsListenerRecord> mSessionsListeners
94 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -080095 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -070096 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -070097 private final PowerManager.WakeLock mMediaEventWakeLock;
RoboErik01fe6612014-02-13 14:19:04 -080098
RoboErik9a9d0b52014-05-20 14:53:39 -070099 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700100 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -0500101 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700102 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700103 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -0700104
Jaewan Kim8f729082016-06-21 12:36:26 +0900105 // List of user IDs running in the foreground.
106 // Multiple users can be in the foreground if the work profile is on.
107 private final List<Integer> mCurrentUserIdList = new ArrayList<>();
RoboErike7880d82014-04-30 12:48:25 -0700108
RoboErik19c95182014-06-23 15:38:48 -0700109 // Used to notify system UI when remote volume was changed. TODO find a
110 // better way to handle this.
111 private IRemoteVolumeController mRvc;
112
RoboErik01fe6612014-02-13 14:19:04 -0800113 public MediaSessionService(Context context) {
114 super(context);
115 mSessionManagerImpl = new SessionManagerImpl();
RoboErika8f95142014-05-05 14:23:49 -0700116 mPriorityStack = new MediaSessionStack();
RoboErik8a2cfc32014-05-16 11:19:38 -0700117 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
118 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
RoboErik01fe6612014-02-13 14:19:04 -0800119 }
120
121 @Override
122 public void onStart() {
123 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700124 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700125 mKeyguardManager =
126 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700127 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500128 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700129 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700130 mSettingsObserver = new SettingsObserver();
131 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800132
133 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700134 }
135
136 private IAudioService getAudioService() {
137 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
138 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700139 }
140
RoboErika8f95142014-05-05 14:23:49 -0700141 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700142 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700143 if (!mAllSessions.contains(record)) {
144 Log.d(TAG, "Unknown session updated. Ignoring.");
145 return;
146 }
RoboErika8f95142014-05-05 14:23:49 -0700147 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700148 }
RoboErik2e7a9162014-06-04 16:53:45 -0700149 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700150 }
151
RoboErik9c5b7cb2015-01-15 15:09:09 -0800152 /**
153 * Tells the system UI that volume has changed on a remote session.
154 */
155 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
156 if (mRvc == null) {
157 return;
158 }
159 try {
160 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
161 } catch (Exception e) {
162 Log.wtf(TAG, "Error sending volume change to system UI.", e);
163 }
164 }
165
RoboErika8f95142014-05-05 14:23:49 -0700166 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700167 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700168 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700169 if (!mAllSessions.contains(record)) {
170 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
171 return;
172 }
RoboErik2e7a9162014-06-04 16:53:45 -0700173 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
174 }
175 if (updateSessions) {
176 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700177 }
178 }
179
RoboErik19c95182014-06-23 15:38:48 -0700180 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
181 synchronized (mLock) {
182 if (!mAllSessions.contains(record)) {
183 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
184 return;
185 }
186 pushRemoteVolumeUpdateLocked(record.getUserId());
187 }
188 }
189
RoboErika278ea72014-04-24 14:49:01 -0700190 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900191 public void onStartUser(int userId) {
192 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700193 updateUser();
194 }
195
196 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900197 public void onSwitchUser(int userId) {
198 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700199 updateUser();
200 }
201
202 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900203 public void onStopUser(int userId) {
204 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700205 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900206 UserRecord user = mUserRecords.get(userId);
RoboErik4646d282014-05-13 10:13:04 -0700207 if (user != null) {
208 destroyUserLocked(user);
209 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900210 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700211 }
212 }
213
214 @Override
RoboErika278ea72014-04-24 14:49:01 -0700215 public void monitor() {
216 synchronized (mLock) {
217 // Check for deadlock
218 }
219 }
220
RoboErik4646d282014-05-13 10:13:04 -0700221 protected void enforcePhoneStatePermission(int pid, int uid) {
222 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
223 != PackageManager.PERMISSION_GRANTED) {
224 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
225 }
226 }
227
RoboErik01fe6612014-02-13 14:19:04 -0800228 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700229 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800230 destroySessionLocked(session);
231 }
232 }
233
234 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700235 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800236 destroySessionLocked(session);
237 }
238 }
239
RoboErik4646d282014-05-13 10:13:04 -0700240 private void updateUser() {
241 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900242 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
243 int currentUser = ActivityManager.getCurrentUser();
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700244 // Include all profiles even though they aren't yet enabled to handle work profile case.
245 int[] userIds = manager.getProfileIdsWithDisabled(currentUser);
Jaewan Kim8f729082016-06-21 12:36:26 +0900246 mCurrentUserIdList.clear();
247 if (userIds != null && userIds.length > 0) {
248 for (int userId : userIds) {
249 mCurrentUserIdList.add(userId);
RoboErik4646d282014-05-13 10:13:04 -0700250 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900251 } else {
252 // This shouldn't happen.
253 Log.w(TAG, "Failed to get enabled profiles.");
254 mCurrentUserIdList.add(currentUser);
255 }
256 for (int userId : mCurrentUserIdList) {
257 if (mUserRecords.get(userId) == null) {
258 mUserRecords.put(userId, new UserRecord(getContext(), userId));
259 }
RoboErik4646d282014-05-13 10:13:04 -0700260 }
261 }
262 }
263
RoboErik7aef77b2014-08-08 15:56:54 -0700264 private void updateActiveSessionListeners() {
265 synchronized (mLock) {
266 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
267 SessionsListenerRecord listener = mSessionsListeners.get(i);
268 try {
269 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
270 listener.mUserId);
271 } catch (SecurityException e) {
272 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
273 + " is no longer authorized. Disconnecting.");
274 mSessionsListeners.remove(i);
275 try {
276 listener.mListener
277 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
278 } catch (Exception e1) {
279 // ignore
280 }
281 }
282 }
283 }
284 }
285
RoboErik4646d282014-05-13 10:13:04 -0700286 /**
287 * Stop the user and unbind from everything.
288 *
289 * @param user The user to dispose of
290 */
291 private void destroyUserLocked(UserRecord user) {
RoboErik4646d282014-05-13 10:13:04 -0700292 user.destroyLocked();
293 mUserRecords.remove(user.mUserId);
294 }
295
296 /*
297 * When a session is removed several things need to happen.
298 * 1. We need to remove it from the relevant user.
299 * 2. We need to remove it from the priority stack.
300 * 3. We need to remove it from all sessions.
301 * 4. If this is the system priority session we need to clear it.
302 * 5. We need to unlink to death from the cb binder
303 * 6. We need to tell the session to do any final cleanup (onDestroy)
304 */
RoboErik01fe6612014-02-13 14:19:04 -0800305 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900306 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900307 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900308 }
RoboErik4646d282014-05-13 10:13:04 -0700309 int userId = session.getUserId();
310 UserRecord user = mUserRecords.get(userId);
311 if (user != null) {
312 user.removeSessionLocked(session);
313 }
314
RoboErika8f95142014-05-05 14:23:49 -0700315 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700316 mAllSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700317
318 try {
319 session.getCallback().asBinder().unlinkToDeath(session, 0);
320 } catch (Exception e) {
321 // ignore exceptions while destroying a session.
322 }
323 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700324
325 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800326 }
327
328 private void enforcePackageName(String packageName, int uid) {
329 if (TextUtils.isEmpty(packageName)) {
330 throw new IllegalArgumentException("packageName may not be empty");
331 }
332 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
333 final int packageCount = packages.length;
334 for (int i = 0; i < packageCount; i++) {
335 if (packageName.equals(packages[i])) {
336 return;
337 }
338 }
339 throw new IllegalArgumentException("packageName is not owned by the calling process");
340 }
341
RoboErike7880d82014-04-30 12:48:25 -0700342 /**
343 * Checks a caller's authorization to register an IRemoteControlDisplay.
344 * Authorization is granted if one of the following is true:
345 * <ul>
346 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
347 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700348 * <li>the caller's listener is one of the enabled notification listeners
349 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700350 * </ul>
351 */
RoboErika5b02322014-05-07 17:05:49 -0700352 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
353 int resolvedUserId) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500354 if (isCurrentVolumeController(uid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700355 if (getContext()
356 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
357 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700358 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
359 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700360 throw new SecurityException("Missing permission to control media.");
361 }
362 }
363
John Spurlockbe19ed02015-02-22 10:57:55 -0500364 private boolean isCurrentVolumeController(int uid) {
John Spurlockeb69e242015-02-17 17:15:04 -0500365 if (mAudioManagerInternal != null) {
366 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
367 if (vcuid > 0 && uid == vcuid) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500368 return true;
John Spurlockeb69e242015-02-17 17:15:04 -0500369 }
370 }
John Spurlockbe19ed02015-02-22 10:57:55 -0500371 return false;
372 }
373
374 private void enforceSystemUiPermission(String action, int pid, int uid) {
375 if (isCurrentVolumeController(uid)) return;
RoboErik19c95182014-06-23 15:38:48 -0700376 if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
377 pid, uid) != PackageManager.PERMISSION_GRANTED) {
378 throw new SecurityException("Only system ui may " + action);
379 }
380 }
381
RoboErika5b02322014-05-07 17:05:49 -0700382 /**
383 * This checks if the component is an enabled notification listener for the
384 * specified user. Enabled components may only operate on behalf of the user
385 * they're running as.
386 *
387 * @param compName The component that is enabled.
388 * @param userId The user id of the caller.
389 * @param forUserId The user id they're making the request on behalf of.
390 * @return True if the component is enabled, false otherwise
391 */
392 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
393 int forUserId) {
394 if (userId != forUserId) {
395 // You may not access another user's content as an enabled listener.
396 return false;
397 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700398 if (DEBUG) {
399 Log.d(TAG, "Checking if enabled notification listener " + compName);
400 }
RoboErike7880d82014-04-30 12:48:25 -0700401 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700402 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700403 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700404 userId);
RoboErike7880d82014-04-30 12:48:25 -0700405 if (enabledNotifListeners != null) {
406 final String[] components = enabledNotifListeners.split(":");
407 for (int i = 0; i < components.length; i++) {
408 final ComponentName component =
409 ComponentName.unflattenFromString(components[i]);
410 if (component != null) {
411 if (compName.equals(component)) {
412 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900413 Log.d(TAG, "ok to get sessions. " + component +
RoboErike7880d82014-04-30 12:48:25 -0700414 " is authorized notification listener");
415 }
416 return true;
417 }
418 }
419 }
420 }
421 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900422 Log.d(TAG, "not ok to get sessions. " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700423 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700424 }
425 }
426 return false;
427 }
428
RoboErika5b02322014-05-07 17:05:49 -0700429 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700430 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800431 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700432 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800433 }
434 }
435
RoboErik4646d282014-05-13 10:13:04 -0700436 /*
437 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700438 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700439 * 2. It needs to be added to all sessions.
440 * 3. It needs to be added to the priority stack.
441 * 4. It needs to be added to the relevant user record.
442 */
RoboErika5b02322014-05-07 17:05:49 -0700443 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
444 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700445
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700446 UserRecord user = mUserRecords.get(userId);
447 if (user == null) {
448 Log.wtf(TAG, "Request from invalid user: " + userId);
449 throw new RuntimeException("Session request from invalid user.");
450 }
451
RoboErika5b02322014-05-07 17:05:49 -0700452 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
453 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800454 try {
455 cb.asBinder().linkToDeath(session, 0);
456 } catch (RemoteException e) {
457 throw new RuntimeException("Media Session owner died prematurely.", e);
458 }
RoboErik4646d282014-05-13 10:13:04 -0700459
460 mAllSessions.add(session);
Jaewan Kim8f729082016-06-21 12:36:26 +0900461 mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
RoboErik4646d282014-05-13 10:13:04 -0700462 user.addSessionLocked(session);
463
RoboErik2e7a9162014-06-04 16:53:45 -0700464 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
465
RoboErik01fe6612014-02-13 14:19:04 -0800466 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900467 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800468 }
469 return session;
470 }
471
RoboErik2e7a9162014-06-04 16:53:45 -0700472 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
473 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800474 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700475 return i;
476 }
477 }
478 return -1;
479 }
480
RoboErik2e7a9162014-06-04 16:53:45 -0700481 private void pushSessionsChanged(int userId) {
482 synchronized (mLock) {
483 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
484 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800485 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700486 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700487 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700488 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700489 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700490 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700491 }
RoboErik19c95182014-06-23 15:38:48 -0700492 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700493 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
494 SessionsListenerRecord record = mSessionsListeners.get(i);
495 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
496 try {
497 record.mListener.onActiveSessionsChanged(tokens);
498 } catch (RemoteException e) {
499 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
500 e);
501 mSessionsListeners.remove(i);
502 }
503 }
504 }
505 }
506 }
507
RoboErik19c95182014-06-23 15:38:48 -0700508 private void pushRemoteVolumeUpdateLocked(int userId) {
509 if (mRvc != null) {
510 try {
511 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
512 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
513 } catch (RemoteException e) {
514 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
515 }
516 }
517 }
518
RoboErikb214efb2014-07-24 13:20:30 -0700519 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
520 PendingIntent receiver = record.getMediaButtonReceiver();
521 UserRecord user = mUserRecords.get(record.getUserId());
522 if (receiver != null && user != null) {
523 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800524 ComponentName component = receiver.getIntent().getComponent();
525 if (component != null && record.getPackageName().equals(component.getPackageName())) {
526 Settings.Secure.putStringForUser(mContentResolver,
527 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
528 record.getUserId());
529 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700530 }
531 }
532
RoboErik4646d282014-05-13 10:13:04 -0700533 /**
534 * Information about a particular user. The contents of this object is
535 * guarded by mLock.
536 */
537 final class UserRecord {
538 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700539 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800540 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700541 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800542 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700543
544 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800545 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700546 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800547 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700548 }
549
RoboErik4646d282014-05-13 10:13:04 -0700550 public void destroyLocked() {
551 for (int i = mSessions.size() - 1; i >= 0; i--) {
552 MediaSessionRecord session = mSessions.get(i);
553 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700554 }
555 }
556
RoboErik4646d282014-05-13 10:13:04 -0700557 public ArrayList<MediaSessionRecord> getSessionsLocked() {
558 return mSessions;
559 }
560
561 public void addSessionLocked(MediaSessionRecord session) {
562 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700563 }
564
565 public void removeSessionLocked(MediaSessionRecord session) {
566 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700567 }
568
569 public void dumpLocked(PrintWriter pw, String prefix) {
570 pw.println(prefix + "Record for user " + mUserId);
571 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700572 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800573 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jeff Brown01a500e2014-07-10 22:50:50 -0700574 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700575 pw.println(indent + size + " Sessions:");
576 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700577 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700578 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700579 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700580 }
581 }
RoboErikc8f92d12015-01-05 16:48:07 -0800582
583 private void restoreMediaButtonReceiver() {
584 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
Jaewan Kim8f729082016-06-21 12:36:26 +0900585 Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
RoboErikc8f92d12015-01-05 16:48:07 -0800586 if (!TextUtils.isEmpty(receiverName)) {
587 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
588 if (eventReceiver == null) {
589 // an invalid name was persisted
590 return;
591 }
592 mRestoredMediaButtonReceiver = eventReceiver;
593 }
594 }
RoboErik4646d282014-05-13 10:13:04 -0700595 }
596
RoboErik2e7a9162014-06-04 16:53:45 -0700597 final class SessionsListenerRecord implements IBinder.DeathRecipient {
598 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700599 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700600 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700601 private final int mPid;
602 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700603
RoboErik7aef77b2014-08-08 15:56:54 -0700604 public SessionsListenerRecord(IActiveSessionsListener listener,
605 ComponentName componentName,
606 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700607 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700608 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700609 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700610 mPid = pid;
611 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700612 }
613
614 @Override
615 public void binderDied() {
616 synchronized (mLock) {
617 mSessionsListeners.remove(this);
618 }
619 }
620 }
621
RoboErik7aef77b2014-08-08 15:56:54 -0700622 final class SettingsObserver extends ContentObserver {
623 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
624 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
625
626 private SettingsObserver() {
627 super(null);
628 }
629
630 private void observe() {
631 mContentResolver.registerContentObserver(mSecureSettingsUri,
632 false, this, UserHandle.USER_ALL);
633 }
634
635 @Override
636 public void onChange(boolean selfChange, Uri uri) {
637 updateActiveSessionListeners();
638 }
639 }
640
RoboErik07c70772014-03-20 13:33:52 -0700641 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700642 private static final String EXTRA_WAKELOCK_ACQUIRED =
643 "android.media.AudioService.WAKELOCK_ACQUIRED";
644 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
645
RoboErik9a9d0b52014-05-20 14:53:39 -0700646 private boolean mVoiceButtonDown = false;
647 private boolean mVoiceButtonHandled = false;
648
RoboErik07c70772014-03-20 13:33:52 -0700649 @Override
RoboErika5b02322014-05-07 17:05:49 -0700650 public ISession createSession(String packageName, ISessionCallback cb, String tag,
651 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800652 final int pid = Binder.getCallingPid();
653 final int uid = Binder.getCallingUid();
654 final long token = Binder.clearCallingIdentity();
655 try {
656 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700657 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
658 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800659 if (cb == null) {
660 throw new IllegalArgumentException("Controller callback cannot be null");
661 }
RoboErika5b02322014-05-07 17:05:49 -0700662 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
663 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700664 } finally {
665 Binder.restoreCallingIdentity(token);
666 }
667 }
668
669 @Override
RoboErika5b02322014-05-07 17:05:49 -0700670 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700671 final int pid = Binder.getCallingPid();
672 final int uid = Binder.getCallingUid();
673 final long token = Binder.clearCallingIdentity();
674
675 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700676 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700677 ArrayList<IBinder> binders = new ArrayList<IBinder>();
678 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700679 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700680 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700681 int size = records.size();
682 for (int i = 0; i < size; i++) {
683 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700684 }
685 }
686 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800687 } finally {
688 Binder.restoreCallingIdentity(token);
689 }
690 }
RoboErika278ea72014-04-24 14:49:01 -0700691
RoboErik2e7a9162014-06-04 16:53:45 -0700692 @Override
693 public void addSessionsListener(IActiveSessionsListener listener,
694 ComponentName componentName, int userId) throws RemoteException {
695 final int pid = Binder.getCallingPid();
696 final int uid = Binder.getCallingUid();
697 final long token = Binder.clearCallingIdentity();
698
699 try {
700 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
701 synchronized (mLock) {
702 int index = findIndexOfSessionsListenerLocked(listener);
703 if (index != -1) {
704 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
705 return;
706 }
707 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700708 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700709 try {
710 listener.asBinder().linkToDeath(record, 0);
711 } catch (RemoteException e) {
712 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
713 return;
714 }
715 mSessionsListeners.add(record);
716 }
717 } finally {
718 Binder.restoreCallingIdentity(token);
719 }
720 }
721
722 @Override
723 public void removeSessionsListener(IActiveSessionsListener listener)
724 throws RemoteException {
725 synchronized (mLock) {
726 int index = findIndexOfSessionsListenerLocked(listener);
727 if (index != -1) {
728 SessionsListenerRecord record = mSessionsListeners.remove(index);
729 try {
730 record.mListener.asBinder().unlinkToDeath(record, 0);
731 } catch (Exception e) {
732 // ignore exceptions, the record is being removed
733 }
734 }
735 }
736 }
737
RoboErik8a2cfc32014-05-16 11:19:38 -0700738 /**
739 * Handles the dispatching of the media button events to one of the
740 * registered listeners, or if there was none, broadcast an
741 * ACTION_MEDIA_BUTTON intent to the rest of the system.
742 *
743 * @param keyEvent a non-null KeyEvent whose key code is one of the
744 * supported media buttons
745 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
746 * while this key event is dispatched.
747 */
748 @Override
749 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
750 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
751 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
752 return;
753 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700754
RoboErik8a2cfc32014-05-16 11:19:38 -0700755 final int pid = Binder.getCallingPid();
756 final int uid = Binder.getCallingUid();
757 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700758 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700759 if (DEBUG) {
760 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
761 + keyEvent);
762 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700763 if (!isUserSetupComplete()) {
764 // Global media key handling can have the side-effect of starting new
765 // activities which is undesirable while setup is in progress.
766 Slog.i(TAG, "Not dispatching media key event because user "
767 + "setup is in progress.");
768 return;
769 }
Jaewan Kimd409d182016-09-19 21:19:55 +0900770 if (isGlobalPriorityActive() && uid != Process.SYSTEM_UID) {
771 // Prevent dispatching key event through reflection while the global priority
772 // session is active.
773 Slog.i(TAG, "Only the system can dispatch media key event "
774 + "to the global priority session.");
775 return;
776 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700777
RoboErik8a2cfc32014-05-16 11:19:38 -0700778 synchronized (mLock) {
RoboErik870c5a62014-12-02 15:08:26 -0800779 // If we don't have a media button receiver to fall back on
780 // include non-playing sessions for dispatching
Jaewan Kim8f729082016-06-21 12:36:26 +0900781 boolean useNotPlayingSessions = true;
782 for (int userId : mCurrentUserIdList) {
783 UserRecord ur = mUserRecords.get(userId);
784 if (ur.mLastMediaButtonReceiver != null
785 || ur.mRestoredMediaButtonReceiver != null) {
786 useNotPlayingSessions = false;
787 break;
788 }
789 }
790
791 if (DEBUG) {
792 Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
793 + useNotPlayingSessions);
794 }
795 MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
796 mCurrentUserIdList, useNotPlayingSessions);
RoboErik9a9d0b52014-05-20 14:53:39 -0700797 if (isVoiceKey(keyEvent.getKeyCode())) {
798 handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700799 } else {
RoboErik9a9d0b52014-05-20 14:53:39 -0700800 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700801 }
802 }
803 } finally {
804 Binder.restoreCallingIdentity(token);
805 }
806 }
807
RoboErika278ea72014-04-24 14:49:01 -0700808 @Override
RoboErik7c82ced2014-12-04 17:39:08 -0800809 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -0700810 final long token = Binder.clearCallingIdentity();
811 try {
812 synchronized (mLock) {
813 MediaSessionRecord session = mPriorityStack
Jaewan Kim8f729082016-06-21 12:36:26 +0900814 .getDefaultVolumeSession(mCurrentUserIdList);
RoboErik1ff5b162014-07-15 17:23:18 -0700815 dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
RoboErikb69ffd42014-05-30 14:57:59 -0700816 }
817 } finally {
818 Binder.restoreCallingIdentity(token);
819 }
820 }
821
822 @Override
RoboErik19c95182014-06-23 15:38:48 -0700823 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
824 final int pid = Binder.getCallingPid();
825 final int uid = Binder.getCallingUid();
826 final long token = Binder.clearCallingIdentity();
827 try {
John Spurlockeb69e242015-02-17 17:15:04 -0500828 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -0700829 mRvc = rvc;
830 } finally {
831 Binder.restoreCallingIdentity(token);
832 }
833 }
834
835 @Override
RoboErikde9ba392014-09-26 12:51:01 -0700836 public boolean isGlobalPriorityActive() {
837 return mPriorityStack.isGlobalPriorityActive();
838 }
839
840 @Override
RoboErika278ea72014-04-24 14:49:01 -0700841 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
842 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
843 != PackageManager.PERMISSION_GRANTED) {
844 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
845 + Binder.getCallingPid()
846 + ", uid=" + Binder.getCallingUid());
847 return;
848 }
849
850 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
851 pw.println();
852
853 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -0800854 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErik4646d282014-05-13 10:13:04 -0700855 int count = mAllSessions.size();
RoboErika8f95142014-05-05 14:23:49 -0700856 pw.println(count + " Sessions:");
RoboErika278ea72014-04-24 14:49:01 -0700857 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -0700858 mAllSessions.get(i).dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700859 pw.println();
RoboErika278ea72014-04-24 14:49:01 -0700860 }
RoboErika5b02322014-05-07 17:05:49 -0700861 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -0700862
RoboErik4646d282014-05-13 10:13:04 -0700863 pw.println("User Records:");
864 count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -0700865 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -0800866 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -0700867 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700868 }
869 }
870 }
RoboErik8a2cfc32014-05-16 11:19:38 -0700871
RoboErik2e7a9162014-06-04 16:53:45 -0700872 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
873 final int uid) {
874 String packageName = null;
875 if (componentName != null) {
876 // If they gave us a component name verify they own the
877 // package
878 packageName = componentName.getPackageName();
879 enforcePackageName(packageName, uid);
880 }
881 // Check that they can make calls on behalf of the user and
882 // get the final user id
883 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
884 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
885 // Check if they have the permissions or their component is
886 // enabled for the user they're calling from.
887 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
888 return resolvedUserId;
889 }
890
RoboErik1ff5b162014-07-15 17:23:18 -0700891 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
RoboErikb69ffd42014-05-30 14:57:59 -0700892 MediaSessionRecord session) {
RoboErik9c785402014-11-11 16:52:26 -0800893 boolean preferSuggestedStream = false;
894 if (isValidLocalStreamType(suggestedStream)
895 && AudioSystem.isStreamActive(suggestedStream, 0)) {
896 preferSuggestedStream = true;
897 }
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900898 if (DEBUG) {
899 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
900 + flags + ", suggestedStream=" + suggestedStream
901 + ", preferSuggestedStream=" + preferSuggestedStream);
902 }
RoboErik9c785402014-11-11 16:52:26 -0800903 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -0700904 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
905 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -0700906 if (DEBUG) {
907 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -0700908 }
RoboErikb7c014c2014-07-22 15:58:22 -0700909 return;
RoboErik3c45c292014-07-08 16:47:31 -0700910 }
RoboErik0791e172014-06-08 10:52:32 -0700911 try {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800912 String packageName = getContext().getOpPackageName();
John Spurlockee5ad722015-03-03 16:17:21 -0500913 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
John Spurlock90874332015-03-10 16:00:54 -0400914 flags, packageName, TAG);
RoboErik0791e172014-06-08 10:52:32 -0700915 } catch (RemoteException e) {
916 Log.e(TAG, "Error adjusting default volume.", e);
RoboErikb69ffd42014-05-30 14:57:59 -0700917 }
918 } else {
RoboErik0dac35a2014-08-12 15:48:49 -0700919 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +0900920 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -0700921 }
922 }
923
RoboErik9a9d0b52014-05-20 14:53:39 -0700924 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
925 MediaSessionRecord session) {
926 if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
927 // If the phone app has priority just give it the event
928 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
929 return;
930 }
931 int action = keyEvent.getAction();
932 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
933 if (action == KeyEvent.ACTION_DOWN) {
934 if (keyEvent.getRepeatCount() == 0) {
935 mVoiceButtonDown = true;
936 mVoiceButtonHandled = false;
937 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
938 mVoiceButtonHandled = true;
939 startVoiceInput(needWakeLock);
940 }
941 } else if (action == KeyEvent.ACTION_UP) {
942 if (mVoiceButtonDown) {
943 mVoiceButtonDown = false;
944 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
945 // Resend the down then send this event through
946 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
947 dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
948 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
949 }
950 }
951 }
952 }
953
954 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
955 MediaSessionRecord session) {
956 if (session != null) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900957 if (DEBUG_MEDIA_KEY_EVENT) {
958 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -0700959 }
960 if (needWakeLock) {
961 mKeyEventReceiver.aquireWakeLockLocked();
962 }
963 // If we don't need a wakelock use -1 as the id so we
964 // won't release it later
965 session.sendMediaButton(keyEvent,
966 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +0900967 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +0900968 getContext().getPackageName());
RoboErik9a9d0b52014-05-20 14:53:39 -0700969 } else {
RoboErikb214efb2014-07-24 13:20:30 -0700970 // Launch the last PendingIntent we had with priority
Jaewan Kim8f729082016-06-21 12:36:26 +0900971 for (int userId : mCurrentUserIdList) {
972 UserRecord user = mUserRecords.get(userId);
973 if (user.mLastMediaButtonReceiver == null
974 && user.mRestoredMediaButtonReceiver == null) {
975 continue;
976 }
RoboErikb214efb2014-07-24 13:20:30 -0700977 if (needWakeLock) {
978 mKeyEventReceiver.aquireWakeLockLocked();
979 }
980 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Insun Kang2054db32016-04-07 15:34:34 +0900981 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RoboErikb214efb2014-07-24 13:20:30 -0700982 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
983 try {
RoboErikc8f92d12015-01-05 16:48:07 -0800984 if (user.mLastMediaButtonReceiver != null) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900985 if (DEBUG_MEDIA_KEY_EVENT) {
986 Log.d(TAG, "Sending " + keyEvent
987 + " to the last known pendingIntent "
988 + user.mLastMediaButtonReceiver);
989 }
RoboErikc8f92d12015-01-05 16:48:07 -0800990 user.mLastMediaButtonReceiver.send(getContext(),
991 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
riddle_hsu02ed0122015-10-20 16:00:15 +0800992 mediaButtonIntent, mKeyEventReceiver, mHandler);
RoboErikc8f92d12015-01-05 16:48:07 -0800993 } else {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900994 if (DEBUG_MEDIA_KEY_EVENT) {
995 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
996 + user.mRestoredMediaButtonReceiver);
997 }
RoboErikc8f92d12015-01-05 16:48:07 -0800998 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
999 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim8f729082016-06-21 12:36:26 +09001000 UserHandle.of(userId));
RoboErikc8f92d12015-01-05 16:48:07 -08001001 }
RoboErikb214efb2014-07-24 13:20:30 -07001002 } catch (CanceledException e) {
1003 Log.i(TAG, "Error sending key event to media button receiver "
1004 + user.mLastMediaButtonReceiver, e);
1005 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001006 return;
RoboErik9a9d0b52014-05-20 14:53:39 -07001007 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001008 if (DEBUG) {
1009 Log.d(TAG, "Sending media key ordered broadcast");
1010 }
1011 if (needWakeLock) {
1012 mMediaEventWakeLock.acquire();
1013 }
1014 // Fallback to legacy behavior
1015 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1016 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1017 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1018 if (needWakeLock) {
1019 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1020 }
1021 // Send broadcast only to the full user.
1022 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1023 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001024 }
1025 }
1026
1027 private void startVoiceInput(boolean needWakeLock) {
1028 Intent voiceIntent = null;
1029 // select which type of search to launch:
1030 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1031 // - device locked or screen off: action is
1032 // ACTION_VOICE_SEARCH_HANDS_FREE
1033 // with EXTRA_SECURE set to true if the device is securely locked
1034 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1035 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1036 if (!isLocked && pm.isScreenOn()) {
1037 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1038 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1039 } else {
1040 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1041 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1042 isLocked && mKeyguardManager.isKeyguardSecure());
1043 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1044 }
1045 // start the search activity
1046 if (needWakeLock) {
1047 mMediaEventWakeLock.acquire();
1048 }
1049 try {
1050 if (voiceIntent != null) {
1051 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1052 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001053 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001054 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1055 }
1056 } catch (ActivityNotFoundException e) {
1057 Log.w(TAG, "No activity for search: " + e);
1058 } finally {
1059 if (needWakeLock) {
1060 mMediaEventWakeLock.release();
1061 }
1062 }
1063 }
1064
1065 private boolean isVoiceKey(int keyCode) {
1066 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1067 }
1068
Jeff Brown38d3feb2015-03-19 18:26:30 -07001069 private boolean isUserSetupComplete() {
1070 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1071 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1072 }
1073
RoboErik9c785402014-11-11 16:52:26 -08001074 // we only handle public stream types, which are 0-5
1075 private boolean isValidLocalStreamType(int streamType) {
1076 return streamType >= AudioManager.STREAM_VOICE_CALL
1077 && streamType <= AudioManager.STREAM_NOTIFICATION;
1078 }
1079
RoboErik418c10c2014-05-19 09:25:25 -07001080 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1081
RoboErikb214efb2014-07-24 13:20:30 -07001082 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1083 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001084 private final Handler mHandler;
1085 private int mRefCount = 0;
1086 private int mLastTimeoutId = 0;
1087
1088 public KeyEventWakeLockReceiver(Handler handler) {
1089 super(handler);
1090 mHandler = handler;
1091 }
1092
1093 public void onTimeout() {
1094 synchronized (mLock) {
1095 if (mRefCount == 0) {
1096 // We've already released it, so just return
1097 return;
1098 }
1099 mLastTimeoutId++;
1100 mRefCount = 0;
1101 releaseWakeLockLocked();
1102 }
1103 }
1104
1105 public void aquireWakeLockLocked() {
1106 if (mRefCount == 0) {
1107 mMediaEventWakeLock.acquire();
1108 }
1109 mRefCount++;
1110 mHandler.removeCallbacks(this);
1111 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1112
1113 }
1114
1115 @Override
1116 public void run() {
1117 onTimeout();
1118 }
1119
RoboErik8a2cfc32014-05-16 11:19:38 -07001120 @Override
1121 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001122 if (resultCode < mLastTimeoutId) {
1123 // Ignore results from calls that were before the last
1124 // timeout, just in case.
1125 return;
1126 } else {
1127 synchronized (mLock) {
1128 if (mRefCount > 0) {
1129 mRefCount--;
1130 if (mRefCount == 0) {
1131 releaseWakeLockLocked();
1132 }
1133 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001134 }
1135 }
1136 }
RoboErik418c10c2014-05-19 09:25:25 -07001137
1138 private void releaseWakeLockLocked() {
1139 mMediaEventWakeLock.release();
1140 mHandler.removeCallbacks(this);
1141 }
RoboErikb214efb2014-07-24 13:20:30 -07001142
1143 @Override
1144 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1145 String resultData, Bundle resultExtras) {
1146 onReceiveResult(resultCode, null);
1147 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001148 };
1149
1150 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1151 @Override
1152 public void onReceive(Context context, Intent intent) {
1153 if (intent == null) {
1154 return;
1155 }
1156 Bundle extras = intent.getExtras();
1157 if (extras == null) {
1158 return;
1159 }
1160 synchronized (mLock) {
1161 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1162 && mMediaEventWakeLock.isHeld()) {
1163 mMediaEventWakeLock.release();
1164 }
1165 }
1166 }
1167 };
RoboErik01fe6612014-02-13 14:19:04 -08001168 }
1169
RoboErik2e7a9162014-06-04 16:53:45 -07001170 final class MessageHandler extends Handler {
1171 private static final int MSG_SESSIONS_CHANGED = 1;
1172
1173 @Override
1174 public void handleMessage(Message msg) {
1175 switch (msg.what) {
1176 case MSG_SESSIONS_CHANGED:
1177 pushSessionsChanged(msg.arg1);
1178 break;
1179 }
1180 }
1181
1182 public void post(int what, int arg1, int arg2) {
1183 obtainMessage(what, arg1, arg2).sendToTarget();
1184 }
1185 }
RoboErik01fe6612014-02-13 14:19:04 -08001186}