blob: 65949bf80aa08f1fb025e09a2e708f156a8de3e3 [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;
RoboErik01fe6612014-02-13 14:19:04 -080050import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070051import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070052import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070053import android.os.UserHandle;
54import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070055import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080056import android.text.TextUtils;
57import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070058import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070059import android.util.SparseArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070060import android.view.KeyEvent;
RoboErik01fe6612014-02-13 14:19:04 -080061
John Spurlockeb69e242015-02-17 17:15:04 -050062import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080063import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070064import com.android.server.Watchdog;
65import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080066
RoboErika278ea72014-04-24 14:49:01 -070067import java.io.FileDescriptor;
68import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080069import java.util.ArrayList;
RoboErike7880d82014-04-30 12:48:25 -070070import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080071
72/**
73 * System implementation of MediaSessionManager
74 */
RoboErika278ea72014-04-24 14:49:01 -070075public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080076 private static final String TAG = "MediaSessionService";
77 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
78
RoboErik418c10c2014-05-19 09:25:25 -070079 private static final int WAKELOCK_TIMEOUT = 5000;
80
RoboErik2610d712015-01-07 11:10:23 -080081 /* package */final IBinder mICallback = new Binder();
82
RoboErik01fe6612014-02-13 14:19:04 -080083 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070084 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080085
RoboErik4646d282014-05-13 10:13:04 -070086 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
87 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070088 private final ArrayList<SessionsListenerRecord> mSessionsListeners
89 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -080090 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -070091 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -070092 private final PowerManager.WakeLock mMediaEventWakeLock;
RoboErik01fe6612014-02-13 14:19:04 -080093
RoboErik9a9d0b52014-05-20 14:53:39 -070094 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -070095 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -050096 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -070097 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -070098 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -070099
RoboErik4646d282014-05-13 10:13:04 -0700100 private int mCurrentUserId = -1;
RoboErike7880d82014-04-30 12:48:25 -0700101
RoboErik19c95182014-06-23 15:38:48 -0700102 // Used to notify system UI when remote volume was changed. TODO find a
103 // better way to handle this.
104 private IRemoteVolumeController mRvc;
105
RoboErik01fe6612014-02-13 14:19:04 -0800106 public MediaSessionService(Context context) {
107 super(context);
108 mSessionManagerImpl = new SessionManagerImpl();
RoboErika8f95142014-05-05 14:23:49 -0700109 mPriorityStack = new MediaSessionStack();
RoboErik8a2cfc32014-05-16 11:19:38 -0700110 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
111 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
RoboErik01fe6612014-02-13 14:19:04 -0800112 }
113
114 @Override
115 public void onStart() {
116 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700117 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700118 mKeyguardManager =
119 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700120 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500121 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700122 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700123 mSettingsObserver = new SettingsObserver();
124 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800125
126 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700127 }
128
129 private IAudioService getAudioService() {
130 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
131 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700132 }
133
RoboErika8f95142014-05-05 14:23:49 -0700134 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700135 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700136 if (!mAllSessions.contains(record)) {
137 Log.d(TAG, "Unknown session updated. Ignoring.");
138 return;
139 }
RoboErika8f95142014-05-05 14:23:49 -0700140 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700141 }
RoboErik2e7a9162014-06-04 16:53:45 -0700142 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700143 }
144
RoboErik9c5b7cb2015-01-15 15:09:09 -0800145 /**
146 * Tells the system UI that volume has changed on a remote session.
147 */
148 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
149 if (mRvc == null) {
150 return;
151 }
152 try {
153 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
154 } catch (Exception e) {
155 Log.wtf(TAG, "Error sending volume change to system UI.", e);
156 }
157 }
158
RoboErika8f95142014-05-05 14:23:49 -0700159 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700160 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700161 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700162 if (!mAllSessions.contains(record)) {
163 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
164 return;
165 }
RoboErik2e7a9162014-06-04 16:53:45 -0700166 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
167 }
168 if (updateSessions) {
169 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700170 }
171 }
172
RoboErik19c95182014-06-23 15:38:48 -0700173 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
174 synchronized (mLock) {
175 if (!mAllSessions.contains(record)) {
176 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
177 return;
178 }
179 pushRemoteVolumeUpdateLocked(record.getUserId());
180 }
181 }
182
RoboErika278ea72014-04-24 14:49:01 -0700183 @Override
RoboErik4646d282014-05-13 10:13:04 -0700184 public void onStartUser(int userHandle) {
185 updateUser();
186 }
187
188 @Override
189 public void onSwitchUser(int userHandle) {
190 updateUser();
191 }
192
193 @Override
194 public void onStopUser(int userHandle) {
195 synchronized (mLock) {
196 UserRecord user = mUserRecords.get(userHandle);
197 if (user != null) {
198 destroyUserLocked(user);
199 }
200 }
201 }
202
203 @Override
RoboErika278ea72014-04-24 14:49:01 -0700204 public void monitor() {
205 synchronized (mLock) {
206 // Check for deadlock
207 }
208 }
209
RoboErik4646d282014-05-13 10:13:04 -0700210 protected void enforcePhoneStatePermission(int pid, int uid) {
211 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
212 != PackageManager.PERMISSION_GRANTED) {
213 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
214 }
215 }
216
RoboErik01fe6612014-02-13 14:19:04 -0800217 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700218 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800219 destroySessionLocked(session);
220 }
221 }
222
223 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700224 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800225 destroySessionLocked(session);
226 }
227 }
228
RoboErik4646d282014-05-13 10:13:04 -0700229 private void updateUser() {
230 synchronized (mLock) {
231 int userId = ActivityManager.getCurrentUser();
232 if (mCurrentUserId != userId) {
233 final int oldUserId = mCurrentUserId;
234 mCurrentUserId = userId; // do this first
235
236 UserRecord oldUser = mUserRecords.get(oldUserId);
237 if (oldUser != null) {
238 oldUser.stopLocked();
239 }
240
241 UserRecord newUser = getOrCreateUser(userId);
242 newUser.startLocked();
243 }
244 }
245 }
246
RoboErik7aef77b2014-08-08 15:56:54 -0700247 private void updateActiveSessionListeners() {
248 synchronized (mLock) {
249 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
250 SessionsListenerRecord listener = mSessionsListeners.get(i);
251 try {
252 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
253 listener.mUserId);
254 } catch (SecurityException e) {
255 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
256 + " is no longer authorized. Disconnecting.");
257 mSessionsListeners.remove(i);
258 try {
259 listener.mListener
260 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
261 } catch (Exception e1) {
262 // ignore
263 }
264 }
265 }
266 }
267 }
268
RoboErik4646d282014-05-13 10:13:04 -0700269 /**
270 * Stop the user and unbind from everything.
271 *
272 * @param user The user to dispose of
273 */
274 private void destroyUserLocked(UserRecord user) {
275 user.stopLocked();
276 user.destroyLocked();
277 mUserRecords.remove(user.mUserId);
278 }
279
280 /*
281 * When a session is removed several things need to happen.
282 * 1. We need to remove it from the relevant user.
283 * 2. We need to remove it from the priority stack.
284 * 3. We need to remove it from all sessions.
285 * 4. If this is the system priority session we need to clear it.
286 * 5. We need to unlink to death from the cb binder
287 * 6. We need to tell the session to do any final cleanup (onDestroy)
288 */
RoboErik01fe6612014-02-13 14:19:04 -0800289 private void destroySessionLocked(MediaSessionRecord session) {
RoboErik4646d282014-05-13 10:13:04 -0700290 int userId = session.getUserId();
291 UserRecord user = mUserRecords.get(userId);
292 if (user != null) {
293 user.removeSessionLocked(session);
294 }
295
RoboErika8f95142014-05-05 14:23:49 -0700296 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700297 mAllSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700298
299 try {
300 session.getCallback().asBinder().unlinkToDeath(session, 0);
301 } catch (Exception e) {
302 // ignore exceptions while destroying a session.
303 }
304 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700305
306 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800307 }
308
309 private void enforcePackageName(String packageName, int uid) {
310 if (TextUtils.isEmpty(packageName)) {
311 throw new IllegalArgumentException("packageName may not be empty");
312 }
313 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
314 final int packageCount = packages.length;
315 for (int i = 0; i < packageCount; i++) {
316 if (packageName.equals(packages[i])) {
317 return;
318 }
319 }
320 throw new IllegalArgumentException("packageName is not owned by the calling process");
321 }
322
RoboErike7880d82014-04-30 12:48:25 -0700323 /**
324 * Checks a caller's authorization to register an IRemoteControlDisplay.
325 * Authorization is granted if one of the following is true:
326 * <ul>
327 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
328 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700329 * <li>the caller's listener is one of the enabled notification listeners
330 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700331 * </ul>
332 */
RoboErika5b02322014-05-07 17:05:49 -0700333 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
334 int resolvedUserId) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500335 if (isCurrentVolumeController(uid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700336 if (getContext()
337 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
338 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700339 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
340 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700341 throw new SecurityException("Missing permission to control media.");
342 }
343 }
344
John Spurlockbe19ed02015-02-22 10:57:55 -0500345 private boolean isCurrentVolumeController(int uid) {
John Spurlockeb69e242015-02-17 17:15:04 -0500346 if (mAudioManagerInternal != null) {
347 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
348 if (vcuid > 0 && uid == vcuid) {
John Spurlockbe19ed02015-02-22 10:57:55 -0500349 return true;
John Spurlockeb69e242015-02-17 17:15:04 -0500350 }
351 }
John Spurlockbe19ed02015-02-22 10:57:55 -0500352 return false;
353 }
354
355 private void enforceSystemUiPermission(String action, int pid, int uid) {
356 if (isCurrentVolumeController(uid)) return;
RoboErik19c95182014-06-23 15:38:48 -0700357 if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
358 pid, uid) != PackageManager.PERMISSION_GRANTED) {
359 throw new SecurityException("Only system ui may " + action);
360 }
361 }
362
RoboErika5b02322014-05-07 17:05:49 -0700363 /**
364 * This checks if the component is an enabled notification listener for the
365 * specified user. Enabled components may only operate on behalf of the user
366 * they're running as.
367 *
368 * @param compName The component that is enabled.
369 * @param userId The user id of the caller.
370 * @param forUserId The user id they're making the request on behalf of.
371 * @return True if the component is enabled, false otherwise
372 */
373 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
374 int forUserId) {
375 if (userId != forUserId) {
376 // You may not access another user's content as an enabled listener.
377 return false;
378 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700379 if (DEBUG) {
380 Log.d(TAG, "Checking if enabled notification listener " + compName);
381 }
RoboErike7880d82014-04-30 12:48:25 -0700382 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700383 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700384 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700385 userId);
RoboErike7880d82014-04-30 12:48:25 -0700386 if (enabledNotifListeners != null) {
387 final String[] components = enabledNotifListeners.split(":");
388 for (int i = 0; i < components.length; i++) {
389 final ComponentName component =
390 ComponentName.unflattenFromString(components[i]);
391 if (component != null) {
392 if (compName.equals(component)) {
393 if (DEBUG) {
394 Log.d(TAG, "ok to get sessions: " + component +
395 " is authorized notification listener");
396 }
397 return true;
398 }
399 }
400 }
401 }
402 if (DEBUG) {
403 Log.d(TAG, "not ok to get sessions, " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700404 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700405 }
406 }
407 return false;
408 }
409
RoboErika5b02322014-05-07 17:05:49 -0700410 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700411 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800412 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700413 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800414 }
415 }
416
RoboErik4646d282014-05-13 10:13:04 -0700417 /*
418 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700419 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700420 * 2. It needs to be added to all sessions.
421 * 3. It needs to be added to the priority stack.
422 * 4. It needs to be added to the relevant user record.
423 */
RoboErika5b02322014-05-07 17:05:49 -0700424 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
425 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700426
RoboErika5b02322014-05-07 17:05:49 -0700427 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
428 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800429 try {
430 cb.asBinder().linkToDeath(session, 0);
431 } catch (RemoteException e) {
432 throw new RuntimeException("Media Session owner died prematurely.", e);
433 }
RoboErik4646d282014-05-13 10:13:04 -0700434
435 mAllSessions.add(session);
RoboErika8f95142014-05-05 14:23:49 -0700436 mPriorityStack.addSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700437
438 UserRecord user = getOrCreateUser(userId);
439 user.addSessionLocked(session);
440
RoboErik2e7a9162014-06-04 16:53:45 -0700441 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
442
RoboErik01fe6612014-02-13 14:19:04 -0800443 if (DEBUG) {
RoboErika5b02322014-05-07 17:05:49 -0700444 Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800445 }
446 return session;
447 }
448
RoboErik4646d282014-05-13 10:13:04 -0700449 private UserRecord getOrCreateUser(int userId) {
450 UserRecord user = mUserRecords.get(userId);
451 if (user == null) {
452 user = new UserRecord(getContext(), userId);
453 mUserRecords.put(userId, user);
454 }
455 return user;
456 }
457
RoboErik2e7a9162014-06-04 16:53:45 -0700458 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
459 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800460 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700461 return i;
462 }
463 }
464 return -1;
465 }
466
RoboErik2e7a9162014-06-04 16:53:45 -0700467 private void pushSessionsChanged(int userId) {
468 synchronized (mLock) {
469 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
470 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800471 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700472 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700473 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700474 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700475 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700476 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700477 }
RoboErik19c95182014-06-23 15:38:48 -0700478 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700479 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
480 SessionsListenerRecord record = mSessionsListeners.get(i);
481 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
482 try {
483 record.mListener.onActiveSessionsChanged(tokens);
484 } catch (RemoteException e) {
485 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
486 e);
487 mSessionsListeners.remove(i);
488 }
489 }
490 }
491 }
492 }
493
RoboErik19c95182014-06-23 15:38:48 -0700494 private void pushRemoteVolumeUpdateLocked(int userId) {
495 if (mRvc != null) {
496 try {
497 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
498 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
499 } catch (RemoteException e) {
500 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
501 }
502 }
503 }
504
RoboErikb214efb2014-07-24 13:20:30 -0700505 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
506 PendingIntent receiver = record.getMediaButtonReceiver();
507 UserRecord user = mUserRecords.get(record.getUserId());
508 if (receiver != null && user != null) {
509 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800510 ComponentName component = receiver.getIntent().getComponent();
511 if (component != null && record.getPackageName().equals(component.getPackageName())) {
512 Settings.Secure.putStringForUser(mContentResolver,
513 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
514 record.getUserId());
515 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700516 }
517 }
518
RoboErik4646d282014-05-13 10:13:04 -0700519 /**
520 * Information about a particular user. The contents of this object is
521 * guarded by mLock.
522 */
523 final class UserRecord {
524 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700525 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800526 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700527 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800528 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700529
530 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800531 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700532 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800533 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700534 }
535
536 public void startLocked() {
RoboErik4646d282014-05-13 10:13:04 -0700537 }
538
539 public void stopLocked() {
Jeff Brown01a500e2014-07-10 22:50:50 -0700540 // nothing for now
RoboErik4646d282014-05-13 10:13:04 -0700541 }
542
543 public void destroyLocked() {
544 for (int i = mSessions.size() - 1; i >= 0; i--) {
545 MediaSessionRecord session = mSessions.get(i);
546 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700547 }
548 }
549
RoboErik4646d282014-05-13 10:13:04 -0700550 public ArrayList<MediaSessionRecord> getSessionsLocked() {
551 return mSessions;
552 }
553
554 public void addSessionLocked(MediaSessionRecord session) {
555 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700556 }
557
558 public void removeSessionLocked(MediaSessionRecord session) {
559 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700560 }
561
562 public void dumpLocked(PrintWriter pw, String prefix) {
563 pw.println(prefix + "Record for user " + mUserId);
564 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700565 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800566 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jeff Brown01a500e2014-07-10 22:50:50 -0700567 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700568 pw.println(indent + size + " Sessions:");
569 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700570 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700571 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700572 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700573 }
574 }
RoboErikc8f92d12015-01-05 16:48:07 -0800575
576 private void restoreMediaButtonReceiver() {
577 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
578 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
579 if (!TextUtils.isEmpty(receiverName)) {
580 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
581 if (eventReceiver == null) {
582 // an invalid name was persisted
583 return;
584 }
585 mRestoredMediaButtonReceiver = eventReceiver;
586 }
587 }
RoboErik4646d282014-05-13 10:13:04 -0700588 }
589
RoboErik2e7a9162014-06-04 16:53:45 -0700590 final class SessionsListenerRecord implements IBinder.DeathRecipient {
591 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700592 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700593 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700594 private final int mPid;
595 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700596
RoboErik7aef77b2014-08-08 15:56:54 -0700597 public SessionsListenerRecord(IActiveSessionsListener listener,
598 ComponentName componentName,
599 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700600 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700601 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700602 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700603 mPid = pid;
604 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700605 }
606
607 @Override
608 public void binderDied() {
609 synchronized (mLock) {
610 mSessionsListeners.remove(this);
611 }
612 }
613 }
614
RoboErik7aef77b2014-08-08 15:56:54 -0700615 final class SettingsObserver extends ContentObserver {
616 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
617 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
618
619 private SettingsObserver() {
620 super(null);
621 }
622
623 private void observe() {
624 mContentResolver.registerContentObserver(mSecureSettingsUri,
625 false, this, UserHandle.USER_ALL);
626 }
627
628 @Override
629 public void onChange(boolean selfChange, Uri uri) {
630 updateActiveSessionListeners();
631 }
632 }
633
RoboErik07c70772014-03-20 13:33:52 -0700634 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700635 private static final String EXTRA_WAKELOCK_ACQUIRED =
636 "android.media.AudioService.WAKELOCK_ACQUIRED";
637 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
638
RoboErik9a9d0b52014-05-20 14:53:39 -0700639 private boolean mVoiceButtonDown = false;
640 private boolean mVoiceButtonHandled = false;
641
RoboErik07c70772014-03-20 13:33:52 -0700642 @Override
RoboErika5b02322014-05-07 17:05:49 -0700643 public ISession createSession(String packageName, ISessionCallback cb, String tag,
644 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800645 final int pid = Binder.getCallingPid();
646 final int uid = Binder.getCallingUid();
647 final long token = Binder.clearCallingIdentity();
648 try {
649 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700650 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
651 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800652 if (cb == null) {
653 throw new IllegalArgumentException("Controller callback cannot be null");
654 }
RoboErika5b02322014-05-07 17:05:49 -0700655 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
656 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700657 } finally {
658 Binder.restoreCallingIdentity(token);
659 }
660 }
661
662 @Override
RoboErika5b02322014-05-07 17:05:49 -0700663 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700664 final int pid = Binder.getCallingPid();
665 final int uid = Binder.getCallingUid();
666 final long token = Binder.clearCallingIdentity();
667
668 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700669 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700670 ArrayList<IBinder> binders = new ArrayList<IBinder>();
671 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700672 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700673 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700674 int size = records.size();
675 for (int i = 0; i < size; i++) {
676 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700677 }
678 }
679 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800680 } finally {
681 Binder.restoreCallingIdentity(token);
682 }
683 }
RoboErika278ea72014-04-24 14:49:01 -0700684
RoboErik2e7a9162014-06-04 16:53:45 -0700685 @Override
686 public void addSessionsListener(IActiveSessionsListener listener,
687 ComponentName componentName, int userId) throws RemoteException {
688 final int pid = Binder.getCallingPid();
689 final int uid = Binder.getCallingUid();
690 final long token = Binder.clearCallingIdentity();
691
692 try {
693 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
694 synchronized (mLock) {
695 int index = findIndexOfSessionsListenerLocked(listener);
696 if (index != -1) {
697 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
698 return;
699 }
700 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700701 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700702 try {
703 listener.asBinder().linkToDeath(record, 0);
704 } catch (RemoteException e) {
705 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
706 return;
707 }
708 mSessionsListeners.add(record);
709 }
710 } finally {
711 Binder.restoreCallingIdentity(token);
712 }
713 }
714
715 @Override
716 public void removeSessionsListener(IActiveSessionsListener listener)
717 throws RemoteException {
718 synchronized (mLock) {
719 int index = findIndexOfSessionsListenerLocked(listener);
720 if (index != -1) {
721 SessionsListenerRecord record = mSessionsListeners.remove(index);
722 try {
723 record.mListener.asBinder().unlinkToDeath(record, 0);
724 } catch (Exception e) {
725 // ignore exceptions, the record is being removed
726 }
727 }
728 }
729 }
730
RoboErik8a2cfc32014-05-16 11:19:38 -0700731 /**
732 * Handles the dispatching of the media button events to one of the
733 * registered listeners, or if there was none, broadcast an
734 * ACTION_MEDIA_BUTTON intent to the rest of the system.
735 *
736 * @param keyEvent a non-null KeyEvent whose key code is one of the
737 * supported media buttons
738 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
739 * while this key event is dispatched.
740 */
741 @Override
742 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
743 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
744 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
745 return;
746 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700747
RoboErik8a2cfc32014-05-16 11:19:38 -0700748 final int pid = Binder.getCallingPid();
749 final int uid = Binder.getCallingUid();
750 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700751 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700752 if (DEBUG) {
753 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
754 + keyEvent);
755 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700756 if (!isUserSetupComplete()) {
757 // Global media key handling can have the side-effect of starting new
758 // activities which is undesirable while setup is in progress.
759 Slog.i(TAG, "Not dispatching media key event because user "
760 + "setup is in progress.");
761 return;
762 }
763
RoboErik8a2cfc32014-05-16 11:19:38 -0700764 synchronized (mLock) {
RoboErik870c5a62014-12-02 15:08:26 -0800765 // If we don't have a media button receiver to fall back on
766 // include non-playing sessions for dispatching
RoboErikc8f92d12015-01-05 16:48:07 -0800767 UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
768 boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null
769 && ur.mRestoredMediaButtonReceiver == null;
RoboErik9a9d0b52014-05-20 14:53:39 -0700770 MediaSessionRecord session = mPriorityStack
RoboErik870c5a62014-12-02 15:08:26 -0800771 .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
RoboErik9a9d0b52014-05-20 14:53:39 -0700772 if (isVoiceKey(keyEvent.getKeyCode())) {
773 handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700774 } else {
RoboErik9a9d0b52014-05-20 14:53:39 -0700775 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700776 }
777 }
778 } finally {
779 Binder.restoreCallingIdentity(token);
780 }
781 }
782
RoboErika278ea72014-04-24 14:49:01 -0700783 @Override
RoboErik7c82ced2014-12-04 17:39:08 -0800784 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -0700785 final int pid = Binder.getCallingPid();
786 final int uid = Binder.getCallingUid();
787 final long token = Binder.clearCallingIdentity();
788 try {
789 synchronized (mLock) {
790 MediaSessionRecord session = mPriorityStack
791 .getDefaultVolumeSession(mCurrentUserId);
RoboErik1ff5b162014-07-15 17:23:18 -0700792 dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
RoboErikb69ffd42014-05-30 14:57:59 -0700793 }
794 } finally {
795 Binder.restoreCallingIdentity(token);
796 }
797 }
798
799 @Override
RoboErik19c95182014-06-23 15:38:48 -0700800 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
801 final int pid = Binder.getCallingPid();
802 final int uid = Binder.getCallingUid();
803 final long token = Binder.clearCallingIdentity();
804 try {
John Spurlockeb69e242015-02-17 17:15:04 -0500805 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -0700806 mRvc = rvc;
807 } finally {
808 Binder.restoreCallingIdentity(token);
809 }
810 }
811
812 @Override
RoboErikde9ba392014-09-26 12:51:01 -0700813 public boolean isGlobalPriorityActive() {
814 return mPriorityStack.isGlobalPriorityActive();
815 }
816
817 @Override
RoboErika278ea72014-04-24 14:49:01 -0700818 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
819 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
820 != PackageManager.PERMISSION_GRANTED) {
821 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
822 + Binder.getCallingPid()
823 + ", uid=" + Binder.getCallingUid());
824 return;
825 }
826
827 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
828 pw.println();
829
830 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -0800831 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErik4646d282014-05-13 10:13:04 -0700832 int count = mAllSessions.size();
RoboErika8f95142014-05-05 14:23:49 -0700833 pw.println(count + " Sessions:");
RoboErika278ea72014-04-24 14:49:01 -0700834 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -0700835 mAllSessions.get(i).dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700836 pw.println();
RoboErika278ea72014-04-24 14:49:01 -0700837 }
RoboErika5b02322014-05-07 17:05:49 -0700838 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -0700839
RoboErik4646d282014-05-13 10:13:04 -0700840 pw.println("User Records:");
841 count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -0700842 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -0800843 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -0700844 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700845 }
846 }
847 }
RoboErik8a2cfc32014-05-16 11:19:38 -0700848
RoboErik2e7a9162014-06-04 16:53:45 -0700849 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
850 final int uid) {
851 String packageName = null;
852 if (componentName != null) {
853 // If they gave us a component name verify they own the
854 // package
855 packageName = componentName.getPackageName();
856 enforcePackageName(packageName, uid);
857 }
858 // Check that they can make calls on behalf of the user and
859 // get the final user id
860 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
861 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
862 // Check if they have the permissions or their component is
863 // enabled for the user they're calling from.
864 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
865 return resolvedUserId;
866 }
867
RoboErik1ff5b162014-07-15 17:23:18 -0700868 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
RoboErikb69ffd42014-05-30 14:57:59 -0700869 MediaSessionRecord session) {
RoboErikb69ffd42014-05-30 14:57:59 -0700870 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700871 String description = session == null ? null : session.toString();
872 Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
873 + flags + ", suggestedStream=" + suggestedStream);
RoboErikb69ffd42014-05-30 14:57:59 -0700874
875 }
RoboErik9c785402014-11-11 16:52:26 -0800876 boolean preferSuggestedStream = false;
877 if (isValidLocalStreamType(suggestedStream)
878 && AudioSystem.isStreamActive(suggestedStream, 0)) {
879 preferSuggestedStream = true;
880 }
881 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -0700882 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
883 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -0700884 if (DEBUG) {
885 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -0700886 }
RoboErikb7c014c2014-07-22 15:58:22 -0700887 return;
RoboErik3c45c292014-07-08 16:47:31 -0700888 }
RoboErik0791e172014-06-08 10:52:32 -0700889 try {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800890 String packageName = getContext().getOpPackageName();
John Spurlockee5ad722015-03-03 16:17:21 -0500891 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
John Spurlock90874332015-03-10 16:00:54 -0400892 flags, packageName, TAG);
RoboErik0791e172014-06-08 10:52:32 -0700893 } catch (RemoteException e) {
894 Log.e(TAG, "Error adjusting default volume.", e);
RoboErikb69ffd42014-05-30 14:57:59 -0700895 }
896 } else {
RoboErik0dac35a2014-08-12 15:48:49 -0700897 session.adjustVolume(direction, flags, getContext().getPackageName(),
RoboErik272e1612014-09-05 11:39:29 -0700898 UserHandle.myUserId(), true);
RoboErikb69ffd42014-05-30 14:57:59 -0700899 }
900 }
901
RoboErik9a9d0b52014-05-20 14:53:39 -0700902 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
903 MediaSessionRecord session) {
904 if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
905 // If the phone app has priority just give it the event
906 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
907 return;
908 }
909 int action = keyEvent.getAction();
910 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
911 if (action == KeyEvent.ACTION_DOWN) {
912 if (keyEvent.getRepeatCount() == 0) {
913 mVoiceButtonDown = true;
914 mVoiceButtonHandled = false;
915 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
916 mVoiceButtonHandled = true;
917 startVoiceInput(needWakeLock);
918 }
919 } else if (action == KeyEvent.ACTION_UP) {
920 if (mVoiceButtonDown) {
921 mVoiceButtonDown = false;
922 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
923 // Resend the down then send this event through
924 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
925 dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
926 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
927 }
928 }
929 }
930 }
931
932 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
933 MediaSessionRecord session) {
934 if (session != null) {
935 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700936 Log.d(TAG, "Sending media key to " + session.toString());
RoboErik9a9d0b52014-05-20 14:53:39 -0700937 }
938 if (needWakeLock) {
939 mKeyEventReceiver.aquireWakeLockLocked();
940 }
941 // If we don't need a wakelock use -1 as the id so we
942 // won't release it later
943 session.sendMediaButton(keyEvent,
944 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
945 mKeyEventReceiver);
946 } else {
RoboErikb214efb2014-07-24 13:20:30 -0700947 // Launch the last PendingIntent we had with priority
948 int userId = ActivityManager.getCurrentUser();
949 UserRecord user = mUserRecords.get(userId);
RoboErikc8f92d12015-01-05 16:48:07 -0800950 if (user.mLastMediaButtonReceiver != null
951 || user.mRestoredMediaButtonReceiver != null) {
RoboErikb214efb2014-07-24 13:20:30 -0700952 if (DEBUG) {
RoboErikc8f92d12015-01-05 16:48:07 -0800953 Log.d(TAG, "Sending media key to last known PendingIntent "
954 + user.mLastMediaButtonReceiver + " or restored Intent "
955 + user.mRestoredMediaButtonReceiver);
RoboErikb214efb2014-07-24 13:20:30 -0700956 }
957 if (needWakeLock) {
958 mKeyEventReceiver.aquireWakeLockLocked();
959 }
960 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
961 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
962 try {
RoboErikc8f92d12015-01-05 16:48:07 -0800963 if (user.mLastMediaButtonReceiver != null) {
964 user.mLastMediaButtonReceiver.send(getContext(),
965 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
966 mediaButtonIntent, mKeyEventReceiver, null);
967 } else {
968 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
969 getContext().sendBroadcastAsUser(mediaButtonIntent,
970 new UserHandle(userId));
971 }
RoboErikb214efb2014-07-24 13:20:30 -0700972 } catch (CanceledException e) {
973 Log.i(TAG, "Error sending key event to media button receiver "
974 + user.mLastMediaButtonReceiver, e);
975 }
976 } else {
977 if (DEBUG) {
978 Log.d(TAG, "Sending media key ordered broadcast");
979 }
980 if (needWakeLock) {
981 mMediaEventWakeLock.acquire();
982 }
983 // Fallback to legacy behavior
984 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
985 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
986 if (needWakeLock) {
987 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
988 WAKELOCK_RELEASE_ON_FINISHED);
989 }
990 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
991 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -0700992 }
RoboErik9a9d0b52014-05-20 14:53:39 -0700993 }
994 }
995
996 private void startVoiceInput(boolean needWakeLock) {
997 Intent voiceIntent = null;
998 // select which type of search to launch:
999 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1000 // - device locked or screen off: action is
1001 // ACTION_VOICE_SEARCH_HANDS_FREE
1002 // with EXTRA_SECURE set to true if the device is securely locked
1003 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1004 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1005 if (!isLocked && pm.isScreenOn()) {
1006 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1007 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1008 } else {
1009 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1010 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1011 isLocked && mKeyguardManager.isKeyguardSecure());
1012 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1013 }
1014 // start the search activity
1015 if (needWakeLock) {
1016 mMediaEventWakeLock.acquire();
1017 }
1018 try {
1019 if (voiceIntent != null) {
1020 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1021 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1022 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1023 }
1024 } catch (ActivityNotFoundException e) {
1025 Log.w(TAG, "No activity for search: " + e);
1026 } finally {
1027 if (needWakeLock) {
1028 mMediaEventWakeLock.release();
1029 }
1030 }
1031 }
1032
1033 private boolean isVoiceKey(int keyCode) {
1034 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1035 }
1036
Jeff Brown38d3feb2015-03-19 18:26:30 -07001037 private boolean isUserSetupComplete() {
1038 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1039 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1040 }
1041
RoboErik9c785402014-11-11 16:52:26 -08001042 // we only handle public stream types, which are 0-5
1043 private boolean isValidLocalStreamType(int streamType) {
1044 return streamType >= AudioManager.STREAM_VOICE_CALL
1045 && streamType <= AudioManager.STREAM_NOTIFICATION;
1046 }
1047
RoboErik418c10c2014-05-19 09:25:25 -07001048 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1049
RoboErikb214efb2014-07-24 13:20:30 -07001050 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1051 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001052 private final Handler mHandler;
1053 private int mRefCount = 0;
1054 private int mLastTimeoutId = 0;
1055
1056 public KeyEventWakeLockReceiver(Handler handler) {
1057 super(handler);
1058 mHandler = handler;
1059 }
1060
1061 public void onTimeout() {
1062 synchronized (mLock) {
1063 if (mRefCount == 0) {
1064 // We've already released it, so just return
1065 return;
1066 }
1067 mLastTimeoutId++;
1068 mRefCount = 0;
1069 releaseWakeLockLocked();
1070 }
1071 }
1072
1073 public void aquireWakeLockLocked() {
1074 if (mRefCount == 0) {
1075 mMediaEventWakeLock.acquire();
1076 }
1077 mRefCount++;
1078 mHandler.removeCallbacks(this);
1079 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1080
1081 }
1082
1083 @Override
1084 public void run() {
1085 onTimeout();
1086 }
1087
RoboErik8a2cfc32014-05-16 11:19:38 -07001088 @Override
1089 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001090 if (resultCode < mLastTimeoutId) {
1091 // Ignore results from calls that were before the last
1092 // timeout, just in case.
1093 return;
1094 } else {
1095 synchronized (mLock) {
1096 if (mRefCount > 0) {
1097 mRefCount--;
1098 if (mRefCount == 0) {
1099 releaseWakeLockLocked();
1100 }
1101 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001102 }
1103 }
1104 }
RoboErik418c10c2014-05-19 09:25:25 -07001105
1106 private void releaseWakeLockLocked() {
1107 mMediaEventWakeLock.release();
1108 mHandler.removeCallbacks(this);
1109 }
RoboErikb214efb2014-07-24 13:20:30 -07001110
1111 @Override
1112 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1113 String resultData, Bundle resultExtras) {
1114 onReceiveResult(resultCode, null);
1115 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001116 };
1117
1118 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1119 @Override
1120 public void onReceive(Context context, Intent intent) {
1121 if (intent == null) {
1122 return;
1123 }
1124 Bundle extras = intent.getExtras();
1125 if (extras == null) {
1126 return;
1127 }
1128 synchronized (mLock) {
1129 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1130 && mMediaEventWakeLock.isHeld()) {
1131 mMediaEventWakeLock.release();
1132 }
1133 }
1134 }
1135 };
RoboErik01fe6612014-02-13 14:19:04 -08001136 }
1137
RoboErik2e7a9162014-06-04 16:53:45 -07001138 final class MessageHandler extends Handler {
1139 private static final int MSG_SESSIONS_CHANGED = 1;
1140
1141 @Override
1142 public void handleMessage(Message msg) {
1143 switch (msg.what) {
1144 case MSG_SESSIONS_CHANGED:
1145 pushSessionsChanged(msg.arg1);
1146 break;
1147 }
1148 }
1149
1150 public void post(int what, int arg1, int arg2) {
1151 obtainMessage(what, arg1, arg2).sendToTarget();
1152 }
1153 }
RoboErik01fe6612014-02-13 14:19:04 -08001154}