blob: 3bf95efaef117c2198e84ff66558d3b9809d2655 [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 Kim6e2b01c2017-01-19 16:33:14 -080040import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090041import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070042import android.media.session.ISession;
43import android.media.session.ISessionCallback;
44import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070045import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080046import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070047import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080048import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070049import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080050import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070051import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070052import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070053import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090054import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080055import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070056import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070057import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070058import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090059import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070060import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070061import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080062import android.text.TextUtils;
63import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070064import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070065import android.util.SparseArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070066import android.view.KeyEvent;
RoboErik01fe6612014-02-13 14:19:04 -080067
John Spurlockeb69e242015-02-17 17:15:04 -050068import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080069import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070070import com.android.server.Watchdog;
71import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080072
RoboErika278ea72014-04-24 14:49:01 -070073import java.io.FileDescriptor;
74import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080075import java.util.ArrayList;
Jaewan Kim8f729082016-06-21 12:36:26 +090076import java.util.Arrays;
RoboErike7880d82014-04-30 12:48:25 -070077import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080078
79/**
80 * System implementation of MediaSessionManager
81 */
RoboErika278ea72014-04-24 14:49:01 -070082public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080083 private static final String TAG = "MediaSessionService";
84 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090085 // Leave log for key event always.
86 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080087
RoboErik418c10c2014-05-19 09:25:25 -070088 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080089 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070090
RoboErik2610d712015-01-07 11:10:23 -080091 /* package */final IBinder mICallback = new Binder();
92
RoboErik01fe6612014-02-13 14:19:04 -080093 private final SessionManagerImpl mSessionManagerImpl;
RoboErika8f95142014-05-05 14:23:49 -070094 private final MediaSessionStack mPriorityStack;
RoboErik01fe6612014-02-13 14:19:04 -080095
RoboErik4646d282014-05-13 10:13:04 -070096 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
97 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -070098 private final ArrayList<SessionsListenerRecord> mSessionsListeners
99 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800100 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700101 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700102 private final PowerManager.WakeLock mMediaEventWakeLock;
RoboErik01fe6612014-02-13 14:19:04 -0800103
RoboErik9a9d0b52014-05-20 14:53:39 -0700104 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700105 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -0500106 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700107 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700108 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -0700109
Jaewan Kim8f729082016-06-21 12:36:26 +0900110 // List of user IDs running in the foreground.
111 // Multiple users can be in the foreground if the work profile is on.
112 private final List<Integer> mCurrentUserIdList = new ArrayList<>();
RoboErike7880d82014-04-30 12:48:25 -0700113
RoboErik19c95182014-06-23 15:38:48 -0700114 // Used to notify system UI when remote volume was changed. TODO find a
115 // better way to handle this.
116 private IRemoteVolumeController mRvc;
117
RoboErik01fe6612014-02-13 14:19:04 -0800118 public MediaSessionService(Context context) {
119 super(context);
120 mSessionManagerImpl = new SessionManagerImpl();
RoboErika8f95142014-05-05 14:23:49 -0700121 mPriorityStack = new MediaSessionStack();
RoboErik8a2cfc32014-05-16 11:19:38 -0700122 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
123 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
RoboErik01fe6612014-02-13 14:19:04 -0800124 }
125
126 @Override
127 public void onStart() {
128 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700129 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700130 mKeyguardManager =
131 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700132 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500133 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700134 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700135 mSettingsObserver = new SettingsObserver();
136 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800137
138 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700139 }
140
141 private IAudioService getAudioService() {
142 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
143 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700144 }
145
RoboErika8f95142014-05-05 14:23:49 -0700146 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700147 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700148 if (!mAllSessions.contains(record)) {
149 Log.d(TAG, "Unknown session updated. Ignoring.");
150 return;
151 }
RoboErika8f95142014-05-05 14:23:49 -0700152 mPriorityStack.onSessionStateChange(record);
RoboErike7880d82014-04-30 12:48:25 -0700153 }
RoboErik2e7a9162014-06-04 16:53:45 -0700154 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700155 }
156
RoboErik9c5b7cb2015-01-15 15:09:09 -0800157 /**
158 * Tells the system UI that volume has changed on a remote session.
159 */
160 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
161 if (mRvc == null) {
162 return;
163 }
164 try {
165 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
166 } catch (Exception e) {
167 Log.wtf(TAG, "Error sending volume change to system UI.", e);
168 }
169 }
170
RoboErika8f95142014-05-05 14:23:49 -0700171 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700172 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700173 synchronized (mLock) {
RoboErik4646d282014-05-13 10:13:04 -0700174 if (!mAllSessions.contains(record)) {
175 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
176 return;
177 }
RoboErik2e7a9162014-06-04 16:53:45 -0700178 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
179 }
180 if (updateSessions) {
181 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700182 }
183 }
184
RoboErik19c95182014-06-23 15:38:48 -0700185 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
186 synchronized (mLock) {
187 if (!mAllSessions.contains(record)) {
188 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
189 return;
190 }
191 pushRemoteVolumeUpdateLocked(record.getUserId());
192 }
193 }
194
RoboErika278ea72014-04-24 14:49:01 -0700195 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900196 public void onStartUser(int userId) {
197 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700198 updateUser();
199 }
200
201 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900202 public void onSwitchUser(int userId) {
203 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700204 updateUser();
205 }
206
207 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900208 public void onStopUser(int userId) {
209 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700210 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900211 UserRecord user = mUserRecords.get(userId);
RoboErik4646d282014-05-13 10:13:04 -0700212 if (user != null) {
213 destroyUserLocked(user);
214 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900215 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700216 }
217 }
218
219 @Override
RoboErika278ea72014-04-24 14:49:01 -0700220 public void monitor() {
221 synchronized (mLock) {
222 // Check for deadlock
223 }
224 }
225
RoboErik4646d282014-05-13 10:13:04 -0700226 protected void enforcePhoneStatePermission(int pid, int uid) {
227 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
228 != PackageManager.PERMISSION_GRANTED) {
229 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
230 }
231 }
232
RoboErik01fe6612014-02-13 14:19:04 -0800233 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700234 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800235 destroySessionLocked(session);
236 }
237 }
238
239 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700240 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800241 destroySessionLocked(session);
242 }
243 }
244
RoboErik4646d282014-05-13 10:13:04 -0700245 private void updateUser() {
246 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900247 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
248 int currentUser = ActivityManager.getCurrentUser();
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700249 // Include all profiles even though they aren't yet enabled to handle work profile case.
250 int[] userIds = manager.getProfileIdsWithDisabled(currentUser);
Jaewan Kim8f729082016-06-21 12:36:26 +0900251 mCurrentUserIdList.clear();
252 if (userIds != null && userIds.length > 0) {
253 for (int userId : userIds) {
254 mCurrentUserIdList.add(userId);
RoboErik4646d282014-05-13 10:13:04 -0700255 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900256 } else {
257 // This shouldn't happen.
258 Log.w(TAG, "Failed to get enabled profiles.");
259 mCurrentUserIdList.add(currentUser);
260 }
261 for (int userId : mCurrentUserIdList) {
262 if (mUserRecords.get(userId) == null) {
263 mUserRecords.put(userId, new UserRecord(getContext(), userId));
264 }
RoboErik4646d282014-05-13 10:13:04 -0700265 }
266 }
267 }
268
RoboErik7aef77b2014-08-08 15:56:54 -0700269 private void updateActiveSessionListeners() {
270 synchronized (mLock) {
271 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
272 SessionsListenerRecord listener = mSessionsListeners.get(i);
273 try {
274 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
275 listener.mUserId);
276 } catch (SecurityException e) {
277 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
278 + " is no longer authorized. Disconnecting.");
279 mSessionsListeners.remove(i);
280 try {
281 listener.mListener
282 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
283 } catch (Exception e1) {
284 // ignore
285 }
286 }
287 }
288 }
289 }
290
RoboErik4646d282014-05-13 10:13:04 -0700291 /**
292 * Stop the user and unbind from everything.
293 *
294 * @param user The user to dispose of
295 */
296 private void destroyUserLocked(UserRecord user) {
RoboErik4646d282014-05-13 10:13:04 -0700297 user.destroyLocked();
298 mUserRecords.remove(user.mUserId);
299 }
300
301 /*
302 * When a session is removed several things need to happen.
303 * 1. We need to remove it from the relevant user.
304 * 2. We need to remove it from the priority stack.
305 * 3. We need to remove it from all sessions.
306 * 4. If this is the system priority session we need to clear it.
307 * 5. We need to unlink to death from the cb binder
308 * 6. We need to tell the session to do any final cleanup (onDestroy)
309 */
RoboErik01fe6612014-02-13 14:19:04 -0800310 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900311 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900312 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900313 }
RoboErik4646d282014-05-13 10:13:04 -0700314 int userId = session.getUserId();
315 UserRecord user = mUserRecords.get(userId);
316 if (user != null) {
317 user.removeSessionLocked(session);
318 }
319
RoboErika8f95142014-05-05 14:23:49 -0700320 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700321 mAllSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700322
323 try {
324 session.getCallback().asBinder().unlinkToDeath(session, 0);
325 } catch (Exception e) {
326 // ignore exceptions while destroying a session.
327 }
328 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700329
330 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800331 }
332
333 private void enforcePackageName(String packageName, int uid) {
334 if (TextUtils.isEmpty(packageName)) {
335 throw new IllegalArgumentException("packageName may not be empty");
336 }
337 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
338 final int packageCount = packages.length;
339 for (int i = 0; i < packageCount; i++) {
340 if (packageName.equals(packages[i])) {
341 return;
342 }
343 }
344 throw new IllegalArgumentException("packageName is not owned by the calling process");
345 }
346
RoboErike7880d82014-04-30 12:48:25 -0700347 /**
348 * Checks a caller's authorization to register an IRemoteControlDisplay.
349 * Authorization is granted if one of the following is true:
350 * <ul>
351 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
352 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700353 * <li>the caller's listener is one of the enabled notification listeners
354 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700355 * </ul>
356 */
RoboErika5b02322014-05-07 17:05:49 -0700357 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
358 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500359 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700360 if (getContext()
361 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
362 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700363 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
364 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700365 throw new SecurityException("Missing permission to control media.");
366 }
367 }
368
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500369 private boolean isCurrentVolumeController(int uid, int pid) {
370 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
371 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500372 }
373
374 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500375 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700376 throw new SecurityException("Only system ui may " + action);
377 }
378 }
379
RoboErika5b02322014-05-07 17:05:49 -0700380 /**
381 * This checks if the component is an enabled notification listener for the
382 * specified user. Enabled components may only operate on behalf of the user
383 * they're running as.
384 *
385 * @param compName The component that is enabled.
386 * @param userId The user id of the caller.
387 * @param forUserId The user id they're making the request on behalf of.
388 * @return True if the component is enabled, false otherwise
389 */
390 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
391 int forUserId) {
392 if (userId != forUserId) {
393 // You may not access another user's content as an enabled listener.
394 return false;
395 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700396 if (DEBUG) {
397 Log.d(TAG, "Checking if enabled notification listener " + compName);
398 }
RoboErike7880d82014-04-30 12:48:25 -0700399 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700400 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700401 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700402 userId);
RoboErike7880d82014-04-30 12:48:25 -0700403 if (enabledNotifListeners != null) {
404 final String[] components = enabledNotifListeners.split(":");
405 for (int i = 0; i < components.length; i++) {
406 final ComponentName component =
407 ComponentName.unflattenFromString(components[i]);
408 if (component != null) {
409 if (compName.equals(component)) {
410 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900411 Log.d(TAG, "ok to get sessions. " + component +
RoboErike7880d82014-04-30 12:48:25 -0700412 " is authorized notification listener");
413 }
414 return true;
415 }
416 }
417 }
418 }
419 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900420 Log.d(TAG, "not ok to get sessions. " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700421 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700422 }
423 }
424 return false;
425 }
426
RoboErika5b02322014-05-07 17:05:49 -0700427 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700428 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800429 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700430 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800431 }
432 }
433
RoboErik4646d282014-05-13 10:13:04 -0700434 /*
435 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700436 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700437 * 2. It needs to be added to all sessions.
438 * 3. It needs to be added to the priority stack.
439 * 4. It needs to be added to the relevant user record.
440 */
RoboErika5b02322014-05-07 17:05:49 -0700441 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
442 String callerPackageName, ISessionCallback cb, String tag) {
RoboErik4646d282014-05-13 10:13:04 -0700443
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700444 UserRecord user = mUserRecords.get(userId);
445 if (user == null) {
446 Log.wtf(TAG, "Request from invalid user: " + userId);
447 throw new RuntimeException("Session request from invalid user.");
448 }
449
RoboErika5b02322014-05-07 17:05:49 -0700450 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
451 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800452 try {
453 cb.asBinder().linkToDeath(session, 0);
454 } catch (RemoteException e) {
455 throw new RuntimeException("Media Session owner died prematurely.", e);
456 }
RoboErik4646d282014-05-13 10:13:04 -0700457
458 mAllSessions.add(session);
Jaewan Kim8f729082016-06-21 12:36:26 +0900459 mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
RoboErik4646d282014-05-13 10:13:04 -0700460 user.addSessionLocked(session);
461
RoboErik2e7a9162014-06-04 16:53:45 -0700462 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
463
RoboErik01fe6612014-02-13 14:19:04 -0800464 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900465 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800466 }
467 return session;
468 }
469
RoboErik2e7a9162014-06-04 16:53:45 -0700470 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
471 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800472 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700473 return i;
474 }
475 }
476 return -1;
477 }
478
RoboErik2e7a9162014-06-04 16:53:45 -0700479 private void pushSessionsChanged(int userId) {
480 synchronized (mLock) {
481 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
482 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800483 if (size > 0 && records.get(0).isPlaybackActive(false)) {
RoboErikb214efb2014-07-24 13:20:30 -0700484 rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700485 }
Jeff Browndba34ba2014-06-24 20:46:03 -0700486 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700487 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700488 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700489 }
RoboErik19c95182014-06-23 15:38:48 -0700490 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700491 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
492 SessionsListenerRecord record = mSessionsListeners.get(i);
493 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
494 try {
495 record.mListener.onActiveSessionsChanged(tokens);
496 } catch (RemoteException e) {
497 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
498 e);
499 mSessionsListeners.remove(i);
500 }
501 }
502 }
503 }
504 }
505
RoboErik19c95182014-06-23 15:38:48 -0700506 private void pushRemoteVolumeUpdateLocked(int userId) {
507 if (mRvc != null) {
508 try {
509 MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
510 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
511 } catch (RemoteException e) {
512 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
513 }
514 }
515 }
516
RoboErikb214efb2014-07-24 13:20:30 -0700517 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
518 PendingIntent receiver = record.getMediaButtonReceiver();
519 UserRecord user = mUserRecords.get(record.getUserId());
520 if (receiver != null && user != null) {
521 user.mLastMediaButtonReceiver = receiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800522 ComponentName component = receiver.getIntent().getComponent();
523 if (component != null && record.getPackageName().equals(component.getPackageName())) {
524 Settings.Secure.putStringForUser(mContentResolver,
525 Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
526 record.getUserId());
527 }
RoboErik6f0e4dd2014-06-17 16:56:27 -0700528 }
529 }
530
Jaewan Kim50269362016-12-23 11:22:02 +0900531 private String getCallingPackageName(int uid) {
532 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
533 if (packages != null && packages.length > 0) {
534 return packages[0];
535 }
536 return "";
537 }
538
RoboErik4646d282014-05-13 10:13:04 -0700539 /**
540 * Information about a particular user. The contents of this object is
541 * guarded by mLock.
542 */
543 final class UserRecord {
544 private final int mUserId;
RoboErik4646d282014-05-13 10:13:04 -0700545 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
RoboErikc8f92d12015-01-05 16:48:07 -0800546 private final Context mContext;
RoboErikb214efb2014-07-24 13:20:30 -0700547 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800548 private ComponentName mRestoredMediaButtonReceiver;
RoboErik4646d282014-05-13 10:13:04 -0700549
Jaewan Kim50269362016-12-23 11:22:02 +0900550 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
551 private int mOnVolumeKeyLongPressListenerUid;
552 private KeyEvent mInitialDownVolumeKeyEvent;
553 private int mInitialDownVolumeStream;
554 private boolean mInitialDownMusicOnly;
555
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800556 private IOnMediaKeyListener mOnMediaKeyListener;
557 private int mOnMediaKeyListenerUid;
558
RoboErik4646d282014-05-13 10:13:04 -0700559 public UserRecord(Context context, int userId) {
RoboErikc8f92d12015-01-05 16:48:07 -0800560 mContext = context;
RoboErik4646d282014-05-13 10:13:04 -0700561 mUserId = userId;
RoboErikc8f92d12015-01-05 16:48:07 -0800562 restoreMediaButtonReceiver();
RoboErik4646d282014-05-13 10:13:04 -0700563 }
564
RoboErik4646d282014-05-13 10:13:04 -0700565 public void destroyLocked() {
566 for (int i = mSessions.size() - 1; i >= 0; i--) {
567 MediaSessionRecord session = mSessions.get(i);
568 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700569 }
570 }
571
RoboErik4646d282014-05-13 10:13:04 -0700572 public ArrayList<MediaSessionRecord> getSessionsLocked() {
573 return mSessions;
574 }
575
576 public void addSessionLocked(MediaSessionRecord session) {
577 mSessions.add(session);
RoboErik4646d282014-05-13 10:13:04 -0700578 }
579
580 public void removeSessionLocked(MediaSessionRecord session) {
581 mSessions.remove(session);
RoboErik4646d282014-05-13 10:13:04 -0700582 }
583
584 public void dumpLocked(PrintWriter pw, String prefix) {
585 pw.println(prefix + "Record for user " + mUserId);
586 String indent = prefix + " ";
RoboErikb214efb2014-07-24 13:20:30 -0700587 pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
RoboErikc8f92d12015-01-05 16:48:07 -0800588 pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
Jaewan Kim50269362016-12-23 11:22:02 +0900589 pw.println(indent + "Volume key long-press listener:" + mOnVolumeKeyLongPressListener);
590 pw.println(indent + "Volume key long-press listener package:" +
591 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800592 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
593 pw.println(indent + "Media key listener package: " +
594 getCallingPackageName(mOnMediaKeyListenerUid));
Jeff Brown01a500e2014-07-10 22:50:50 -0700595 int size = mSessions.size();
RoboErik4646d282014-05-13 10:13:04 -0700596 pw.println(indent + size + " Sessions:");
597 for (int i = 0; i < size; i++) {
RoboErikaa4e23b2014-07-24 18:35:11 -0700598 // Just print the short version, the full session dump will
RoboErik4646d282014-05-13 10:13:04 -0700599 // already be in the list of all sessions.
RoboErikaa4e23b2014-07-24 18:35:11 -0700600 pw.println(indent + mSessions.get(i).toString());
RoboErik4646d282014-05-13 10:13:04 -0700601 }
602 }
RoboErikc8f92d12015-01-05 16:48:07 -0800603
604 private void restoreMediaButtonReceiver() {
605 String receiverName = Settings.Secure.getStringForUser(mContentResolver,
Jaewan Kim8f729082016-06-21 12:36:26 +0900606 Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
RoboErikc8f92d12015-01-05 16:48:07 -0800607 if (!TextUtils.isEmpty(receiverName)) {
608 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
609 if (eventReceiver == null) {
610 // an invalid name was persisted
611 return;
612 }
613 mRestoredMediaButtonReceiver = eventReceiver;
614 }
615 }
RoboErik4646d282014-05-13 10:13:04 -0700616 }
617
RoboErik2e7a9162014-06-04 16:53:45 -0700618 final class SessionsListenerRecord implements IBinder.DeathRecipient {
619 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700620 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700621 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700622 private final int mPid;
623 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700624
RoboErik7aef77b2014-08-08 15:56:54 -0700625 public SessionsListenerRecord(IActiveSessionsListener listener,
626 ComponentName componentName,
627 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700628 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700629 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700630 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700631 mPid = pid;
632 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700633 }
634
635 @Override
636 public void binderDied() {
637 synchronized (mLock) {
638 mSessionsListeners.remove(this);
639 }
640 }
641 }
642
RoboErik7aef77b2014-08-08 15:56:54 -0700643 final class SettingsObserver extends ContentObserver {
644 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
645 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
646
647 private SettingsObserver() {
648 super(null);
649 }
650
651 private void observe() {
652 mContentResolver.registerContentObserver(mSecureSettingsUri,
653 false, this, UserHandle.USER_ALL);
654 }
655
656 @Override
657 public void onChange(boolean selfChange, Uri uri) {
658 updateActiveSessionListeners();
659 }
660 }
661
RoboErik07c70772014-03-20 13:33:52 -0700662 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700663 private static final String EXTRA_WAKELOCK_ACQUIRED =
664 "android.media.AudioService.WAKELOCK_ACQUIRED";
665 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
666
RoboErik9a9d0b52014-05-20 14:53:39 -0700667 private boolean mVoiceButtonDown = false;
668 private boolean mVoiceButtonHandled = false;
669
RoboErik07c70772014-03-20 13:33:52 -0700670 @Override
RoboErika5b02322014-05-07 17:05:49 -0700671 public ISession createSession(String packageName, ISessionCallback cb, String tag,
672 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800673 final int pid = Binder.getCallingPid();
674 final int uid = Binder.getCallingUid();
675 final long token = Binder.clearCallingIdentity();
676 try {
677 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700678 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
679 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800680 if (cb == null) {
681 throw new IllegalArgumentException("Controller callback cannot be null");
682 }
RoboErika5b02322014-05-07 17:05:49 -0700683 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
684 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700685 } finally {
686 Binder.restoreCallingIdentity(token);
687 }
688 }
689
690 @Override
RoboErika5b02322014-05-07 17:05:49 -0700691 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700692 final int pid = Binder.getCallingPid();
693 final int uid = Binder.getCallingUid();
694 final long token = Binder.clearCallingIdentity();
695
696 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700697 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700698 ArrayList<IBinder> binders = new ArrayList<IBinder>();
699 synchronized (mLock) {
RoboErika8f95142014-05-05 14:23:49 -0700700 ArrayList<MediaSessionRecord> records = mPriorityStack
RoboErika5b02322014-05-07 17:05:49 -0700701 .getActiveSessions(resolvedUserId);
RoboErika8f95142014-05-05 14:23:49 -0700702 int size = records.size();
703 for (int i = 0; i < size; i++) {
704 binders.add(records.get(i).getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700705 }
706 }
707 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800708 } finally {
709 Binder.restoreCallingIdentity(token);
710 }
711 }
RoboErika278ea72014-04-24 14:49:01 -0700712
RoboErik2e7a9162014-06-04 16:53:45 -0700713 @Override
714 public void addSessionsListener(IActiveSessionsListener listener,
715 ComponentName componentName, int userId) throws RemoteException {
716 final int pid = Binder.getCallingPid();
717 final int uid = Binder.getCallingUid();
718 final long token = Binder.clearCallingIdentity();
719
720 try {
721 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
722 synchronized (mLock) {
723 int index = findIndexOfSessionsListenerLocked(listener);
724 if (index != -1) {
725 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
726 return;
727 }
728 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700729 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700730 try {
731 listener.asBinder().linkToDeath(record, 0);
732 } catch (RemoteException e) {
733 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
734 return;
735 }
736 mSessionsListeners.add(record);
737 }
738 } finally {
739 Binder.restoreCallingIdentity(token);
740 }
741 }
742
743 @Override
744 public void removeSessionsListener(IActiveSessionsListener listener)
745 throws RemoteException {
746 synchronized (mLock) {
747 int index = findIndexOfSessionsListenerLocked(listener);
748 if (index != -1) {
749 SessionsListenerRecord record = mSessionsListeners.remove(index);
750 try {
751 record.mListener.asBinder().unlinkToDeath(record, 0);
752 } catch (Exception e) {
753 // ignore exceptions, the record is being removed
754 }
755 }
756 }
757 }
758
RoboErik8a2cfc32014-05-16 11:19:38 -0700759 /**
760 * Handles the dispatching of the media button events to one of the
761 * registered listeners, or if there was none, broadcast an
762 * ACTION_MEDIA_BUTTON intent to the rest of the system.
763 *
764 * @param keyEvent a non-null KeyEvent whose key code is one of the
765 * supported media buttons
766 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
767 * while this key event is dispatched.
768 */
769 @Override
770 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
771 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
772 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
773 return;
774 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700775
RoboErik8a2cfc32014-05-16 11:19:38 -0700776 final int pid = Binder.getCallingPid();
777 final int uid = Binder.getCallingUid();
778 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700779 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700780 if (DEBUG) {
781 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
782 + keyEvent);
783 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700784 if (!isUserSetupComplete()) {
785 // Global media key handling can have the side-effect of starting new
786 // activities which is undesirable while setup is in progress.
787 Slog.i(TAG, "Not dispatching media key event because user "
788 + "setup is in progress.");
789 return;
790 }
Jaewan Kimd409d182016-09-19 21:19:55 +0900791 if (isGlobalPriorityActive() && uid != Process.SYSTEM_UID) {
792 // Prevent dispatching key event through reflection while the global priority
793 // session is active.
794 Slog.i(TAG, "Only the system can dispatch media key event "
795 + "to the global priority session.");
796 return;
797 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700798
RoboErik8a2cfc32014-05-16 11:19:38 -0700799 synchronized (mLock) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800800 if (!isGlobalPriorityActive() && isVoiceKey(keyEvent.getKeyCode())) {
801 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700802 } else {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800803 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, true);
RoboErik8a2cfc32014-05-16 11:19:38 -0700804 }
805 }
806 } finally {
807 Binder.restoreCallingIdentity(token);
808 }
809 }
810
RoboErika278ea72014-04-24 14:49:01 -0700811 @Override
Jaewan Kim50269362016-12-23 11:22:02 +0900812 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
813 final int pid = Binder.getCallingPid();
814 final int uid = Binder.getCallingUid();
815 final long token = Binder.clearCallingIdentity();
816 try {
817 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
818 if (getContext().checkPermission(
819 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
820 != PackageManager.PERMISSION_GRANTED) {
821 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
822 " permission.");
823 }
824
825 synchronized (mLock) {
826 UserRecord user = mUserRecords.get(UserHandle.getUserId(uid));
827 if (user.mOnVolumeKeyLongPressListener != null &&
828 user.mOnVolumeKeyLongPressListenerUid != uid) {
829 Log.w(TAG, "Volume key long-press listener cannot be reset by another app");
830 return;
831 }
832
833 user.mOnVolumeKeyLongPressListener = listener;
834 user.mOnVolumeKeyLongPressListenerUid = uid;
835
836 Log.d(TAG, "Volume key long-press listener "
837 + listener + " is set by " + getCallingPackageName(uid));
838
839 if (user.mOnVolumeKeyLongPressListener != null) {
840 try {
841 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
842 new IBinder.DeathRecipient() {
843 @Override
844 public void binderDied() {
845 synchronized (mLock) {
846 user.mOnVolumeKeyLongPressListener = null;
847 }
848 }
849 }, 0);
850 } catch (RemoteException e) {
851 Log.w(TAG, "Failed to set death recipient "
852 + user.mOnVolumeKeyLongPressListener);
853 user.mOnVolumeKeyLongPressListener = null;
854 }
855 }
856 }
857 } finally {
858 Binder.restoreCallingIdentity(token);
859 }
860 }
861
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800862 @Override
863 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
864 final int pid = Binder.getCallingPid();
865 final int uid = Binder.getCallingUid();
866 final long token = Binder.clearCallingIdentity();
867 try {
868 // Enforce SET_MEDIA_KEY_LISTENER permission.
869 if (getContext().checkPermission(
870 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
871 != PackageManager.PERMISSION_GRANTED) {
872 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
873 " permission.");
874 }
875
876 synchronized (mLock) {
877 int userId = UserHandle.getUserId(uid);
878 UserRecord user = mUserRecords.get(userId);
879 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
880 Log.w(TAG, "Media key listener cannot be reset by another app");
881 return;
882 }
883
884 user.mOnMediaKeyListener = listener;
885 user.mOnMediaKeyListenerUid = uid;
886
887 Log.d(TAG, "Media key listener " + user.mOnMediaKeyListener
888 + " is set by " + getCallingPackageName(uid));
889
890 if (user.mOnMediaKeyListener != null) {
891 try {
892 user.mOnMediaKeyListener.asBinder().linkToDeath(
893 new IBinder.DeathRecipient() {
894 @Override
895 public void binderDied() {
896 synchronized (mLock) {
897 user.mOnMediaKeyListener = null;
898 }
899 }
900 }, 0);
901 } catch (RemoteException e) {
902 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
903 user.mOnMediaKeyListener = null;
904 }
905 }
906 }
907 } finally {
908 Binder.restoreCallingIdentity(token);
909 }
910 }
911
Jaewan Kim50269362016-12-23 11:22:02 +0900912 /**
913 * Handles the dispatching of the volume button events to one of the
914 * registered listeners. If there's a volume key long-press listener and
915 * there's no active global priority session, long-pressess will be sent to the
916 * long-press listener instead of adjusting volume.
917 *
918 * @param keyEvent a non-null KeyEvent whose key code is one of the
919 * {@link KeyEvent#KEYCODE_VOLUME_UP},
920 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
921 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
922 * @param stream stream type to adjust volume.
923 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
924 */
925 @Override
926 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
927 if (keyEvent == null ||
928 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
929 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
930 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
931 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
932 return;
933 }
934
935 final int pid = Binder.getCallingPid();
936 final int uid = Binder.getCallingUid();
937 final long token = Binder.clearCallingIdentity();
938
939 if (DEBUG) {
940 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
941 + keyEvent);
942 }
943
944 try {
945 synchronized (mLock) {
946 // Only consider full user.
947 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
948
949 if (mPriorityStack.isGlobalPriorityActive()
950 || user.mOnVolumeKeyLongPressListener == null) {
951 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
952 } else {
953 // TODO: Consider the case when both volume up and down keys are pressed
954 // at the same time.
955 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
956 if (keyEvent.getRepeatCount() == 0) {
957 user.mInitialDownVolumeKeyEvent = keyEvent;
958 user.mInitialDownVolumeStream = stream;
959 user.mInitialDownMusicOnly = musicOnly;
960 }
961 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
962 if (user.mInitialDownVolumeKeyEvent != null) {
963 dispatchVolumeKeyLongPressLocked(
964 user.mInitialDownVolumeKeyEvent);
965 // Mark that the key is already handled.
966 user.mInitialDownVolumeKeyEvent = null;
967 }
968 dispatchVolumeKeyLongPressLocked(keyEvent);
969 }
970 } else { // if up
971 if (user.mInitialDownVolumeKeyEvent != null
972 && user.mInitialDownVolumeKeyEvent.getDownTime()
973 == keyEvent.getDownTime()) {
974 // Short-press. Should change volume.
975 dispatchVolumeKeyEventLocked(
976 user.mInitialDownVolumeKeyEvent,
977 user.mInitialDownVolumeStream,
978 user.mInitialDownMusicOnly);
979 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
980 } else {
981 dispatchVolumeKeyLongPressLocked(keyEvent);
982 }
983 }
984 }
985 }
986 } finally {
987 Binder.restoreCallingIdentity(token);
988 }
989 }
990
991 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
992 // Only consider full user.
993 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
994 try {
995 user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
996 } catch (RemoteException e) {
997 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
998 }
999 }
1000
1001 private void dispatchVolumeKeyEventLocked(
1002 KeyEvent keyEvent, int stream, boolean musicOnly) {
1003 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1004 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1005 int direction = 0;
1006 boolean isMute = false;
1007 switch (keyEvent.getKeyCode()) {
1008 case KeyEvent.KEYCODE_VOLUME_UP:
1009 direction = AudioManager.ADJUST_RAISE;
1010 break;
1011 case KeyEvent.KEYCODE_VOLUME_DOWN:
1012 direction = AudioManager.ADJUST_LOWER;
1013 break;
1014 case KeyEvent.KEYCODE_VOLUME_MUTE:
1015 isMute = true;
1016 break;
1017 }
1018 if (down || up) {
1019 int flags = AudioManager.FLAG_FROM_KEY;
1020 if (musicOnly) {
1021 // This flag is used when the screen is off to only affect active media.
1022 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1023 } else {
1024 // These flags are consistent with the home screen
1025 if (up) {
1026 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1027 } else {
1028 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1029 }
1030 }
1031 if (direction != 0) {
1032 // If this is action up we want to send a beep for non-music events
1033 if (up) {
1034 direction = 0;
1035 }
1036 dispatchAdjustVolumeLocked(stream, direction, flags);
1037 } else if (isMute) {
1038 if (down && keyEvent.getRepeatCount() == 0) {
1039 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1040 }
1041 }
1042 }
1043 }
1044
1045 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001046 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001047 final long token = Binder.clearCallingIdentity();
1048 try {
1049 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001050 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001051 }
1052 } finally {
1053 Binder.restoreCallingIdentity(token);
1054 }
1055 }
1056
1057 @Override
RoboErik19c95182014-06-23 15:38:48 -07001058 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1059 final int pid = Binder.getCallingPid();
1060 final int uid = Binder.getCallingUid();
1061 final long token = Binder.clearCallingIdentity();
1062 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001063 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001064 mRvc = rvc;
1065 } finally {
1066 Binder.restoreCallingIdentity(token);
1067 }
1068 }
1069
1070 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001071 public boolean isGlobalPriorityActive() {
1072 return mPriorityStack.isGlobalPriorityActive();
1073 }
1074
1075 @Override
RoboErika278ea72014-04-24 14:49:01 -07001076 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1077 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
1078 != PackageManager.PERMISSION_GRANTED) {
1079 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
1080 + Binder.getCallingPid()
1081 + ", uid=" + Binder.getCallingUid());
1082 return;
1083 }
1084
1085 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1086 pw.println();
1087
1088 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001089 pw.println(mSessionsListeners.size() + " sessions listeners.");
RoboErik4646d282014-05-13 10:13:04 -07001090 int count = mAllSessions.size();
RoboErika8f95142014-05-05 14:23:49 -07001091 pw.println(count + " Sessions:");
RoboErika278ea72014-04-24 14:49:01 -07001092 for (int i = 0; i < count; i++) {
RoboErik4646d282014-05-13 10:13:04 -07001093 mAllSessions.get(i).dump(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001094 pw.println();
RoboErika278ea72014-04-24 14:49:01 -07001095 }
RoboErika5b02322014-05-07 17:05:49 -07001096 mPriorityStack.dump(pw, "");
RoboErika8f95142014-05-05 14:23:49 -07001097
RoboErik4646d282014-05-13 10:13:04 -07001098 pw.println("User Records:");
1099 count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001100 for (int i = 0; i < count; i++) {
RoboErik7b3da2d2015-02-02 15:21:29 -08001101 UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
RoboErik4646d282014-05-13 10:13:04 -07001102 user.dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001103 }
1104 }
1105 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001106
RoboErik2e7a9162014-06-04 16:53:45 -07001107 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1108 final int uid) {
1109 String packageName = null;
1110 if (componentName != null) {
1111 // If they gave us a component name verify they own the
1112 // package
1113 packageName = componentName.getPackageName();
1114 enforcePackageName(packageName, uid);
1115 }
1116 // Check that they can make calls on behalf of the user and
1117 // get the final user id
1118 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1119 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1120 // Check if they have the permissions or their component is
1121 // enabled for the user they're calling from.
1122 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1123 return resolvedUserId;
1124 }
1125
Jaewan Kim50269362016-12-23 11:22:02 +09001126 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
1127 MediaSessionRecord session = mPriorityStack.getDefaultVolumeSession(mCurrentUserIdList);
1128
RoboErik9c785402014-11-11 16:52:26 -08001129 boolean preferSuggestedStream = false;
1130 if (isValidLocalStreamType(suggestedStream)
1131 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1132 preferSuggestedStream = true;
1133 }
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001134 if (DEBUG) {
1135 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1136 + flags + ", suggestedStream=" + suggestedStream
1137 + ", preferSuggestedStream=" + preferSuggestedStream);
1138 }
RoboErik9c785402014-11-11 16:52:26 -08001139 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001140 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1141 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001142 if (DEBUG) {
1143 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001144 }
RoboErikb7c014c2014-07-22 15:58:22 -07001145 return;
RoboErik3c45c292014-07-08 16:47:31 -07001146 }
Shibin George19e84042016-06-14 20:42:13 +05301147
1148 // Execute mAudioService.adjustSuggestedStreamVolume() on
1149 // handler thread of MediaSessionService.
1150 // This will release the MediaSessionService.mLock sooner and avoid
1151 // a potential deadlock between MediaSessionService.mLock and
1152 // ActivityManagerService lock.
1153 mHandler.post(new Runnable() {
1154 @Override
1155 public void run() {
1156 try {
1157 String packageName = getContext().getOpPackageName();
1158 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1159 flags, packageName, TAG);
1160 } catch (RemoteException e) {
1161 Log.e(TAG, "Error adjusting default volume.", e);
1162 }
1163 }
1164 });
RoboErikb69ffd42014-05-30 14:57:59 -07001165 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001166 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001167 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001168 }
1169 }
1170
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001171 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001172 int action = keyEvent.getAction();
1173 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1174 if (action == KeyEvent.ACTION_DOWN) {
1175 if (keyEvent.getRepeatCount() == 0) {
1176 mVoiceButtonDown = true;
1177 mVoiceButtonHandled = false;
1178 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1179 mVoiceButtonHandled = true;
1180 startVoiceInput(needWakeLock);
1181 }
1182 } else if (action == KeyEvent.ACTION_UP) {
1183 if (mVoiceButtonDown) {
1184 mVoiceButtonDown = false;
1185 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1186 // Resend the down then send this event through
1187 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001188 dispatchMediaKeyEventLocked(downEvent, needWakeLock, true);
1189 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, true);
RoboErik9a9d0b52014-05-20 14:53:39 -07001190 }
1191 }
1192 }
1193 }
1194
1195 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001196 boolean checkMediaKeyListener) {
1197 // If we don't have a media button receiver to fall back on
1198 // include non-playing sessions for dispatching.
1199 boolean useNotPlayingSessions = true;
1200 for (int userId : mCurrentUserIdList) {
1201 UserRecord ur = mUserRecords.get(userId);
1202 if (ur.mLastMediaButtonReceiver != null
1203 || ur.mRestoredMediaButtonReceiver != null) {
1204 useNotPlayingSessions = false;
1205 break;
1206 }
1207 }
1208 if (DEBUG) {
1209 Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
1210 + useNotPlayingSessions);
1211 }
1212
1213 MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
1214 mCurrentUserIdList, useNotPlayingSessions);
1215
1216 if ((session == null
1217 || !session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY))
1218 && checkMediaKeyListener) {
1219 // Only consider full user.
1220 UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
1221 if (user.mOnMediaKeyListener != null) {
1222 if (DEBUG_KEY_EVENT) {
1223 Log.d(TAG, "Send " + keyEvent + " to media key listener");
1224 }
1225 try {
1226 user.mOnMediaKeyListener.onMediaKey(keyEvent,
1227 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
1228 return;
1229 } catch (RemoteException e) {
1230 Log.w(TAG, "Failed to send " + keyEvent + " to media key listener");
1231 }
1232 }
1233 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001234 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001235 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001236 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001237 }
1238 if (needWakeLock) {
1239 mKeyEventReceiver.aquireWakeLockLocked();
1240 }
Jaewan Kim50269362016-12-23 11:22:02 +09001241 // 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 -07001242 session.sendMediaButton(keyEvent,
1243 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001244 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001245 getContext().getPackageName());
RoboErik9a9d0b52014-05-20 14:53:39 -07001246 } else {
RoboErikb214efb2014-07-24 13:20:30 -07001247 // Launch the last PendingIntent we had with priority
Jaewan Kim8f729082016-06-21 12:36:26 +09001248 for (int userId : mCurrentUserIdList) {
1249 UserRecord user = mUserRecords.get(userId);
1250 if (user.mLastMediaButtonReceiver == null
1251 && user.mRestoredMediaButtonReceiver == null) {
1252 continue;
1253 }
RoboErikb214efb2014-07-24 13:20:30 -07001254 if (needWakeLock) {
1255 mKeyEventReceiver.aquireWakeLockLocked();
1256 }
1257 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Insun Kang2054db32016-04-07 15:34:34 +09001258 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RoboErikb214efb2014-07-24 13:20:30 -07001259 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1260 try {
RoboErikc8f92d12015-01-05 16:48:07 -08001261 if (user.mLastMediaButtonReceiver != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001262 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001263 Log.d(TAG, "Sending " + keyEvent
1264 + " to the last known pendingIntent "
1265 + user.mLastMediaButtonReceiver);
1266 }
RoboErikc8f92d12015-01-05 16:48:07 -08001267 user.mLastMediaButtonReceiver.send(getContext(),
1268 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
riddle_hsu02ed0122015-10-20 16:00:15 +08001269 mediaButtonIntent, mKeyEventReceiver, mHandler);
RoboErikc8f92d12015-01-05 16:48:07 -08001270 } else {
Jaewan Kim50269362016-12-23 11:22:02 +09001271 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001272 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1273 + user.mRestoredMediaButtonReceiver);
1274 }
RoboErikc8f92d12015-01-05 16:48:07 -08001275 mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
1276 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim8f729082016-06-21 12:36:26 +09001277 UserHandle.of(userId));
RoboErikc8f92d12015-01-05 16:48:07 -08001278 }
RoboErikb214efb2014-07-24 13:20:30 -07001279 } catch (CanceledException e) {
1280 Log.i(TAG, "Error sending key event to media button receiver "
1281 + user.mLastMediaButtonReceiver, e);
1282 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001283 return;
RoboErik9a9d0b52014-05-20 14:53:39 -07001284 }
Jaewan Kim8f729082016-06-21 12:36:26 +09001285 if (DEBUG) {
1286 Log.d(TAG, "Sending media key ordered broadcast");
1287 }
1288 if (needWakeLock) {
1289 mMediaEventWakeLock.acquire();
1290 }
1291 // Fallback to legacy behavior
1292 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1293 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1294 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1295 if (needWakeLock) {
1296 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1297 }
1298 // Send broadcast only to the full user.
1299 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1300 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001301 }
1302 }
1303
1304 private void startVoiceInput(boolean needWakeLock) {
1305 Intent voiceIntent = null;
1306 // select which type of search to launch:
1307 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1308 // - device locked or screen off: action is
1309 // ACTION_VOICE_SEARCH_HANDS_FREE
1310 // with EXTRA_SECURE set to true if the device is securely locked
1311 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1312 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1313 if (!isLocked && pm.isScreenOn()) {
1314 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1315 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1316 } else {
1317 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1318 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1319 isLocked && mKeyguardManager.isKeyguardSecure());
1320 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1321 }
1322 // start the search activity
1323 if (needWakeLock) {
1324 mMediaEventWakeLock.acquire();
1325 }
1326 try {
1327 if (voiceIntent != null) {
1328 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1329 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001330 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001331 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1332 }
1333 } catch (ActivityNotFoundException e) {
1334 Log.w(TAG, "No activity for search: " + e);
1335 } finally {
1336 if (needWakeLock) {
1337 mMediaEventWakeLock.release();
1338 }
1339 }
1340 }
1341
1342 private boolean isVoiceKey(int keyCode) {
1343 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1344 }
1345
Jeff Brown38d3feb2015-03-19 18:26:30 -07001346 private boolean isUserSetupComplete() {
1347 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1348 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1349 }
1350
RoboErik9c785402014-11-11 16:52:26 -08001351 // we only handle public stream types, which are 0-5
1352 private boolean isValidLocalStreamType(int streamType) {
1353 return streamType >= AudioManager.STREAM_VOICE_CALL
1354 && streamType <= AudioManager.STREAM_NOTIFICATION;
1355 }
1356
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001357 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1358 private KeyEvent mKeyEvent;
1359 private boolean mNeedWakeLock;
1360 private boolean mHandled;
1361
1362 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1363 super(mHandler);
1364 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1365 mKeyEvent = keyEvent;
1366 mNeedWakeLock = needWakeLock;
1367 }
1368
1369 @Override
1370 public void run() {
1371 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1372 dispatchMediaKeyEvent();
1373 }
1374
1375 @Override
1376 protected void onReceiveResult(int resultCode, Bundle resultData) {
1377 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1378 mHandled = true;
1379 mHandler.removeCallbacks(this);
1380 return;
1381 }
1382 dispatchMediaKeyEvent();
1383 }
1384
1385 private void dispatchMediaKeyEvent() {
1386 if (mHandled) {
1387 return;
1388 }
1389 mHandled = true;
1390 mHandler.removeCallbacks(this);
1391 synchronized (mLock) {
1392 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock, false);
1393 }
1394 }
1395 }
1396
RoboErik418c10c2014-05-19 09:25:25 -07001397 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1398
RoboErikb214efb2014-07-24 13:20:30 -07001399 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1400 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001401 private final Handler mHandler;
1402 private int mRefCount = 0;
1403 private int mLastTimeoutId = 0;
1404
1405 public KeyEventWakeLockReceiver(Handler handler) {
1406 super(handler);
1407 mHandler = handler;
1408 }
1409
1410 public void onTimeout() {
1411 synchronized (mLock) {
1412 if (mRefCount == 0) {
1413 // We've already released it, so just return
1414 return;
1415 }
1416 mLastTimeoutId++;
1417 mRefCount = 0;
1418 releaseWakeLockLocked();
1419 }
1420 }
1421
1422 public void aquireWakeLockLocked() {
1423 if (mRefCount == 0) {
1424 mMediaEventWakeLock.acquire();
1425 }
1426 mRefCount++;
1427 mHandler.removeCallbacks(this);
1428 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1429
1430 }
1431
1432 @Override
1433 public void run() {
1434 onTimeout();
1435 }
1436
RoboErik8a2cfc32014-05-16 11:19:38 -07001437 @Override
1438 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001439 if (resultCode < mLastTimeoutId) {
1440 // Ignore results from calls that were before the last
1441 // timeout, just in case.
1442 return;
1443 } else {
1444 synchronized (mLock) {
1445 if (mRefCount > 0) {
1446 mRefCount--;
1447 if (mRefCount == 0) {
1448 releaseWakeLockLocked();
1449 }
1450 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001451 }
1452 }
1453 }
RoboErik418c10c2014-05-19 09:25:25 -07001454
1455 private void releaseWakeLockLocked() {
1456 mMediaEventWakeLock.release();
1457 mHandler.removeCallbacks(this);
1458 }
RoboErikb214efb2014-07-24 13:20:30 -07001459
1460 @Override
1461 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1462 String resultData, Bundle resultExtras) {
1463 onReceiveResult(resultCode, null);
1464 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001465 };
1466
1467 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1468 @Override
1469 public void onReceive(Context context, Intent intent) {
1470 if (intent == null) {
1471 return;
1472 }
1473 Bundle extras = intent.getExtras();
1474 if (extras == null) {
1475 return;
1476 }
1477 synchronized (mLock) {
1478 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1479 && mMediaEventWakeLock.isHeld()) {
1480 mMediaEventWakeLock.release();
1481 }
1482 }
1483 }
1484 };
RoboErik01fe6612014-02-13 14:19:04 -08001485 }
1486
RoboErik2e7a9162014-06-04 16:53:45 -07001487 final class MessageHandler extends Handler {
1488 private static final int MSG_SESSIONS_CHANGED = 1;
1489
1490 @Override
1491 public void handleMessage(Message msg) {
1492 switch (msg.what) {
1493 case MSG_SESSIONS_CHANGED:
1494 pushSessionsChanged(msg.arg1);
1495 break;
1496 }
1497 }
1498
1499 public void post(int what, int arg1, int arg2) {
1500 obtainMessage(what, arg1, arg2).sendToTarget();
1501 }
1502 }
RoboErik01fe6612014-02-13 14:19:04 -08001503}