blob: 667d02a6d549b6dd80fde65a7e92a87eb854a52d [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;
RoboErik94c716e2014-09-14 13:54:31 -070034import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070035import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070036import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070037import android.media.session.IActiveSessionsListener;
RoboErik07c70772014-03-20 13:33:52 -070038import android.media.session.ISession;
39import android.media.session.ISessionCallback;
40import android.media.session.ISessionManager;
RoboErikd2b8c942014-08-19 11:23:40 -070041import android.media.session.MediaController.PlaybackInfo;
Jeff Browndba34ba2014-06-24 20:46:03 -070042import android.media.session.MediaSession;
RoboErik7c82ced2014-12-04 17:39:08 -080043import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070044import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080045import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070046import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080047import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070048import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070049import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070050import android.os.PowerManager;
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;
55import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070056import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080057import android.text.TextUtils;
58import android.util.Log;
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
62import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070063import com.android.server.Watchdog;
64import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080065
RoboErika278ea72014-04-24 14:49:01 -070066import java.io.FileDescriptor;
67import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080068import java.util.ArrayList;
RoboErike7880d82014-04-30 12:48:25 -070069import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080070
71/**
72 * System implementation of MediaSessionManager
73 */
RoboErika278ea72014-04-24 14:49:01 -070074public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080075 private static final String TAG = "MediaSessionService";
76 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
77
RoboErik418c10c2014-05-19 09:25:25 -070078 private static final int WAKELOCK_TIMEOUT = 5000;
79
RoboErik2610d712015-01-07 11:10:23 -080080 /* package */final IBinder mICallback = new Binder();
81
RoboErik01fe6612014-02-13 14:19:04 -080082 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070083 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080084
RoboErik4646d282014-05-13 10:13:04 -070085 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
86 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070087 private final ArrayList<SessionsListenerRecord> mSessionsListeners
88 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -080089 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -070090 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -070091 private final PowerManager.WakeLock mMediaEventWakeLock;
RoboErik519c7742014-11-18 10:59:09 -080092 private final boolean mUseMasterVolume;
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;
RoboErik2610d712015-01-07 11:10:23 -080096 private AudioManager mAudioManager;
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");
RoboErik519c7742014-11-18 10:59:09 -0800112 mUseMasterVolume = context.getResources().getBoolean(
113 com.android.internal.R.bool.config_useMasterVolume);
RoboErik01fe6612014-02-13 14:19:04 -0800114 }
115
116 @Override
117 public void onStart() {
118 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700119 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700120 mKeyguardManager =
121 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700122 mAudioService = getAudioService();
RoboErik2610d712015-01-07 11:10:23 -0800123 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700124 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700125 mSettingsObserver = new SettingsObserver();
126 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800127
128 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700129 }
130
131 private IAudioService getAudioService() {
132 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
133 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700134 }
135
RoboErika8f95142014-05-05 14:23:49 -0700136 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700137 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700138 if (!mAllSessions.contains(record)) {
139 Log.d(TAG, "Unknown session updated. Ignoring.");
140 return;
141 }
RoboErika8f95142014-05-05 14:23:49 -0700142 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700143 }
RoboErik2e7a9162014-06-04 16:53:45 -0700144 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700145 }
146
RoboErik9c5b7cb2015-01-15 15:09:09 -0800147 /**
148 * Tells the system UI that volume has changed on a remote session.
149 */
150 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
151 if (mRvc == null) {
152 return;
153 }
154 try {
155 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
156 } catch (Exception e) {
157 Log.wtf(TAG, "Error sending volume change to system UI.", e);
158 }
159 }
160
RoboErika8f95142014-05-05 14:23:49 -0700161 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700162 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700163 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700164 if (!mAllSessions.contains(record)) {
165 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
166 return;
167 }
RoboErik2e7a9162014-06-04 16:53:45 -0700168 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
169 }
170 if (updateSessions) {
171 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700172 }
173 }
174
RoboErik19c95182014-06-23 15:38:48 -0700175 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
176 synchronized (mLock) {
177 if (!mAllSessions.contains(record)) {
178 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
179 return;
180 }
181 pushRemoteVolumeUpdateLocked(record.getUserId());
182 }
183 }
184
RoboErika278ea72014-04-24 14:49:01 -0700185 @Override
RoboErik4646d282014-05-13 10:13:04 -0700186 public void onStartUser(int userHandle) {
187 updateUser();
188 }
189
190 @Override
191 public void onSwitchUser(int userHandle) {
192 updateUser();
193 }
194
195 @Override
196 public void onStopUser(int userHandle) {
197 synchronized (mLock) {
198 UserRecord user = mUserRecords.get(userHandle);
199 if (user != null) {
200 destroyUserLocked(user);
201 }
202 }
203 }
204
205 @Override
RoboErika278ea72014-04-24 14:49:01 -0700206 public void monitor() {
207 synchronized (mLock) {
208 // Check for deadlock
209 }
210 }
211
RoboErik4646d282014-05-13 10:13:04 -0700212 protected void enforcePhoneStatePermission(int pid, int uid) {
213 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
214 != PackageManager.PERMISSION_GRANTED) {
215 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
216 }
217 }
218
RoboErik01fe6612014-02-13 14:19:04 -0800219 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700220 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800221 destroySessionLocked(session);
222 }
223 }
224
225 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700226 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800227 destroySessionLocked(session);
228 }
229 }
230
RoboErik4646d282014-05-13 10:13:04 -0700231 private void updateUser() {
232 synchronized (mLock) {
233 int userId = ActivityManager.getCurrentUser();
234 if (mCurrentUserId != userId) {
235 final int oldUserId = mCurrentUserId;
236 mCurrentUserId = userId; // do this first
237
238 UserRecord oldUser = mUserRecords.get(oldUserId);
239 if (oldUser != null) {
240 oldUser.stopLocked();
241 }
242
243 UserRecord newUser = getOrCreateUser(userId);
244 newUser.startLocked();
245 }
246 }
247 }
248
RoboErik7aef77b2014-08-08 15:56:54 -0700249 private void updateActiveSessionListeners() {
250 synchronized (mLock) {
251 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
252 SessionsListenerRecord listener = mSessionsListeners.get(i);
253 try {
254 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
255 listener.mUserId);
256 } catch (SecurityException e) {
257 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
258 + " is no longer authorized. Disconnecting.");
259 mSessionsListeners.remove(i);
260 try {
261 listener.mListener
262 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
263 } catch (Exception e1) {
264 // ignore
265 }
266 }
267 }
268 }
269 }
270
RoboErik4646d282014-05-13 10:13:04 -0700271 /**
272 * Stop the user and unbind from everything.
273 *
274 * @param user The user to dispose of
275 */
276 private void destroyUserLocked(UserRecord user) {
277 user.stopLocked();
278 user.destroyLocked();
279 mUserRecords.remove(user.mUserId);
280 }
281
282 /*
283 * When a session is removed several things need to happen.
284 * 1. We need to remove it from the relevant user.
285 * 2. We need to remove it from the priority stack.
286 * 3. We need to remove it from all sessions.
287 * 4. If this is the system priority session we need to clear it.
288 * 5. We need to unlink to death from the cb binder
289 * 6. We need to tell the session to do any final cleanup (onDestroy)
290 */
RoboErik01fe6612014-02-13 14:19:04 -0800291 private void destroySessionLocked(MediaSessionRecord session) {
RoboErik4646d282014-05-13 10:13:04 -0700292 int userId = session.getUserId();
293 UserRecord user = mUserRecords.get(userId);
294 if (user != null) {
295 user.removeSessionLocked(session);
296 }
297
RoboErika8f95142014-05-05 14:23:49 -0700298 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700299 mAllSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700300
301 try {
302 session.getCallback().asBinder().unlinkToDeath(session, 0);
303 } catch (Exception e) {
304 // ignore exceptions while destroying a session.
305 }
306 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700307
308 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800309 }
310
311 private void enforcePackageName(String packageName, int uid) {
312 if (TextUtils.isEmpty(packageName)) {
313 throw new IllegalArgumentException("packageName may not be empty");
314 }
315 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
316 final int packageCount = packages.length;
317 for (int i = 0; i < packageCount; i++) {
318 if (packageName.equals(packages[i])) {
319 return;
320 }
321 }
322 throw new IllegalArgumentException("packageName is not owned by the calling process");
323 }
324
RoboErike7880d82014-04-30 12:48:25 -0700325 /**
326 * Checks a caller's authorization to register an IRemoteControlDisplay.
327 * Authorization is granted if one of the following is true:
328 * <ul>
329 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
330 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700331 * <li>the caller's listener is one of the enabled notification listeners
332 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700333 * </ul>
334 */
RoboErika5b02322014-05-07 17:05:49 -0700335 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
336 int resolvedUserId) {
RoboErike7880d82014-04-30 12:48:25 -0700337 if (getContext()
338 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
339 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700340 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
341 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700342 throw new SecurityException("Missing permission to control media.");
343 }
344 }
345
RoboErik19c95182014-06-23 15:38:48 -0700346 private void enforceStatusBarPermission(String action, int pid, int uid) {
347 if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
348 pid, uid) != PackageManager.PERMISSION_GRANTED) {
349 throw new SecurityException("Only system ui may " + action);
350 }
351 }
352
RoboErika5b02322014-05-07 17:05:49 -0700353 /**
354 * This checks if the component is an enabled notification listener for the
355 * specified user. Enabled components may only operate on behalf of the user
356 * they're running as.
357 *
358 * @param compName The component that is enabled.
359 * @param userId The user id of the caller.
360 * @param forUserId The user id they're making the request on behalf of.
361 * @return True if the component is enabled, false otherwise
362 */
363 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
364 int forUserId) {
365 if (userId != forUserId) {
366 // You may not access another user's content as an enabled listener.
367 return false;
368 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700369 if (DEBUG) {
370 Log.d(TAG, "Checking if enabled notification listener " + compName);
371 }
RoboErike7880d82014-04-30 12:48:25 -0700372 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700373 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700374 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700375 userId);
RoboErike7880d82014-04-30 12:48:25 -0700376 if (enabledNotifListeners != null) {
377 final String[] components = enabledNotifListeners.split(":");
378 for (int i = 0; i < components.length; i++) {
379 final ComponentName component =
380 ComponentName.unflattenFromString(components[i]);
381 if (component != null) {
382 if (compName.equals(component)) {
383 if (DEBUG) {
384 Log.d(TAG, "ok to get sessions: " + component +
385 " is authorized notification listener");
386 }
387 return true;
388 }
389 }
390 }
391 }
392 if (DEBUG) {
393 Log.d(TAG, "not ok to get sessions, " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700394 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700395 }
396 }
397 return false;
398 }
399
RoboErika5b02322014-05-07 17:05:49 -0700400 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700401 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800402 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700403 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800404 }
405 }
406
RoboErik4646d282014-05-13 10:13:04 -0700407 /*
408 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700409 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700410 * 2. It needs to be added to all sessions.
411 * 3. It needs to be added to the priority stack.
412 * 4. It needs to be added to the relevant user record.
413 */
RoboErika5b02322014-05-07 17:05:49 -0700414 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
415 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700416
RoboErika5b02322014-05-07 17:05:49 -0700417 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
418 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800419 try {
420 cb.asBinder().linkToDeath(session, 0);
421 } catch (RemoteException e) {
422 throw new RuntimeException("Media Session owner died prematurely.", e);
423 }
RoboErik4646d282014-05-13 10:13:04 -0700424
425 mAllSessions.add(session);
RoboErika8f95142014-05-05 14:23:49 -0700426 mPriorityStack.addSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700427
428 UserRecord user = getOrCreateUser(userId);
429 user.addSessionLocked(session);
430
RoboErik2e7a9162014-06-04 16:53:45 -0700431 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
432
RoboErik01fe6612014-02-13 14:19:04 -0800433 if (DEBUG) {
RoboErika5b02322014-05-07 17:05:49 -0700434 Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800435 }
436 return session;
437 }
438
RoboErik4646d282014-05-13 10:13:04 -0700439 private UserRecord getOrCreateUser(int userId) {
440 UserRecord user = mUserRecords.get(userId);
441 if (user == null) {
442 user = new UserRecord(getContext(), userId);
443 mUserRecords.put(userId, user);
444 }
445 return user;
446 }
447
RoboErik2e7a9162014-06-04 16:53:45 -0700448 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
449 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800450 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700451 return i;
452 }
453 }
454 return -1;
455 }
456
RoboErike7880d82014-04-30 12:48:25 -0700457 private boolean isSessionDiscoverable(MediaSessionRecord record) {
RoboErik4646d282014-05-13 10:13:04 -0700458 // TODO probably want to check more than if it's active.
RoboErika8f95142014-05-05 14:23:49 -0700459 return record.isActive();
RoboErike7880d82014-04-30 12:48:25 -0700460 }
461
RoboErik2e7a9162014-06-04 16:53:45 -0700462 private void pushSessionsChanged(int userId) {
463 synchronized (mLock) {
464 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
465 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800466 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700467 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700468 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700469 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700470 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700471 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700472 }
RoboErik19c95182014-06-23 15:38:48 -0700473 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700474 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
475 SessionsListenerRecord record = mSessionsListeners.get(i);
476 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
477 try {
478 record.mListener.onActiveSessionsChanged(tokens);
479 } catch (RemoteException e) {
480 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
481 e);
482 mSessionsListeners.remove(i);
483 }
484 }
485 }
486 }
487 }
488
RoboErik19c95182014-06-23 15:38:48 -0700489 private void pushRemoteVolumeUpdateLocked(int userId) {
490 if (mRvc != null) {
491 try {
492 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
493 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
494 } catch (RemoteException e) {
495 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
496 }
497 }
498 }
499
RoboErikb214efb2014-07-24 13:20:30 -0700500 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
501 PendingIntent receiver = record.getMediaButtonReceiver();
502 UserRecord user = mUserRecords.get(record.getUserId());
503 if (receiver != null && user != null) {
504 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800505 ComponentName component = receiver.getIntent().getComponent();
506 if (component != null && record.getPackageName().equals(component.getPackageName())) {
507 Settings.Secure.putStringForUser(mContentResolver,
508 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
509 record.getUserId());
510 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700511 }
512 }
513
RoboErik4646d282014-05-13 10:13:04 -0700514 /**
515 * Information about a particular user. The contents of this object is
516 * guarded by mLock.
517 */
518 final class UserRecord {
519 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700520 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800521 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700522 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800523 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700524
525 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800526 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700527 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800528 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700529 }
530
531 public void startLocked() {
RoboErik4646d282014-05-13 10:13:04 -0700532 }
533
534 public void stopLocked() {
Jeff Brown01a500e2014-07-10 22:50:50 -0700535 // nothing for now
RoboErik4646d282014-05-13 10:13:04 -0700536 }
537
538 public void destroyLocked() {
539 for (int i = mSessions.size() - 1; i >= 0; i--) {
540 MediaSessionRecord session = mSessions.get(i);
541 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700542 }
543 }
544
RoboErik4646d282014-05-13 10:13:04 -0700545 public ArrayList<MediaSessionRecord> getSessionsLocked() {
546 return mSessions;
547 }
548
549 public void addSessionLocked(MediaSessionRecord session) {
550 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700551 }
552
553 public void removeSessionLocked(MediaSessionRecord session) {
554 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700555 }
556
557 public void dumpLocked(PrintWriter pw, String prefix) {
558 pw.println(prefix + "Record for user " + mUserId);
559 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700560 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800561 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jeff Brown01a500e2014-07-10 22:50:50 -0700562 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700563 pw.println(indent + size + " Sessions:");
564 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700565 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700566 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700567 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700568 }
569 }
RoboErikc8f92d12015-01-05 16:48:07 -0800570
571 private void restoreMediaButtonReceiver() {
572 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
573 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
574 if (!TextUtils.isEmpty(receiverName)) {
575 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
576 if (eventReceiver == null) {
577 // an invalid name was persisted
578 return;
579 }
580 mRestoredMediaButtonReceiver = eventReceiver;
581 }
582 }
RoboErik4646d282014-05-13 10:13:04 -0700583 }
584
RoboErik2e7a9162014-06-04 16:53:45 -0700585 final class SessionsListenerRecord implements IBinder.DeathRecipient {
586 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700587 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700588 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700589 private final int mPid;
590 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700591
RoboErik7aef77b2014-08-08 15:56:54 -0700592 public SessionsListenerRecord(IActiveSessionsListener listener,
593 ComponentName componentName,
594 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700595 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700596 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700597 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700598 mPid = pid;
599 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700600 }
601
602 @Override
603 public void binderDied() {
604 synchronized (mLock) {
605 mSessionsListeners.remove(this);
606 }
607 }
608 }
609
RoboErik7aef77b2014-08-08 15:56:54 -0700610 final class SettingsObserver extends ContentObserver {
611 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
612 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
613
614 private SettingsObserver() {
615 super(null);
616 }
617
618 private void observe() {
619 mContentResolver.registerContentObserver(mSecureSettingsUri,
620 false, this, UserHandle.USER_ALL);
621 }
622
623 @Override
624 public void onChange(boolean selfChange, Uri uri) {
625 updateActiveSessionListeners();
626 }
627 }
628
RoboErik07c70772014-03-20 13:33:52 -0700629 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700630 private static final String EXTRA_WAKELOCK_ACQUIRED =
631 "android.media.AudioService.WAKELOCK_ACQUIRED";
632 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
633
RoboErik9a9d0b52014-05-20 14:53:39 -0700634 private boolean mVoiceButtonDown = false;
635 private boolean mVoiceButtonHandled = false;
636
RoboErik07c70772014-03-20 13:33:52 -0700637 @Override
RoboErika5b02322014-05-07 17:05:49 -0700638 public ISession createSession(String packageName, ISessionCallback cb, String tag,
639 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800640 final int pid = Binder.getCallingPid();
641 final int uid = Binder.getCallingUid();
642 final long token = Binder.clearCallingIdentity();
643 try {
644 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700645 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
646 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800647 if (cb == null) {
648 throw new IllegalArgumentException("Controller callback cannot be null");
649 }
RoboErika5b02322014-05-07 17:05:49 -0700650 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
651 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700652 } finally {
653 Binder.restoreCallingIdentity(token);
654 }
655 }
656
657 @Override
RoboErika5b02322014-05-07 17:05:49 -0700658 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700659 final int pid = Binder.getCallingPid();
660 final int uid = Binder.getCallingUid();
661 final long token = Binder.clearCallingIdentity();
662
663 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700664 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700665 ArrayList<IBinder> binders = new ArrayList<IBinder>();
666 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700667 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700668 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700669 int size = records.size();
670 for (int i = 0; i < size; i++) {
671 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700672 }
673 }
674 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800675 } finally {
676 Binder.restoreCallingIdentity(token);
677 }
678 }
RoboErika278ea72014-04-24 14:49:01 -0700679
RoboErik2e7a9162014-06-04 16:53:45 -0700680 @Override
681 public void addSessionsListener(IActiveSessionsListener listener,
682 ComponentName componentName, int userId) throws RemoteException {
683 final int pid = Binder.getCallingPid();
684 final int uid = Binder.getCallingUid();
685 final long token = Binder.clearCallingIdentity();
686
687 try {
688 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
689 synchronized (mLock) {
690 int index = findIndexOfSessionsListenerLocked(listener);
691 if (index != -1) {
692 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
693 return;
694 }
695 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700696 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700697 try {
698 listener.asBinder().linkToDeath(record, 0);
699 } catch (RemoteException e) {
700 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
701 return;
702 }
703 mSessionsListeners.add(record);
704 }
705 } finally {
706 Binder.restoreCallingIdentity(token);
707 }
708 }
709
710 @Override
711 public void removeSessionsListener(IActiveSessionsListener listener)
712 throws RemoteException {
713 synchronized (mLock) {
714 int index = findIndexOfSessionsListenerLocked(listener);
715 if (index != -1) {
716 SessionsListenerRecord record = mSessionsListeners.remove(index);
717 try {
718 record.mListener.asBinder().unlinkToDeath(record, 0);
719 } catch (Exception e) {
720 // ignore exceptions, the record is being removed
721 }
722 }
723 }
724 }
725
RoboErik8a2cfc32014-05-16 11:19:38 -0700726 /**
727 * Handles the dispatching of the media button events to one of the
728 * registered listeners, or if there was none, broadcast an
729 * ACTION_MEDIA_BUTTON intent to the rest of the system.
730 *
731 * @param keyEvent a non-null KeyEvent whose key code is one of the
732 * supported media buttons
733 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
734 * while this key event is dispatched.
735 */
736 @Override
737 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
738 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
739 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
740 return;
741 }
742 final int pid = Binder.getCallingPid();
743 final int uid = Binder.getCallingUid();
744 final long token = Binder.clearCallingIdentity();
745
746 try {
RoboErik8a2cfc32014-05-16 11:19:38 -0700747 synchronized (mLock) {
RoboErik870c5a62014-12-02 15:08:26 -0800748 // If we don't have a media button receiver to fall back on
749 // include non-playing sessions for dispatching
RoboErikc8f92d12015-01-05 16:48:07 -0800750 UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
751 boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null
752 && ur.mRestoredMediaButtonReceiver == null;
RoboErik9a9d0b52014-05-20 14:53:39 -0700753 MediaSessionRecord session = mPriorityStack
RoboErik870c5a62014-12-02 15:08:26 -0800754 .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
RoboErik9a9d0b52014-05-20 14:53:39 -0700755 if (isVoiceKey(keyEvent.getKeyCode())) {
756 handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700757 } else {
RoboErik9a9d0b52014-05-20 14:53:39 -0700758 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700759 }
760 }
761 } finally {
762 Binder.restoreCallingIdentity(token);
763 }
764 }
765
RoboErika278ea72014-04-24 14:49:01 -0700766 @Override
RoboErik7c82ced2014-12-04 17:39:08 -0800767 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -0700768 final int pid = Binder.getCallingPid();
769 final int uid = Binder.getCallingUid();
770 final long token = Binder.clearCallingIdentity();
771 try {
772 synchronized (mLock) {
773 MediaSessionRecord session = mPriorityStack
774 .getDefaultVolumeSession(mCurrentUserId);
RoboErik1ff5b162014-07-15 17:23:18 -0700775 dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
RoboErikb69ffd42014-05-30 14:57:59 -0700776 }
777 } finally {
778 Binder.restoreCallingIdentity(token);
779 }
780 }
781
782 @Override
RoboErik19c95182014-06-23 15:38:48 -0700783 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
784 final int pid = Binder.getCallingPid();
785 final int uid = Binder.getCallingUid();
786 final long token = Binder.clearCallingIdentity();
787 try {
788 enforceStatusBarPermission("listen for volume changes", pid, uid);
789 mRvc = rvc;
790 } finally {
791 Binder.restoreCallingIdentity(token);
792 }
793 }
794
795 @Override
RoboErikde9ba392014-09-26 12:51:01 -0700796 public boolean isGlobalPriorityActive() {
797 return mPriorityStack.isGlobalPriorityActive();
798 }
799
800 @Override
RoboErika278ea72014-04-24 14:49:01 -0700801 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
802 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
803 != PackageManager.PERMISSION_GRANTED) {
804 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
805 + Binder.getCallingPid()
806 + ", uid=" + Binder.getCallingUid());
807 return;
808 }
809
810 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
811 pw.println();
812
813 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -0800814 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErik4646d282014-05-13 10:13:04 -0700815 int count = mAllSessions.size();
RoboErika8f95142014-05-05 14:23:49 -0700816 pw.println(count + " Sessions:");
RoboErika278ea72014-04-24 14:49:01 -0700817 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -0700818 mAllSessions.get(i).dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700819 pw.println();
RoboErika278ea72014-04-24 14:49:01 -0700820 }
RoboErika5b02322014-05-07 17:05:49 -0700821 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -0700822
RoboErik4646d282014-05-13 10:13:04 -0700823 pw.println("User Records:");
824 count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -0700825 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -0700826 UserRecord user = mUserRecords.get(i);
827 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -0700828 }
829 }
830 }
RoboErik8a2cfc32014-05-16 11:19:38 -0700831
RoboErik2e7a9162014-06-04 16:53:45 -0700832 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
833 final int uid) {
834 String packageName = null;
835 if (componentName != null) {
836 // If they gave us a component name verify they own the
837 // package
838 packageName = componentName.getPackageName();
839 enforcePackageName(packageName, uid);
840 }
841 // Check that they can make calls on behalf of the user and
842 // get the final user id
843 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
844 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
845 // Check if they have the permissions or their component is
846 // enabled for the user they're calling from.
847 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
848 return resolvedUserId;
849 }
850
RoboErik1ff5b162014-07-15 17:23:18 -0700851 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
RoboErikb69ffd42014-05-30 14:57:59 -0700852 MediaSessionRecord session) {
RoboErikb69ffd42014-05-30 14:57:59 -0700853 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700854 String description = session == null ? null : session.toString();
855 Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
856 + flags + ", suggestedStream=" + suggestedStream);
RoboErikb69ffd42014-05-30 14:57:59 -0700857
858 }
RoboErik9c785402014-11-11 16:52:26 -0800859 boolean preferSuggestedStream = false;
860 if (isValidLocalStreamType(suggestedStream)
861 && AudioSystem.isStreamActive(suggestedStream, 0)) {
862 preferSuggestedStream = true;
863 }
864 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -0700865 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
866 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -0700867 if (DEBUG) {
868 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -0700869 }
RoboErikb7c014c2014-07-22 15:58:22 -0700870 return;
RoboErik3c45c292014-07-08 16:47:31 -0700871 }
RoboErik0791e172014-06-08 10:52:32 -0700872 try {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800873 String packageName = getContext().getOpPackageName();
RoboErik519c7742014-11-18 10:59:09 -0800874 if (mUseMasterVolume) {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800875 boolean isMasterMute = mAudioService.isMasterMute();
RoboErik7c82ced2014-12-04 17:39:08 -0800876 if (direction == MediaSessionManager.DIRECTION_MUTE) {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800877 mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback);
RoboErik7c82ced2014-12-04 17:39:08 -0800878 } else {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800879 mAudioService.adjustMasterVolume(direction, flags, packageName);
Sungsoo Limd5489222015-01-05 09:08:34 +0900880 // Do not call setMasterMute when direction = 0 which is used just to
881 // show the UI.
Sungsoo Lim48248c82014-12-24 17:30:00 +0900882 if (isMasterMute && direction != 0) {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800883 mAudioService.setMasterMute(false, flags, packageName, mICallback);
884 }
RoboErik7c82ced2014-12-04 17:39:08 -0800885 }
RoboErik519c7742014-11-18 10:59:09 -0800886 } else {
Eric Laurent2b5208c2014-12-19 10:07:03 -0800887 boolean isStreamMute = mAudioService.isStreamMute(suggestedStream);
RoboErik7c82ced2014-12-04 17:39:08 -0800888 if (direction == MediaSessionManager.DIRECTION_MUTE) {
RoboErik2610d712015-01-07 11:10:23 -0800889 mAudioManager.setStreamMute(suggestedStream, !isStreamMute);
RoboErik7c82ced2014-12-04 17:39:08 -0800890 } else {
891 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
Eric Laurent2b5208c2014-12-19 10:07:03 -0800892 flags, packageName);
Sungsoo Limd5489222015-01-05 09:08:34 +0900893 // Do not call setStreamMute when direction = 0 which is used just to
894 // show the UI.
Sungsoo Lim48248c82014-12-24 17:30:00 +0900895 if (isStreamMute && direction != 0) {
RoboErik2610d712015-01-07 11:10:23 -0800896 mAudioManager.setStreamMute(suggestedStream, false);
Eric Laurent2b5208c2014-12-19 10:07:03 -0800897 }
RoboErik7c82ced2014-12-04 17:39:08 -0800898 }
RoboErik519c7742014-11-18 10:59:09 -0800899 }
RoboErik0791e172014-06-08 10:52:32 -0700900 } catch (RemoteException e) {
901 Log.e(TAG, "Error adjusting default volume.", e);
RoboErikb69ffd42014-05-30 14:57:59 -0700902 }
903 } else {
RoboErik0dac35a2014-08-12 15:48:49 -0700904 session.adjustVolume(direction, flags, getContext().getPackageName(),
RoboErik272e1612014-09-05 11:39:29 -0700905 UserHandle.myUserId(), true);
RoboErikb69ffd42014-05-30 14:57:59 -0700906 }
907 }
908
RoboErik9a9d0b52014-05-20 14:53:39 -0700909 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
910 MediaSessionRecord session) {
911 if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
912 // If the phone app has priority just give it the event
913 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
914 return;
915 }
916 int action = keyEvent.getAction();
917 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
918 if (action == KeyEvent.ACTION_DOWN) {
919 if (keyEvent.getRepeatCount() == 0) {
920 mVoiceButtonDown = true;
921 mVoiceButtonHandled = false;
922 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
923 mVoiceButtonHandled = true;
924 startVoiceInput(needWakeLock);
925 }
926 } else if (action == KeyEvent.ACTION_UP) {
927 if (mVoiceButtonDown) {
928 mVoiceButtonDown = false;
929 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
930 // Resend the down then send this event through
931 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
932 dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
933 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
934 }
935 }
936 }
937 }
938
939 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
940 MediaSessionRecord session) {
941 if (session != null) {
942 if (DEBUG) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700943 Log.d(TAG, "Sending media key to " + session.toString());
RoboErik9a9d0b52014-05-20 14:53:39 -0700944 }
945 if (needWakeLock) {
946 mKeyEventReceiver.aquireWakeLockLocked();
947 }
948 // If we don't need a wakelock use -1 as the id so we
949 // won't release it later
950 session.sendMediaButton(keyEvent,
951 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
952 mKeyEventReceiver);
953 } else {
RoboErikb214efb2014-07-24 13:20:30 -0700954 // Launch the last PendingIntent we had with priority
955 int userId = ActivityManager.getCurrentUser();
956 UserRecord user = mUserRecords.get(userId);
RoboErikc8f92d12015-01-05 16:48:07 -0800957 if (user.mLastMediaButtonReceiver != null
958 || user.mRestoredMediaButtonReceiver != null) {
RoboErikb214efb2014-07-24 13:20:30 -0700959 if (DEBUG) {
RoboErikc8f92d12015-01-05 16:48:07 -0800960 Log.d(TAG, "Sending media key to last known PendingIntent "
961 + user.mLastMediaButtonReceiver + " or restored Intent "
962 + user.mRestoredMediaButtonReceiver);
RoboErikb214efb2014-07-24 13:20:30 -0700963 }
964 if (needWakeLock) {
965 mKeyEventReceiver.aquireWakeLockLocked();
966 }
967 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
968 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
969 try {
RoboErikc8f92d12015-01-05 16:48:07 -0800970 if (user.mLastMediaButtonReceiver != null) {
971 user.mLastMediaButtonReceiver.send(getContext(),
972 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
973 mediaButtonIntent, mKeyEventReceiver, null);
974 } else {
975 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
976 getContext().sendBroadcastAsUser(mediaButtonIntent,
977 new UserHandle(userId));
978 }
RoboErikb214efb2014-07-24 13:20:30 -0700979 } catch (CanceledException e) {
980 Log.i(TAG, "Error sending key event to media button receiver "
981 + user.mLastMediaButtonReceiver, e);
982 }
983 } else {
984 if (DEBUG) {
985 Log.d(TAG, "Sending media key ordered broadcast");
986 }
987 if (needWakeLock) {
988 mMediaEventWakeLock.acquire();
989 }
990 // Fallback to legacy behavior
991 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
992 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
993 if (needWakeLock) {
994 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
995 WAKELOCK_RELEASE_ON_FINISHED);
996 }
997 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
998 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -0700999 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001000 }
1001 }
1002
1003 private void startVoiceInput(boolean needWakeLock) {
1004 Intent voiceIntent = null;
1005 // select which type of search to launch:
1006 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1007 // - device locked or screen off: action is
1008 // ACTION_VOICE_SEARCH_HANDS_FREE
1009 // with EXTRA_SECURE set to true if the device is securely locked
1010 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1011 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1012 if (!isLocked && pm.isScreenOn()) {
1013 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1014 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1015 } else {
1016 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1017 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1018 isLocked && mKeyguardManager.isKeyguardSecure());
1019 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1020 }
1021 // start the search activity
1022 if (needWakeLock) {
1023 mMediaEventWakeLock.acquire();
1024 }
1025 try {
1026 if (voiceIntent != null) {
1027 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1028 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1029 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1030 }
1031 } catch (ActivityNotFoundException e) {
1032 Log.w(TAG, "No activity for search: " + e);
1033 } finally {
1034 if (needWakeLock) {
1035 mMediaEventWakeLock.release();
1036 }
1037 }
1038 }
1039
1040 private boolean isVoiceKey(int keyCode) {
1041 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1042 }
1043
RoboErik9c785402014-11-11 16:52:26 -08001044 // we only handle public stream types, which are 0-5
1045 private boolean isValidLocalStreamType(int streamType) {
1046 return streamType >= AudioManager.STREAM_VOICE_CALL
1047 && streamType <= AudioManager.STREAM_NOTIFICATION;
1048 }
1049
RoboErik418c10c2014-05-19 09:25:25 -07001050 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1051
RoboErikb214efb2014-07-24 13:20:30 -07001052 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1053 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001054 private final Handler mHandler;
1055 private int mRefCount = 0;
1056 private int mLastTimeoutId = 0;
1057
1058 public KeyEventWakeLockReceiver(Handler handler) {
1059 super(handler);
1060 mHandler = handler;
1061 }
1062
1063 public void onTimeout() {
1064 synchronized (mLock) {
1065 if (mRefCount == 0) {
1066 // We've already released it, so just return
1067 return;
1068 }
1069 mLastTimeoutId++;
1070 mRefCount = 0;
1071 releaseWakeLockLocked();
1072 }
1073 }
1074
1075 public void aquireWakeLockLocked() {
1076 if (mRefCount == 0) {
1077 mMediaEventWakeLock.acquire();
1078 }
1079 mRefCount++;
1080 mHandler.removeCallbacks(this);
1081 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1082
1083 }
1084
1085 @Override
1086 public void run() {
1087 onTimeout();
1088 }
1089
RoboErik8a2cfc32014-05-16 11:19:38 -07001090 @Override
1091 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001092 if (resultCode < mLastTimeoutId) {
1093 // Ignore results from calls that were before the last
1094 // timeout, just in case.
1095 return;
1096 } else {
1097 synchronized (mLock) {
1098 if (mRefCount > 0) {
1099 mRefCount--;
1100 if (mRefCount == 0) {
1101 releaseWakeLockLocked();
1102 }
1103 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001104 }
1105 }
1106 }
RoboErik418c10c2014-05-19 09:25:25 -07001107
1108 private void releaseWakeLockLocked() {
1109 mMediaEventWakeLock.release();
1110 mHandler.removeCallbacks(this);
1111 }
RoboErikb214efb2014-07-24 13:20:30 -07001112
1113 @Override
1114 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1115 String resultData, Bundle resultExtras) {
1116 onReceiveResult(resultCode, null);
1117 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001118 };
1119
1120 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1121 @Override
1122 public void onReceive(Context context, Intent intent) {
1123 if (intent == null) {
1124 return;
1125 }
1126 Bundle extras = intent.getExtras();
1127 if (extras == null) {
1128 return;
1129 }
1130 synchronized (mLock) {
1131 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1132 && mMediaEventWakeLock.isHeld()) {
1133 mMediaEventWakeLock.release();
1134 }
1135 }
1136 }
1137 };
RoboErik01fe6612014-02-13 14:19:04 -08001138 }
1139
RoboErik2e7a9162014-06-04 16:53:45 -07001140 final class MessageHandler extends Handler {
1141 private static final int MSG_SESSIONS_CHANGED = 1;
1142
1143 @Override
1144 public void handleMessage(Message msg) {
1145 switch (msg.what) {
1146 case MSG_SESSIONS_CHANGED:
1147 pushSessionsChanged(msg.arg1);
1148 break;
1149 }
1150 }
1151
1152 public void post(int what, int arg1, int arg2) {
1153 obtainMessage(what, arg1, arg2).sendToTarget();
1154 }
1155 }
RoboErik01fe6612014-02-13 14:19:04 -08001156}