blob: 04d5d9f84aa8a43c3112f127341a99aa39d94f59 [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;
Jaewan Kim50269362016-12-23 11:22:02 +090020import android.annotation.NonNull;
RoboErik8a2cfc32014-05-16 11:19:38 -070021import android.app.Activity;
RoboErike7880d82014-04-30 12:48:25 -070022import android.app.ActivityManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070023import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070024import android.app.PendingIntent;
25import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070026import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070027import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070028import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070029import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080030import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070031import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070032import android.content.pm.PackageManager;
RoboErik7aef77b2014-08-08 15:56:54 -070033import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070034import android.media.AudioManager;
John Spurlockeb69e242015-02-17 17:15:04 -050035import android.media.AudioManagerInternal;
RoboErik94c716e2014-09-14 13:54:31 -070036import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070037import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070038import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070039import android.media.session.IActiveSessionsListener;
Jaewan Kim50269362016-12-23 11:22:02 +090040import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070041import android.media.session.ISession;
42import android.media.session.ISessionCallback;
43import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070044import android.media.session.MediaSession;
RoboErik7aef77b2014-08-08 15:56:54 -070045import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080046import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070047import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080048import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070049import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070050import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070051import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090052import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080053import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070054import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070055import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070056import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090057import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070058import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070059import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080060import android.text.TextUtils;
61import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070062import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070063import android.util.SparseArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070064import android.view.KeyEvent;
RoboErik01fe6612014-02-13 14:19:04 -080065
John Spurlockeb69e242015-02-17 17:15:04 -050066import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080067import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070068import com.android.server.Watchdog;
69import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080070
RoboErika278ea72014-04-24 14:49:01 -070071import java.io.FileDescriptor;
72import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080073import java.util.ArrayList;
Jaewan Kim8f729082016-06-21 12:36:26 +090074import java.util.Arrays;
RoboErike7880d82014-04-30 12:48:25 -070075import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080076
77/**
78 * System implementation of MediaSessionManager
79 */
RoboErika278ea72014-04-24 14:49:01 -070080public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080081 private static final String TAG = "MediaSessionService";
82 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090083 // Leave log for key event always.
84 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080085
RoboErik418c10c2014-05-19 09:25:25 -070086 private static final int WAKELOCK_TIMEOUT = 5000;
87
RoboErik2610d712015-01-07 11:10:23 -080088 /* package */final IBinder mICallback = new Binder();
89
RoboErik01fe6612014-02-13 14:19:04 -080090 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070091 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080092
RoboErik4646d282014-05-13 10:13:04 -070093 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
94 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070095 private final ArrayList<SessionsListenerRecord> mSessionsListeners
96 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -080097 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -070098 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -070099 private final PowerManager.WakeLock mMediaEventWakeLock;
RoboErik01fe6612014-02-13 14:19:04 -0800100
RoboErik9a9d0b52014-05-20 14:53:39 -0700101 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700102 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -0500103 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700104 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700105 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -0700106
Jaewan Kim8f729082016-06-21 12:36:26 +0900107 // List of user IDs running in the foreground.
108 // Multiple users can be in the foreground if the work profile is on.
109 private final List<Integer> mCurrentUserIdList = new ArrayList<>();
RoboErike7880d82014-04-30 12:48:25 -0700110
RoboErik19c95182014-06-23 15:38:48 -0700111 // Used to notify system UI when remote volume was changed. TODO find a
112 // better way to handle this.
113 private IRemoteVolumeController mRvc;
114
RoboErik01fe6612014-02-13 14:19:04 -0800115 public MediaSessionService(Context context) {
116 super(context);
117 mSessionManagerImpl = new SessionManagerImpl();
RoboErika8f95142014-05-05 14:23:49 -0700118 mPriorityStack = new MediaSessionStack();
RoboErik8a2cfc32014-05-16 11:19:38 -0700119 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
120 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
RoboErik01fe6612014-02-13 14:19:04 -0800121 }
122
123 @Override
124 public void onStart() {
125 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700126 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700127 mKeyguardManager =
128 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700129 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500130 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700131 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700132 mSettingsObserver = new SettingsObserver();
133 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800134
135 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700136 }
137
138 private IAudioService getAudioService() {
139 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
140 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700141 }
142
RoboErika8f95142014-05-05 14:23:49 -0700143 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700144 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700145 if (!mAllSessions.contains(record)) {
146 Log.d(TAG, "Unknown session updated. Ignoring.");
147 return;
148 }
RoboErika8f95142014-05-05 14:23:49 -0700149 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700150 }
RoboErik2e7a9162014-06-04 16:53:45 -0700151 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700152 }
153
RoboErik9c5b7cb2015-01-15 15:09:09 -0800154 /**
155 * Tells the system UI that volume has changed on a remote session.
156 */
157 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
158 if (mRvc == null) {
159 return;
160 }
161 try {
162 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
163 } catch (Exception e) {
164 Log.wtf(TAG, "Error sending volume change to system UI.", e);
165 }
166 }
167
RoboErika8f95142014-05-05 14:23:49 -0700168 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700169 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700170 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700171 if (!mAllSessions.contains(record)) {
172 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
173 return;
174 }
RoboErik2e7a9162014-06-04 16:53:45 -0700175 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
176 }
177 if (updateSessions) {
178 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700179 }
180 }
181
RoboErik19c95182014-06-23 15:38:48 -0700182 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
183 synchronized (mLock) {
184 if (!mAllSessions.contains(record)) {
185 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
186 return;
187 }
188 pushRemoteVolumeUpdateLocked(record.getUserId());
189 }
190 }
191
RoboErika278ea72014-04-24 14:49:01 -0700192 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900193 public void onStartUser(int userId) {
194 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700195 updateUser();
196 }
197
198 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900199 public void onSwitchUser(int userId) {
200 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700201 updateUser();
202 }
203
204 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900205 public void onStopUser(int userId) {
206 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700207 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900208 UserRecord user = mUserRecords.get(userId);
RoboErik4646d282014-05-13 10:13:04 -0700209 if (user != null) {
210 destroyUserLocked(user);
211 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900212 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700213 }
214 }
215
216 @Override
RoboErika278ea72014-04-24 14:49:01 -0700217 public void monitor() {
218 synchronized (mLock) {
219 // Check for deadlock
220 }
221 }
222
RoboErik4646d282014-05-13 10:13:04 -0700223 protected void enforcePhoneStatePermission(int pid, int uid) {
224 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
225 != PackageManager.PERMISSION_GRANTED) {
226 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
227 }
228 }
229
RoboErik01fe6612014-02-13 14:19:04 -0800230 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700231 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800232 destroySessionLocked(session);
233 }
234 }
235
236 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700237 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800238 destroySessionLocked(session);
239 }
240 }
241
RoboErik4646d282014-05-13 10:13:04 -0700242 private void updateUser() {
243 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900244 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
245 int currentUser = ActivityManager.getCurrentUser();
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700246 // Include all profiles even though they aren't yet enabled to handle work profile case.
247 int[] userIds = manager.getProfileIdsWithDisabled(currentUser);
Jaewan Kim8f729082016-06-21 12:36:26 +0900248 mCurrentUserIdList.clear();
249 if (userIds != null && userIds.length > 0) {
250 for (int userId : userIds) {
251 mCurrentUserIdList.add(userId);
RoboErik4646d282014-05-13 10:13:04 -0700252 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900253 } else {
254 // This shouldn't happen.
255 Log.w(TAG, "Failed to get enabled profiles.");
256 mCurrentUserIdList.add(currentUser);
257 }
258 for (int userId : mCurrentUserIdList) {
259 if (mUserRecords.get(userId) == null) {
260 mUserRecords.put(userId, new UserRecord(getContext(), userId));
261 }
RoboErik4646d282014-05-13 10:13:04 -0700262 }
263 }
264 }
265
RoboErik7aef77b2014-08-08 15:56:54 -0700266 private void updateActiveSessionListeners() {
267 synchronized (mLock) {
268 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
269 SessionsListenerRecord listener = mSessionsListeners.get(i);
270 try {
271 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
272 listener.mUserId);
273 } catch (SecurityException e) {
274 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
275 + " is no longer authorized. Disconnecting.");
276 mSessionsListeners.remove(i);
277 try {
278 listener.mListener
279 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
280 } catch (Exception e1) {
281 // ignore
282 }
283 }
284 }
285 }
286 }
287
RoboErik4646d282014-05-13 10:13:04 -0700288 /**
289 * Stop the user and unbind from everything.
290 *
291 * @param user The user to dispose of
292 */
293 private void destroyUserLocked(UserRecord user) {
RoboErik4646d282014-05-13 10:13:04 -0700294 user.destroyLocked();
295 mUserRecords.remove(user.mUserId);
296 }
297
298 /*
299 * When a session is removed several things need to happen.
300 * 1. We need to remove it from the relevant user.
301 * 2. We need to remove it from the priority stack.
302 * 3. We need to remove it from all sessions.
303 * 4. If this is the system priority session we need to clear it.
304 * 5. We need to unlink to death from the cb binder
305 * 6. We need to tell the session to do any final cleanup (onDestroy)
306 */
RoboErik01fe6612014-02-13 14:19:04 -0800307 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900308 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900309 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900310 }
RoboErik4646d282014-05-13 10:13:04 -0700311 int userId = session.getUserId();
312 UserRecord user = mUserRecords.get(userId);
313 if (user != null) {
314 user.removeSessionLocked(session);
315 }
316
RoboErika8f95142014-05-05 14:23:49 -0700317 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700318 mAllSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700319
320 try {
321 session.getCallback().asBinder().unlinkToDeath(session, 0);
322 } catch (Exception e) {
323 // ignore exceptions while destroying a session.
324 }
325 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700326
327 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800328 }
329
330 private void enforcePackageName(String packageName, int uid) {
331 if (TextUtils.isEmpty(packageName)) {
332 throw new IllegalArgumentException("packageName may not be empty");
333 }
334 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
335 final int packageCount = packages.length;
336 for (int i = 0; i < packageCount; i++) {
337 if (packageName.equals(packages[i])) {
338 return;
339 }
340 }
341 throw new IllegalArgumentException("packageName is not owned by the calling process");
342 }
343
RoboErike7880d82014-04-30 12:48:25 -0700344 /**
345 * Checks a caller's authorization to register an IRemoteControlDisplay.
346 * Authorization is granted if one of the following is true:
347 * <ul>
348 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
349 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700350 * <li>the caller's listener is one of the enabled notification listeners
351 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700352 * </ul>
353 */
RoboErika5b02322014-05-07 17:05:49 -0700354 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
355 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500356 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700357 if (getContext()
358 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
359 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700360 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
361 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700362 throw new SecurityException("Missing permission to control media.");
363 }
364 }
365
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500366 private boolean isCurrentVolumeController(int uid, int pid) {
367 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
368 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500369 }
370
371 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500372 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700373 throw new SecurityException("Only system ui may " + action);
374 }
375 }
376
RoboErika5b02322014-05-07 17:05:49 -0700377 /**
378 * This checks if the component is an enabled notification listener for the
379 * specified user. Enabled components may only operate on behalf of the user
380 * they're running as.
381 *
382 * @param compName The component that is enabled.
383 * @param userId The user id of the caller.
384 * @param forUserId The user id they're making the request on behalf of.
385 * @return True if the component is enabled, false otherwise
386 */
387 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
388 int forUserId) {
389 if (userId != forUserId) {
390 // You may not access another user's content as an enabled listener.
391 return false;
392 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700393 if (DEBUG) {
394 Log.d(TAG, "Checking if enabled notification listener " + compName);
395 }
RoboErike7880d82014-04-30 12:48:25 -0700396 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700397 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700398 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700399 userId);
RoboErike7880d82014-04-30 12:48:25 -0700400 if (enabledNotifListeners != null) {
401 final String[] components = enabledNotifListeners.split(":");
402 for (int i = 0; i < components.length; i++) {
403 final ComponentName component =
404 ComponentName.unflattenFromString(components[i]);
405 if (component != null) {
406 if (compName.equals(component)) {
407 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900408 Log.d(TAG, "ok to get sessions. " + component +
RoboErike7880d82014-04-30 12:48:25 -0700409 " is authorized notification listener");
410 }
411 return true;
412 }
413 }
414 }
415 }
416 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900417 Log.d(TAG, "not ok to get sessions. " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700418 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700419 }
420 }
421 return false;
422 }
423
RoboErika5b02322014-05-07 17:05:49 -0700424 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700425 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800426 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700427 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800428 }
429 }
430
RoboErik4646d282014-05-13 10:13:04 -0700431 /*
432 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700433 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700434 * 2. It needs to be added to all sessions.
435 * 3. It needs to be added to the priority stack.
436 * 4. It needs to be added to the relevant user record.
437 */
RoboErika5b02322014-05-07 17:05:49 -0700438 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
439 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700440
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700441 UserRecord user = mUserRecords.get(userId);
442 if (user == null) {
443 Log.wtf(TAG, "Request from invalid user: " + userId);
444 throw new RuntimeException("Session request from invalid user.");
445 }
446
RoboErika5b02322014-05-07 17:05:49 -0700447 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
448 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800449 try {
450 cb.asBinder().linkToDeath(session, 0);
451 } catch (RemoteException e) {
452 throw new RuntimeException("Media Session owner died prematurely.", e);
453 }
RoboErik4646d282014-05-13 10:13:04 -0700454
455 mAllSessions.add(session);
Jaewan Kim8f729082016-06-21 12:36:26 +0900456 mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
RoboErik4646d282014-05-13 10:13:04 -0700457 user.addSessionLocked(session);
458
RoboErik2e7a9162014-06-04 16:53:45 -0700459 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
460
RoboErik01fe6612014-02-13 14:19:04 -0800461 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900462 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800463 }
464 return session;
465 }
466
RoboErik2e7a9162014-06-04 16:53:45 -0700467 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
468 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800469 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700470 return i;
471 }
472 }
473 return -1;
474 }
475
RoboErik2e7a9162014-06-04 16:53:45 -0700476 private void pushSessionsChanged(int userId) {
477 synchronized (mLock) {
478 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
479 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800480 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700481 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700482 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700483 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700484 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700485 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700486 }
RoboErik19c95182014-06-23 15:38:48 -0700487 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700488 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
489 SessionsListenerRecord record = mSessionsListeners.get(i);
490 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
491 try {
492 record.mListener.onActiveSessionsChanged(tokens);
493 } catch (RemoteException e) {
494 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
495 e);
496 mSessionsListeners.remove(i);
497 }
498 }
499 }
500 }
501 }
502
RoboErik19c95182014-06-23 15:38:48 -0700503 private void pushRemoteVolumeUpdateLocked(int userId) {
504 if (mRvc != null) {
505 try {
506 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
507 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
508 } catch (RemoteException e) {
509 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
510 }
511 }
512 }
513
RoboErikb214efb2014-07-24 13:20:30 -0700514 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
515 PendingIntent receiver = record.getMediaButtonReceiver();
516 UserRecord user = mUserRecords.get(record.getUserId());
517 if (receiver != null && user != null) {
518 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800519 ComponentName component = receiver.getIntent().getComponent();
520 if (component != null && record.getPackageName().equals(component.getPackageName())) {
521 Settings.Secure.putStringForUser(mContentResolver,
522 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
523 record.getUserId());
524 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700525 }
526 }
527
Jaewan Kim50269362016-12-23 11:22:02 +0900528 private String getCallingPackageName(int uid) {
529 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
530 if (packages != null && packages.length > 0) {
531 return packages[0];
532 }
533 return "";
534 }
535
RoboErik4646d282014-05-13 10:13:04 -0700536 /**
537 * Information about a particular user. The contents of this object is
538 * guarded by mLock.
539 */
540 final class UserRecord {
541 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700542 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800543 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700544 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800545 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700546
Jaewan Kim50269362016-12-23 11:22:02 +0900547 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
548 private int mOnVolumeKeyLongPressListenerUid;
549 private KeyEvent mInitialDownVolumeKeyEvent;
550 private int mInitialDownVolumeStream;
551 private boolean mInitialDownMusicOnly;
552
RoboErik4646d282014-05-13 10:13:04 -0700553 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800554 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700555 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800556 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700557 }
558
RoboErik4646d282014-05-13 10:13:04 -0700559 public void destroyLocked() {
560 for (int i = mSessions.size() - 1; i >= 0; i--) {
561 MediaSessionRecord session = mSessions.get(i);
562 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700563 }
564 }
565
RoboErik4646d282014-05-13 10:13:04 -0700566 public ArrayList<MediaSessionRecord> getSessionsLocked() {
567 return mSessions;
568 }
569
570 public void addSessionLocked(MediaSessionRecord session) {
571 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700572 }
573
574 public void removeSessionLocked(MediaSessionRecord session) {
575 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700576 }
577
578 public void dumpLocked(PrintWriter pw, String prefix) {
579 pw.println(prefix + "Record for user " + mUserId);
580 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700581 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800582 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jaewan Kim50269362016-12-23 11:22:02 +0900583 pw.println(indent + "Volume key long-press listener:" + mOnVolumeKeyLongPressListener);
584 pw.println(indent + "Volume key long-press listener package:" +
585 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jeff Brown01a500e2014-07-10 22:50:50 -0700586 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700587 pw.println(indent + size + " Sessions:");
588 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700589 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700590 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700591 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700592 }
593 }
RoboErikc8f92d12015-01-05 16:48:07 -0800594
595 private void restoreMediaButtonReceiver() {
596 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
Jaewan Kim8f729082016-06-21 12:36:26 +0900597 Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
RoboErikc8f92d12015-01-05 16:48:07 -0800598 if (!TextUtils.isEmpty(receiverName)) {
599 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
600 if (eventReceiver == null) {
601 // an invalid name was persisted
602 return;
603 }
604 mRestoredMediaButtonReceiver = eventReceiver;
605 }
606 }
RoboErik4646d282014-05-13 10:13:04 -0700607 }
608
RoboErik2e7a9162014-06-04 16:53:45 -0700609 final class SessionsListenerRecord implements IBinder.DeathRecipient {
610 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700611 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700612 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700613 private final int mPid;
614 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700615
RoboErik7aef77b2014-08-08 15:56:54 -0700616 public SessionsListenerRecord(IActiveSessionsListener listener,
617 ComponentName componentName,
618 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700619 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700620 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700621 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700622 mPid = pid;
623 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700624 }
625
626 @Override
627 public void binderDied() {
628 synchronized (mLock) {
629 mSessionsListeners.remove(this);
630 }
631 }
632 }
633
RoboErik7aef77b2014-08-08 15:56:54 -0700634 final class SettingsObserver extends ContentObserver {
635 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
636 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
637
638 private SettingsObserver() {
639 super(null);
640 }
641
642 private void observe() {
643 mContentResolver.registerContentObserver(mSecureSettingsUri,
644 false, this, UserHandle.USER_ALL);
645 }
646
647 @Override
648 public void onChange(boolean selfChange, Uri uri) {
649 updateActiveSessionListeners();
650 }
651 }
652
RoboErik07c70772014-03-20 13:33:52 -0700653 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700654 private static final String EXTRA_WAKELOCK_ACQUIRED =
655 "android.media.AudioService.WAKELOCK_ACQUIRED";
656 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
657
RoboErik9a9d0b52014-05-20 14:53:39 -0700658 private boolean mVoiceButtonDown = false;
659 private boolean mVoiceButtonHandled = false;
660
RoboErik07c70772014-03-20 13:33:52 -0700661 @Override
RoboErika5b02322014-05-07 17:05:49 -0700662 public ISession createSession(String packageName, ISessionCallback cb, String tag,
663 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800664 final int pid = Binder.getCallingPid();
665 final int uid = Binder.getCallingUid();
666 final long token = Binder.clearCallingIdentity();
667 try {
668 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700669 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
670 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800671 if (cb == null) {
672 throw new IllegalArgumentException("Controller callback cannot be null");
673 }
RoboErika5b02322014-05-07 17:05:49 -0700674 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
675 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700676 } finally {
677 Binder.restoreCallingIdentity(token);
678 }
679 }
680
681 @Override
RoboErika5b02322014-05-07 17:05:49 -0700682 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700683 final int pid = Binder.getCallingPid();
684 final int uid = Binder.getCallingUid();
685 final long token = Binder.clearCallingIdentity();
686
687 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700688 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700689 ArrayList<IBinder> binders = new ArrayList<IBinder>();
690 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700691 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700692 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700693 int size = records.size();
694 for (int i = 0; i < size; i++) {
695 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700696 }
697 }
698 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800699 } finally {
700 Binder.restoreCallingIdentity(token);
701 }
702 }
RoboErika278ea72014-04-24 14:49:01 -0700703
RoboErik2e7a9162014-06-04 16:53:45 -0700704 @Override
705 public void addSessionsListener(IActiveSessionsListener listener,
706 ComponentName componentName, int userId) throws RemoteException {
707 final int pid = Binder.getCallingPid();
708 final int uid = Binder.getCallingUid();
709 final long token = Binder.clearCallingIdentity();
710
711 try {
712 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
713 synchronized (mLock) {
714 int index = findIndexOfSessionsListenerLocked(listener);
715 if (index != -1) {
716 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
717 return;
718 }
719 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700720 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700721 try {
722 listener.asBinder().linkToDeath(record, 0);
723 } catch (RemoteException e) {
724 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
725 return;
726 }
727 mSessionsListeners.add(record);
728 }
729 } finally {
730 Binder.restoreCallingIdentity(token);
731 }
732 }
733
734 @Override
735 public void removeSessionsListener(IActiveSessionsListener listener)
736 throws RemoteException {
737 synchronized (mLock) {
738 int index = findIndexOfSessionsListenerLocked(listener);
739 if (index != -1) {
740 SessionsListenerRecord record = mSessionsListeners.remove(index);
741 try {
742 record.mListener.asBinder().unlinkToDeath(record, 0);
743 } catch (Exception e) {
744 // ignore exceptions, the record is being removed
745 }
746 }
747 }
748 }
749
RoboErik8a2cfc32014-05-16 11:19:38 -0700750 /**
751 * Handles the dispatching of the media button events to one of the
752 * registered listeners, or if there was none, broadcast an
753 * ACTION_MEDIA_BUTTON intent to the rest of the system.
754 *
755 * @param keyEvent a non-null KeyEvent whose key code is one of the
756 * supported media buttons
757 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
758 * while this key event is dispatched.
759 */
760 @Override
761 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
762 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
763 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
764 return;
765 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700766
RoboErik8a2cfc32014-05-16 11:19:38 -0700767 final int pid = Binder.getCallingPid();
768 final int uid = Binder.getCallingUid();
769 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700770 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700771 if (DEBUG) {
772 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
773 + keyEvent);
774 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700775 if (!isUserSetupComplete()) {
776 // Global media key handling can have the side-effect of starting new
777 // activities which is undesirable while setup is in progress.
778 Slog.i(TAG, "Not dispatching media key event because user "
779 + "setup is in progress.");
780 return;
781 }
Jaewan Kimd409d182016-09-19 21:19:55 +0900782 if (isGlobalPriorityActive() && uid != Process.SYSTEM_UID) {
783 // Prevent dispatching key event through reflection while the global priority
784 // session is active.
785 Slog.i(TAG, "Only the system can dispatch media key event "
786 + "to the global priority session.");
787 return;
788 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700789
RoboErik8a2cfc32014-05-16 11:19:38 -0700790 synchronized (mLock) {
RoboErik870c5a62014-12-02 15:08:26 -0800791 // If we don't have a media button receiver to fall back on
792 // include non-playing sessions for dispatching
Jaewan Kim8f729082016-06-21 12:36:26 +0900793 boolean useNotPlayingSessions = true;
794 for (int userId : mCurrentUserIdList) {
795 UserRecord ur = mUserRecords.get(userId);
796 if (ur.mLastMediaButtonReceiver != null
797 || ur.mRestoredMediaButtonReceiver != null) {
798 useNotPlayingSessions = false;
799 break;
800 }
801 }
802
803 if (DEBUG) {
804 Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
805 + useNotPlayingSessions);
806 }
807 MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
808 mCurrentUserIdList, useNotPlayingSessions);
RoboErik9a9d0b52014-05-20 14:53:39 -0700809 if (isVoiceKey(keyEvent.getKeyCode())) {
810 handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700811 } else {
RoboErik9a9d0b52014-05-20 14:53:39 -0700812 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
RoboErik8a2cfc32014-05-16 11:19:38 -0700813 }
814 }
815 } finally {
816 Binder.restoreCallingIdentity(token);
817 }
818 }
819
RoboErika278ea72014-04-24 14:49:01 -0700820 @Override
Jaewan Kim50269362016-12-23 11:22:02 +0900821 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
822 final int pid = Binder.getCallingPid();
823 final int uid = Binder.getCallingUid();
824 final long token = Binder.clearCallingIdentity();
825 try {
826 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
827 if (getContext().checkPermission(
828 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
829 != PackageManager.PERMISSION_GRANTED) {
830 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
831 " permission.");
832 }
833
834 synchronized (mLock) {
835 UserRecord user = mUserRecords.get(UserHandle.getUserId(uid));
836 if (user.mOnVolumeKeyLongPressListener != null &&
837 user.mOnVolumeKeyLongPressListenerUid != uid) {
838 Log.w(TAG, "Volume key long-press listener cannot be reset by another app");
839 return;
840 }
841
842 user.mOnVolumeKeyLongPressListener = listener;
843 user.mOnVolumeKeyLongPressListenerUid = uid;
844
845 Log.d(TAG, "Volume key long-press listener "
846 + listener + " is set by " + getCallingPackageName(uid));
847
848 if (user.mOnVolumeKeyLongPressListener != null) {
849 try {
850 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
851 new IBinder.DeathRecipient() {
852 @Override
853 public void binderDied() {
854 synchronized (mLock) {
855 user.mOnVolumeKeyLongPressListener = null;
856 }
857 }
858 }, 0);
859 } catch (RemoteException e) {
860 Log.w(TAG, "Failed to set death recipient "
861 + user.mOnVolumeKeyLongPressListener);
862 user.mOnVolumeKeyLongPressListener = null;
863 }
864 }
865 }
866 } finally {
867 Binder.restoreCallingIdentity(token);
868 }
869 }
870
871 /**
872 * Handles the dispatching of the volume button events to one of the
873 * registered listeners. If there's a volume key long-press listener and
874 * there's no active global priority session, long-pressess will be sent to the
875 * long-press listener instead of adjusting volume.
876 *
877 * @param keyEvent a non-null KeyEvent whose key code is one of the
878 * {@link KeyEvent#KEYCODE_VOLUME_UP},
879 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
880 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
881 * @param stream stream type to adjust volume.
882 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
883 */
884 @Override
885 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
886 if (keyEvent == null ||
887 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
888 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
889 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
890 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
891 return;
892 }
893
894 final int pid = Binder.getCallingPid();
895 final int uid = Binder.getCallingUid();
896 final long token = Binder.clearCallingIdentity();
897
898 if (DEBUG) {
899 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
900 + keyEvent);
901 }
902
903 try {
904 synchronized (mLock) {
905 // Only consider full user.
906 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
907
908 if (mPriorityStack.isGlobalPriorityActive()
909 || user.mOnVolumeKeyLongPressListener == null) {
910 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
911 } else {
912 // TODO: Consider the case when both volume up and down keys are pressed
913 // at the same time.
914 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
915 if (keyEvent.getRepeatCount() == 0) {
916 user.mInitialDownVolumeKeyEvent = keyEvent;
917 user.mInitialDownVolumeStream = stream;
918 user.mInitialDownMusicOnly = musicOnly;
919 }
920 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
921 if (user.mInitialDownVolumeKeyEvent != null) {
922 dispatchVolumeKeyLongPressLocked(
923 user.mInitialDownVolumeKeyEvent);
924 // Mark that the key is already handled.
925 user.mInitialDownVolumeKeyEvent = null;
926 }
927 dispatchVolumeKeyLongPressLocked(keyEvent);
928 }
929 } else { // if up
930 if (user.mInitialDownVolumeKeyEvent != null
931 && user.mInitialDownVolumeKeyEvent.getDownTime()
932 == keyEvent.getDownTime()) {
933 // Short-press. Should change volume.
934 dispatchVolumeKeyEventLocked(
935 user.mInitialDownVolumeKeyEvent,
936 user.mInitialDownVolumeStream,
937 user.mInitialDownMusicOnly);
938 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
939 } else {
940 dispatchVolumeKeyLongPressLocked(keyEvent);
941 }
942 }
943 }
944 }
945 } finally {
946 Binder.restoreCallingIdentity(token);
947 }
948 }
949
950 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
951 // Only consider full user.
952 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
953 try {
954 user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
955 } catch (RemoteException e) {
956 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
957 }
958 }
959
960 private void dispatchVolumeKeyEventLocked(
961 KeyEvent keyEvent, int stream, boolean musicOnly) {
962 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
963 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
964 int direction = 0;
965 boolean isMute = false;
966 switch (keyEvent.getKeyCode()) {
967 case KeyEvent.KEYCODE_VOLUME_UP:
968 direction = AudioManager.ADJUST_RAISE;
969 break;
970 case KeyEvent.KEYCODE_VOLUME_DOWN:
971 direction = AudioManager.ADJUST_LOWER;
972 break;
973 case KeyEvent.KEYCODE_VOLUME_MUTE:
974 isMute = true;
975 break;
976 }
977 if (down || up) {
978 int flags = AudioManager.FLAG_FROM_KEY;
979 if (musicOnly) {
980 // This flag is used when the screen is off to only affect active media.
981 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
982 } else {
983 // These flags are consistent with the home screen
984 if (up) {
985 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
986 } else {
987 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
988 }
989 }
990 if (direction != 0) {
991 // If this is action up we want to send a beep for non-music events
992 if (up) {
993 direction = 0;
994 }
995 dispatchAdjustVolumeLocked(stream, direction, flags);
996 } else if (isMute) {
997 if (down && keyEvent.getRepeatCount() == 0) {
998 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
999 }
1000 }
1001 }
1002 }
1003
1004 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001005 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001006 final long token = Binder.clearCallingIdentity();
1007 try {
1008 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001009 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001010 }
1011 } finally {
1012 Binder.restoreCallingIdentity(token);
1013 }
1014 }
1015
1016 @Override
RoboErik19c95182014-06-23 15:38:48 -07001017 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1018 final int pid = Binder.getCallingPid();
1019 final int uid = Binder.getCallingUid();
1020 final long token = Binder.clearCallingIdentity();
1021 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001022 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001023 mRvc = rvc;
1024 } finally {
1025 Binder.restoreCallingIdentity(token);
1026 }
1027 }
1028
1029 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001030 public boolean isGlobalPriorityActive() {
1031 return mPriorityStack.isGlobalPriorityActive();
1032 }
1033
1034 @Override
RoboErika278ea72014-04-24 14:49:01 -07001035 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1036 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
1037 != PackageManager.PERMISSION_GRANTED) {
1038 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
1039 + Binder.getCallingPid()
1040 + ", uid=" + Binder.getCallingUid());
1041 return;
1042 }
1043
1044 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1045 pw.println();
1046
1047 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001048 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErik4646d282014-05-13 10:13:04 -07001049 int count = mAllSessions.size();
RoboErika8f95142014-05-05 14:23:49 -07001050 pw.println(count + " Sessions:");
RoboErika278ea72014-04-24 14:49:01 -07001051 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -07001052 mAllSessions.get(i).dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001053 pw.println();
RoboErika278ea72014-04-24 14:49:01 -07001054 }
RoboErika5b02322014-05-07 17:05:49 -07001055 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -07001056
RoboErik4646d282014-05-13 10:13:04 -07001057 pw.println("User Records:");
1058 count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001059 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -08001060 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -07001061 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001062 }
1063 }
1064 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001065
RoboErik2e7a9162014-06-04 16:53:45 -07001066 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1067 final int uid) {
1068 String packageName = null;
1069 if (componentName != null) {
1070 // If they gave us a component name verify they own the
1071 // package
1072 packageName = componentName.getPackageName();
1073 enforcePackageName(packageName, uid);
1074 }
1075 // Check that they can make calls on behalf of the user and
1076 // get the final user id
1077 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1078 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1079 // Check if they have the permissions or their component is
1080 // enabled for the user they're calling from.
1081 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1082 return resolvedUserId;
1083 }
1084
Jaewan Kim50269362016-12-23 11:22:02 +09001085 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
1086 MediaSessionRecord session = mPriorityStack.getDefaultVolumeSession(mCurrentUserIdList);
1087
RoboErik9c785402014-11-11 16:52:26 -08001088 boolean preferSuggestedStream = false;
1089 if (isValidLocalStreamType(suggestedStream)
1090 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1091 preferSuggestedStream = true;
1092 }
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001093 if (DEBUG) {
1094 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1095 + flags + ", suggestedStream=" + suggestedStream
1096 + ", preferSuggestedStream=" + preferSuggestedStream);
1097 }
RoboErik9c785402014-11-11 16:52:26 -08001098 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001099 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1100 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001101 if (DEBUG) {
1102 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001103 }
RoboErikb7c014c2014-07-22 15:58:22 -07001104 return;
RoboErik3c45c292014-07-08 16:47:31 -07001105 }
Shibin George19e84042016-06-14 20:42:13 +05301106
1107 // Execute mAudioService.adjustSuggestedStreamVolume() on
1108 // handler thread of MediaSessionService.
1109 // This will release the MediaSessionService.mLock sooner and avoid
1110 // a potential deadlock between MediaSessionService.mLock and
1111 // ActivityManagerService lock.
1112 mHandler.post(new Runnable() {
1113 @Override
1114 public void run() {
1115 try {
1116 String packageName = getContext().getOpPackageName();
1117 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1118 flags, packageName, TAG);
1119 } catch (RemoteException e) {
1120 Log.e(TAG, "Error adjusting default volume.", e);
1121 }
1122 }
1123 });
RoboErikb69ffd42014-05-30 14:57:59 -07001124 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001125 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001126 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001127 }
1128 }
1129
RoboErik9a9d0b52014-05-20 14:53:39 -07001130 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
1131 MediaSessionRecord session) {
1132 if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
1133 // If the phone app has priority just give it the event
1134 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
1135 return;
1136 }
1137 int action = keyEvent.getAction();
1138 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1139 if (action == KeyEvent.ACTION_DOWN) {
1140 if (keyEvent.getRepeatCount() == 0) {
1141 mVoiceButtonDown = true;
1142 mVoiceButtonHandled = false;
1143 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1144 mVoiceButtonHandled = true;
1145 startVoiceInput(needWakeLock);
1146 }
1147 } else if (action == KeyEvent.ACTION_UP) {
1148 if (mVoiceButtonDown) {
1149 mVoiceButtonDown = false;
1150 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1151 // Resend the down then send this event through
1152 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
1153 dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
1154 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
1155 }
1156 }
1157 }
1158 }
1159
1160 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
1161 MediaSessionRecord session) {
1162 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001163 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001164 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001165 }
1166 if (needWakeLock) {
1167 mKeyEventReceiver.aquireWakeLockLocked();
1168 }
Jaewan Kim50269362016-12-23 11:22:02 +09001169 // If we don't need a wakelock use -1 as the id so we won't release it later.
RoboErik9a9d0b52014-05-20 14:53:39 -07001170 session.sendMediaButton(keyEvent,
1171 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001172 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001173 getContext().getPackageName());
RoboErik9a9d0b52014-05-20 14:53:39 -07001174 } else {
RoboErikb214efb2014-07-24 13:20:30 -07001175 // Launch the last PendingIntent we had with priority
Jaewan Kim8f729082016-06-21 12:36:26 +09001176 for (int userId : mCurrentUserIdList) {
1177 UserRecord user = mUserRecords.get(userId);
1178 if (user.mLastMediaButtonReceiver == null
1179 && user.mRestoredMediaButtonReceiver == null) {
1180 continue;
1181 }
RoboErikb214efb2014-07-24 13:20:30 -07001182 if (needWakeLock) {
1183 mKeyEventReceiver.aquireWakeLockLocked();
1184 }
1185 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Insun Kang2054db32016-04-07 15:34:34 +09001186 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RoboErikb214efb2014-07-24 13:20:30 -07001187 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1188 try {
RoboErikc8f92d12015-01-05 16:48:07 -08001189 if (user.mLastMediaButtonReceiver != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001190 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001191 Log.d(TAG, "Sending " + keyEvent
1192 + " to the last known pendingIntent "
1193 + user.mLastMediaButtonReceiver);
1194 }
RoboErikc8f92d12015-01-05 16:48:07 -08001195 user.mLastMediaButtonReceiver.send(getContext(),
1196 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
riddle_hsu02ed0122015-10-20 16:00:15 +08001197 mediaButtonIntent, mKeyEventReceiver, mHandler);
RoboErikc8f92d12015-01-05 16:48:07 -08001198 } else {
Jaewan Kim50269362016-12-23 11:22:02 +09001199 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001200 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1201 + user.mRestoredMediaButtonReceiver);
1202 }
RoboErikc8f92d12015-01-05 16:48:07 -08001203 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
1204 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim8f729082016-06-21 12:36:26 +09001205 UserHandle.of(userId));
RoboErikc8f92d12015-01-05 16:48:07 -08001206 }
RoboErikb214efb2014-07-24 13:20:30 -07001207 } catch (CanceledException e) {
1208 Log.i(TAG, "Error sending key event to media button receiver "
1209 + user.mLastMediaButtonReceiver, e);
1210 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001211 return;
RoboErik9a9d0b52014-05-20 14:53:39 -07001212 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001213 if (DEBUG) {
1214 Log.d(TAG, "Sending media key ordered broadcast");
1215 }
1216 if (needWakeLock) {
1217 mMediaEventWakeLock.acquire();
1218 }
1219 // Fallback to legacy behavior
1220 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1221 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1222 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1223 if (needWakeLock) {
1224 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1225 }
1226 // Send broadcast only to the full user.
1227 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1228 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001229 }
1230 }
1231
1232 private void startVoiceInput(boolean needWakeLock) {
1233 Intent voiceIntent = null;
1234 // select which type of search to launch:
1235 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1236 // - device locked or screen off: action is
1237 // ACTION_VOICE_SEARCH_HANDS_FREE
1238 // with EXTRA_SECURE set to true if the device is securely locked
1239 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1240 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1241 if (!isLocked && pm.isScreenOn()) {
1242 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1243 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1244 } else {
1245 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1246 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1247 isLocked && mKeyguardManager.isKeyguardSecure());
1248 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1249 }
1250 // start the search activity
1251 if (needWakeLock) {
1252 mMediaEventWakeLock.acquire();
1253 }
1254 try {
1255 if (voiceIntent != null) {
1256 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1257 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001258 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001259 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1260 }
1261 } catch (ActivityNotFoundException e) {
1262 Log.w(TAG, "No activity for search: " + e);
1263 } finally {
1264 if (needWakeLock) {
1265 mMediaEventWakeLock.release();
1266 }
1267 }
1268 }
1269
1270 private boolean isVoiceKey(int keyCode) {
1271 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1272 }
1273
Jeff Brown38d3feb2015-03-19 18:26:30 -07001274 private boolean isUserSetupComplete() {
1275 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1276 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1277 }
1278
RoboErik9c785402014-11-11 16:52:26 -08001279 // we only handle public stream types, which are 0-5
1280 private boolean isValidLocalStreamType(int streamType) {
1281 return streamType >= AudioManager.STREAM_VOICE_CALL
1282 && streamType <= AudioManager.STREAM_NOTIFICATION;
1283 }
1284
RoboErik418c10c2014-05-19 09:25:25 -07001285 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1286
RoboErikb214efb2014-07-24 13:20:30 -07001287 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1288 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001289 private final Handler mHandler;
1290 private int mRefCount = 0;
1291 private int mLastTimeoutId = 0;
1292
1293 public KeyEventWakeLockReceiver(Handler handler) {
1294 super(handler);
1295 mHandler = handler;
1296 }
1297
1298 public void onTimeout() {
1299 synchronized (mLock) {
1300 if (mRefCount == 0) {
1301 // We've already released it, so just return
1302 return;
1303 }
1304 mLastTimeoutId++;
1305 mRefCount = 0;
1306 releaseWakeLockLocked();
1307 }
1308 }
1309
1310 public void aquireWakeLockLocked() {
1311 if (mRefCount == 0) {
1312 mMediaEventWakeLock.acquire();
1313 }
1314 mRefCount++;
1315 mHandler.removeCallbacks(this);
1316 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1317
1318 }
1319
1320 @Override
1321 public void run() {
1322 onTimeout();
1323 }
1324
RoboErik8a2cfc32014-05-16 11:19:38 -07001325 @Override
1326 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001327 if (resultCode < mLastTimeoutId) {
1328 // Ignore results from calls that were before the last
1329 // timeout, just in case.
1330 return;
1331 } else {
1332 synchronized (mLock) {
1333 if (mRefCount > 0) {
1334 mRefCount--;
1335 if (mRefCount == 0) {
1336 releaseWakeLockLocked();
1337 }
1338 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001339 }
1340 }
1341 }
RoboErik418c10c2014-05-19 09:25:25 -07001342
1343 private void releaseWakeLockLocked() {
1344 mMediaEventWakeLock.release();
1345 mHandler.removeCallbacks(this);
1346 }
RoboErikb214efb2014-07-24 13:20:30 -07001347
1348 @Override
1349 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1350 String resultData, Bundle resultExtras) {
1351 onReceiveResult(resultCode, null);
1352 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001353 };
1354
1355 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1356 @Override
1357 public void onReceive(Context context, Intent intent) {
1358 if (intent == null) {
1359 return;
1360 }
1361 Bundle extras = intent.getExtras();
1362 if (extras == null) {
1363 return;
1364 }
1365 synchronized (mLock) {
1366 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1367 && mMediaEventWakeLock.isHeld()) {
1368 mMediaEventWakeLock.release();
1369 }
1370 }
1371 }
1372 };
RoboErik01fe6612014-02-13 14:19:04 -08001373 }
1374
RoboErik2e7a9162014-06-04 16:53:45 -07001375 final class MessageHandler extends Handler {
1376 private static final int MSG_SESSIONS_CHANGED = 1;
1377
1378 @Override
1379 public void handleMessage(Message msg) {
1380 switch (msg.what) {
1381 case MSG_SESSIONS_CHANGED:
1382 pushSessionsChanged(msg.arg1);
1383 break;
1384 }
1385 }
1386
1387 public void post(int what, int arg1, int arg2) {
1388 obtainMessage(what, arg1, arg2).sendToTarget();
1389 }
1390 }
RoboErik01fe6612014-02-13 14:19:04 -08001391}