blob: e6c38afb9db5dd9c0756094134b2e29972d0f67e [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;
Jaewan Kima7dce192017-02-16 17:10:54 +090033import android.content.pm.UserInfo;
RoboErik7aef77b2014-08-08 15:56:54 -070034import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070035import android.media.AudioManager;
John Spurlockeb69e242015-02-17 17:15:04 -050036import android.media.AudioManagerInternal;
RoboErik94c716e2014-09-14 13:54:31 -070037import android.media.AudioSystem;
RoboErikb69ffd42014-05-30 14:57:59 -070038import android.media.IAudioService;
RoboErik19c95182014-06-23 15:38:48 -070039import android.media.IRemoteVolumeController;
RoboErik2e7a9162014-06-04 16:53:45 -070040import android.media.session.IActiveSessionsListener;
Jaewan Kimbd16f452017-02-03 16:21:38 +090041import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080042import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090043import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070044import android.media.session.ISession;
45import android.media.session.ISessionCallback;
46import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070047import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080048import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070049import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080050import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070051import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080052import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070053import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070054import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070055import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090056import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080057import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070058import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070059import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070060import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090061import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070062import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070063import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080064import android.text.TextUtils;
65import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070066import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070067import android.util.SparseArray;
Jaewan Kima7dce192017-02-16 17:10:54 +090068import android.util.SparseIntArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070069import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090070import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080071
John Spurlockeb69e242015-02-17 17:15:04 -050072import com.android.server.LocalServices;
RoboErik01fe6612014-02-13 14:19:04 -080073import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070074import com.android.server.Watchdog;
75import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080076
RoboErika278ea72014-04-24 14:49:01 -070077import java.io.FileDescriptor;
78import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080079import java.util.ArrayList;
Jaewan Kim8f729082016-06-21 12:36:26 +090080import java.util.Arrays;
RoboErike7880d82014-04-30 12:48:25 -070081import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080082
83/**
84 * System implementation of MediaSessionManager
85 */
RoboErika278ea72014-04-24 14:49:01 -070086public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080087 private static final String TAG = "MediaSessionService";
88 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090089 // Leave log for key event always.
90 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080091
RoboErik418c10c2014-05-19 09:25:25 -070092 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080093 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070094
RoboErik01fe6612014-02-13 14:19:04 -080095 private final SessionManagerImpl mSessionManagerImpl;
96
Jaewan Kima7dce192017-02-16 17:10:54 +090097 // Keeps the full user id for each user.
98 private final SparseIntArray mFullUserIds = new SparseIntArray();
99 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
RoboErik2e7a9162014-06-04 16:53:45 -0700100 private final ArrayList<SessionsListenerRecord> mSessionsListeners
101 = new ArrayList<SessionsListenerRecord>();
RoboErik01fe6612014-02-13 14:19:04 -0800102 private final Object mLock = new Object();
RoboErik2e7a9162014-06-04 16:53:45 -0700103 private final MessageHandler mHandler = new MessageHandler();
RoboErik8a2cfc32014-05-16 11:19:38 -0700104 private final PowerManager.WakeLock mMediaEventWakeLock;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900105 private final int mLongPressTimeout;
RoboErik01fe6612014-02-13 14:19:04 -0800106
RoboErik9a9d0b52014-05-20 14:53:39 -0700107 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700108 private IAudioService mAudioService;
John Spurlockeb69e242015-02-17 17:15:04 -0500109 private AudioManagerInternal mAudioManagerInternal;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700110 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700111 private SettingsObserver mSettingsObserver;
RoboErik9a9d0b52014-05-20 14:53:39 -0700112
Jaewan Kima7dce192017-02-16 17:10:54 +0900113 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
114 // It's always not null after the MediaSessionService is started.
115 private FullUserRecord mCurrentFullUserRecord;
116 private MediaSessionRecord mGlobalPrioritySession;
RoboErike7880d82014-04-30 12:48:25 -0700117
RoboErik19c95182014-06-23 15:38:48 -0700118 // Used to notify system UI when remote volume was changed. TODO find a
119 // better way to handle this.
120 private IRemoteVolumeController mRvc;
121
RoboErik01fe6612014-02-13 14:19:04 -0800122 public MediaSessionService(Context context) {
123 super(context);
124 mSessionManagerImpl = new SessionManagerImpl();
RoboErik8a2cfc32014-05-16 11:19:38 -0700125 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
126 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900127 mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
RoboErik01fe6612014-02-13 14:19:04 -0800128 }
129
130 @Override
131 public void onStart() {
132 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700133 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700134 mKeyguardManager =
135 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700136 mAudioService = getAudioService();
John Spurlockeb69e242015-02-17 17:15:04 -0500137 mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700138 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700139 mSettingsObserver = new SettingsObserver();
140 mSettingsObserver.observe();
RoboErikc8f92d12015-01-05 16:48:07 -0800141
142 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700143 }
144
145 private IAudioService getAudioService() {
146 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
147 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700148 }
149
Jaewan Kima7dce192017-02-16 17:10:54 +0900150 private boolean isGlobalPriorityActiveLocked() {
151 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
152 }
153
RoboErika8f95142014-05-05 14:23:49 -0700154 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700155 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900156 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
157 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700158 Log.d(TAG, "Unknown session updated. Ignoring.");
159 return;
160 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900161 user.mPriorityStack.onSessionStateChange(record);
162 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
163 mGlobalPrioritySession = record;
164 }
RoboErike7880d82014-04-30 12:48:25 -0700165 }
RoboErik2e7a9162014-06-04 16:53:45 -0700166 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErike7880d82014-04-30 12:48:25 -0700167 }
168
RoboErik9c5b7cb2015-01-15 15:09:09 -0800169 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900170 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800171 */
172 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900173 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800174 return;
175 }
176 try {
177 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
178 } catch (Exception e) {
179 Log.wtf(TAG, "Error sending volume change to system UI.", e);
180 }
181 }
182
RoboErika8f95142014-05-05 14:23:49 -0700183 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
RoboErik2e7a9162014-06-04 16:53:45 -0700184 boolean updateSessions = false;
RoboErika8f95142014-05-05 14:23:49 -0700185 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900186 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
187 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700188 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
189 return;
190 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900191 updateSessions = user.mPriorityStack.onPlaystateChange(record, oldState, newState);
RoboErik2e7a9162014-06-04 16:53:45 -0700192 }
193 if (updateSessions) {
194 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
RoboErika8f95142014-05-05 14:23:49 -0700195 }
196 }
197
RoboErik19c95182014-06-23 15:38:48 -0700198 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
199 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900200 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
201 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700202 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
203 return;
204 }
205 pushRemoteVolumeUpdateLocked(record.getUserId());
206 }
207 }
208
RoboErika278ea72014-04-24 14:49:01 -0700209 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900210 public void onStartUser(int userId) {
211 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700212 updateUser();
213 }
214
215 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900216 public void onSwitchUser(int userId) {
217 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700218 updateUser();
219 }
220
221 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900222 public void onStopUser(int userId) {
223 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700224 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900225 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700226 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900227 if (user.mFullUserId == userId) {
228 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
229 mUserRecords.remove(userId);
230 } else {
231 user.destroySessionsForUserLocked(userId);
232 }
RoboErik4646d282014-05-13 10:13:04 -0700233 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900234 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700235 }
236 }
237
238 @Override
RoboErika278ea72014-04-24 14:49:01 -0700239 public void monitor() {
240 synchronized (mLock) {
241 // Check for deadlock
242 }
243 }
244
RoboErik4646d282014-05-13 10:13:04 -0700245 protected void enforcePhoneStatePermission(int pid, int uid) {
246 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
247 != PackageManager.PERMISSION_GRANTED) {
248 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
249 }
250 }
251
RoboErik01fe6612014-02-13 14:19:04 -0800252 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700253 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800254 destroySessionLocked(session);
255 }
256 }
257
258 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700259 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800260 destroySessionLocked(session);
261 }
262 }
263
RoboErik4646d282014-05-13 10:13:04 -0700264 private void updateUser() {
265 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900266 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900267 mFullUserIds.clear();
268 List<UserInfo> allUsers = manager.getUsers();
269 if (allUsers != null) {
270 for (UserInfo userInfo : allUsers) {
271 if (userInfo.isManagedProfile()) {
272 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
273 } else {
274 mFullUserIds.put(userInfo.id, userInfo.id);
275 if (mUserRecords.get(userInfo.id) == null) {
276 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
277 }
278 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900279 }
RoboErik4646d282014-05-13 10:13:04 -0700280 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900281 // Ensure that the current full user exists.
282 int currentFullUserId = ActivityManager.getCurrentUser();
283 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
284 if (mCurrentFullUserRecord == null) {
285 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
286 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
287 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
288 }
289 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700290 }
291 }
292
RoboErik7aef77b2014-08-08 15:56:54 -0700293 private void updateActiveSessionListeners() {
294 synchronized (mLock) {
295 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
296 SessionsListenerRecord listener = mSessionsListeners.get(i);
297 try {
298 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
299 listener.mUserId);
300 } catch (SecurityException e) {
301 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
302 + " is no longer authorized. Disconnecting.");
303 mSessionsListeners.remove(i);
304 try {
305 listener.mListener
306 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
307 } catch (Exception e1) {
308 // ignore
309 }
310 }
311 }
312 }
313 }
314
RoboErik4646d282014-05-13 10:13:04 -0700315 /*
316 * When a session is removed several things need to happen.
317 * 1. We need to remove it from the relevant user.
318 * 2. We need to remove it from the priority stack.
319 * 3. We need to remove it from all sessions.
320 * 4. If this is the system priority session we need to clear it.
321 * 5. We need to unlink to death from the cb binder
322 * 6. We need to tell the session to do any final cleanup (onDestroy)
323 */
RoboErik01fe6612014-02-13 14:19:04 -0800324 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900325 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900326 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900327 }
RoboErik4646d282014-05-13 10:13:04 -0700328 int userId = session.getUserId();
Jaewan Kima7dce192017-02-16 17:10:54 +0900329 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700330 if (user != null) {
331 user.removeSessionLocked(session);
332 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900333 if (mGlobalPrioritySession == session) {
334 mGlobalPrioritySession = null;
335 }
RoboErik4646d282014-05-13 10:13:04 -0700336
337 try {
338 session.getCallback().asBinder().unlinkToDeath(session, 0);
339 } catch (Exception e) {
340 // ignore exceptions while destroying a session.
341 }
342 session.onDestroy();
RoboErik2e7a9162014-06-04 16:53:45 -0700343
344 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
RoboErik01fe6612014-02-13 14:19:04 -0800345 }
346
347 private void enforcePackageName(String packageName, int uid) {
348 if (TextUtils.isEmpty(packageName)) {
349 throw new IllegalArgumentException("packageName may not be empty");
350 }
351 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
352 final int packageCount = packages.length;
353 for (int i = 0; i < packageCount; i++) {
354 if (packageName.equals(packages[i])) {
355 return;
356 }
357 }
358 throw new IllegalArgumentException("packageName is not owned by the calling process");
359 }
360
RoboErike7880d82014-04-30 12:48:25 -0700361 /**
362 * Checks a caller's authorization to register an IRemoteControlDisplay.
363 * Authorization is granted if one of the following is true:
364 * <ul>
365 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
366 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700367 * <li>the caller's listener is one of the enabled notification listeners
368 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700369 * </ul>
370 */
RoboErika5b02322014-05-07 17:05:49 -0700371 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
372 int resolvedUserId) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500373 if (isCurrentVolumeController(uid, pid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700374 if (getContext()
375 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
376 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700377 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
378 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700379 throw new SecurityException("Missing permission to control media.");
380 }
381 }
382
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500383 private boolean isCurrentVolumeController(int uid, int pid) {
384 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
385 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500386 }
387
388 private void enforceSystemUiPermission(String action, int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500389 if (!isCurrentVolumeController(uid, pid)) {
RoboErik19c95182014-06-23 15:38:48 -0700390 throw new SecurityException("Only system ui may " + action);
391 }
392 }
393
RoboErika5b02322014-05-07 17:05:49 -0700394 /**
395 * This checks if the component is an enabled notification listener for the
396 * specified user. Enabled components may only operate on behalf of the user
397 * they're running as.
398 *
399 * @param compName The component that is enabled.
400 * @param userId The user id of the caller.
401 * @param forUserId The user id they're making the request on behalf of.
402 * @return True if the component is enabled, false otherwise
403 */
404 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
405 int forUserId) {
406 if (userId != forUserId) {
407 // You may not access another user's content as an enabled listener.
408 return false;
409 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700410 if (DEBUG) {
411 Log.d(TAG, "Checking if enabled notification listener " + compName);
412 }
RoboErike7880d82014-04-30 12:48:25 -0700413 if (compName != null) {
RoboErik6f0e4dd2014-06-17 16:56:27 -0700414 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
RoboErike7880d82014-04-30 12:48:25 -0700415 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
RoboErika5b02322014-05-07 17:05:49 -0700416 userId);
RoboErike7880d82014-04-30 12:48:25 -0700417 if (enabledNotifListeners != null) {
418 final String[] components = enabledNotifListeners.split(":");
419 for (int i = 0; i < components.length; i++) {
420 final ComponentName component =
421 ComponentName.unflattenFromString(components[i]);
422 if (component != null) {
423 if (compName.equals(component)) {
424 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900425 Log.d(TAG, "ok to get sessions. " + component +
RoboErike7880d82014-04-30 12:48:25 -0700426 " is authorized notification listener");
427 }
428 return true;
429 }
430 }
431 }
432 }
433 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900434 Log.d(TAG, "not ok to get sessions. " + compName +
RoboErika5b02322014-05-07 17:05:49 -0700435 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
RoboErike7880d82014-04-30 12:48:25 -0700436 }
437 }
438 return false;
439 }
440
RoboErika5b02322014-05-07 17:05:49 -0700441 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700442 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800443 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700444 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800445 }
446 }
447
RoboErik4646d282014-05-13 10:13:04 -0700448 /*
449 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700450 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700451 * 2. It needs to be added to all sessions.
452 * 3. It needs to be added to the priority stack.
453 * 4. It needs to be added to the relevant user record.
454 */
RoboErika5b02322014-05-07 17:05:49 -0700455 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
456 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900457 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700458 if (user == null) {
459 Log.wtf(TAG, "Request from invalid user: " + userId);
460 throw new RuntimeException("Session request from invalid user.");
461 }
462
RoboErika5b02322014-05-07 17:05:49 -0700463 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
464 callerPackageName, cb, tag, this, mHandler);
RoboErik01fe6612014-02-13 14:19:04 -0800465 try {
466 cb.asBinder().linkToDeath(session, 0);
467 } catch (RemoteException e) {
468 throw new RuntimeException("Media Session owner died prematurely.", e);
469 }
RoboErik4646d282014-05-13 10:13:04 -0700470
RoboErik4646d282014-05-13 10:13:04 -0700471 user.addSessionLocked(session);
472
RoboErik2e7a9162014-06-04 16:53:45 -0700473 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
474
RoboErik01fe6612014-02-13 14:19:04 -0800475 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900476 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800477 }
478 return session;
479 }
480
RoboErik2e7a9162014-06-04 16:53:45 -0700481 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
482 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800483 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700484 return i;
485 }
486 }
487 return -1;
488 }
489
RoboErik2e7a9162014-06-04 16:53:45 -0700490 private void pushSessionsChanged(int userId) {
491 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900492 FullUserRecord user = getFullUserRecordLocked(userId);
493 if (user == null) {
494 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
495 return;
496 }
497 List<MediaSessionRecord> records = user.mPriorityStack.getActiveSessions(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700498 int size = records.size();
RoboErik870c5a62014-12-02 15:08:26 -0800499 if (size > 0 && records.get(0).isPlaybackActive(false)) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900500 user.rememberMediaButtonReceiverLocked(records.get(0));
RoboErik6f0e4dd2014-06-17 16:56:27 -0700501 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900502 user.pushAddressedPlayerChangedLocked();
Jeff Browndba34ba2014-06-24 20:46:03 -0700503 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700504 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700505 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700506 }
RoboErik19c95182014-06-23 15:38:48 -0700507 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700508 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
509 SessionsListenerRecord record = mSessionsListeners.get(i);
510 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
511 try {
512 record.mListener.onActiveSessionsChanged(tokens);
513 } catch (RemoteException e) {
514 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
515 e);
516 mSessionsListeners.remove(i);
517 }
518 }
519 }
520 }
521 }
522
RoboErik19c95182014-06-23 15:38:48 -0700523 private void pushRemoteVolumeUpdateLocked(int userId) {
524 if (mRvc != null) {
525 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900526 FullUserRecord user = getFullUserRecordLocked(userId);
527 if (user == null) {
528 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
529 return;
530 }
531 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700532 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
533 } catch (RemoteException e) {
534 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
535 }
536 }
537 }
538
Jaewan Kim50269362016-12-23 11:22:02 +0900539 private String getCallingPackageName(int uid) {
540 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
541 if (packages != null && packages.length > 0) {
542 return packages[0];
543 }
544 return "";
545 }
546
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900547 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900548 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900549 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900550 } catch (RemoteException e) {
551 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
552 }
553 }
554
Jaewan Kima7dce192017-02-16 17:10:54 +0900555 private FullUserRecord getFullUserRecordLocked(int userId) {
556 int fullUserId = mFullUserIds.get(userId, -1);
557 if (fullUserId < 0) {
558 return null;
559 }
560 return mUserRecords.get(fullUserId);
561 }
562
RoboErik4646d282014-05-13 10:13:04 -0700563 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900564 * Information about a full user and its corresponding managed profiles.
565 *
566 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
567 * them when he/she presses a media/volume button. So keeping media sessions for them in one
568 * place makes more sense and increases the readability.</p>
569 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700570 */
Jaewan Kima7dce192017-02-16 17:10:54 +0900571 final class FullUserRecord {
572 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
573 private final int mFullUserId;
574 private final MediaSessionStack mPriorityStack = new MediaSessionStack();
RoboErikb214efb2014-07-24 13:20:30 -0700575 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800576 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900577 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700578
Jaewan Kim50269362016-12-23 11:22:02 +0900579 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
580 private int mOnVolumeKeyLongPressListenerUid;
581 private KeyEvent mInitialDownVolumeKeyEvent;
582 private int mInitialDownVolumeStream;
583 private boolean mInitialDownMusicOnly;
584
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800585 private IOnMediaKeyListener mOnMediaKeyListener;
586 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900587 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800588
Jaewan Kima7dce192017-02-16 17:10:54 +0900589 public FullUserRecord(int fullUserId) {
590 mFullUserId = fullUserId;
591 // Restore the remembered media button receiver before the boot.
592 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
593 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
594 if (mediaButtonReceiver == null) {
595 return;
596 }
597 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
598 if (tokens == null || tokens.length != 2) {
599 return;
600 }
601 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
602 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700603 }
604
Jaewan Kima7dce192017-02-16 17:10:54 +0900605 public void destroySessionsForUserLocked(int userId) {
606 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, 0, userId);
607 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700608 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700609 }
610 }
611
RoboErik4646d282014-05-13 10:13:04 -0700612 public void addSessionLocked(MediaSessionRecord session) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900613 mPriorityStack.addSession(session,
614 mFullUserId == mFullUserIds.get(session.getUserId()));
RoboErik4646d282014-05-13 10:13:04 -0700615 }
616
617 public void removeSessionLocked(MediaSessionRecord session) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900618 mPriorityStack.removeSession(session);
RoboErik4646d282014-05-13 10:13:04 -0700619 }
620
621 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900622 pw.print(prefix + "Record for full_user=" + mFullUserId);
623 // Dump managed profile user ids associated with this user.
624 int size = mFullUserIds.size();
625 for (int i = 0; i < size; i++) {
626 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
627 && mFullUserIds.valueAt(i) == mFullUserId) {
628 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
629 }
630 }
631 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700632 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900633 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
634 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900635 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800636 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
637 pw.println(indent + "Media key listener package: " +
638 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900639 pw.println(indent + "Callback: " + mCallback);
640 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
641 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
642 mPriorityStack.dump(pw, indent);
643 }
644
645 // Remember the media button receiver and keep it in the persistent storage.
646 private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
647 PendingIntent receiver = record.getMediaButtonReceiver();
648 if (receiver == null) {
649 return;
650 }
651 mLastMediaButtonReceiver = receiver;
652 ComponentName component = receiver.getIntent().getComponent();
653 if (component != null && record.getPackageName().equals(component.getPackageName())) {
654 String componentName = component.flattenToString();
655 Settings.Secure.putStringForUser(mContentResolver,
656 Settings.System.MEDIA_BUTTON_RECEIVER,
657 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
658 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700659 }
660 }
RoboErikc8f92d12015-01-05 16:48:07 -0800661
Jaewan Kima7dce192017-02-16 17:10:54 +0900662 private void pushAddressedPlayerChangedLocked() {
663 if (mCallback == null) {
664 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800665 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900666 try {
667 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
668 if (mediaButtonSession != null) {
669 mCallback.onAddressedPlayerChangedToMediaSession(
670 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
671 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
672 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
673 mCurrentFullUserRecord.mLastMediaButtonReceiver
674 .getIntent().getComponent());
675 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
676 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
677 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
678 }
679 } catch (RemoteException e) {
680 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
681 }
682 }
683
684 private MediaSessionRecord getMediaButtonSessionLocked() {
685 if (isGlobalPriorityActiveLocked()) {
686 return mGlobalPrioritySession;
687 }
688 // If we don't have a media button receiver to fall back on
689 // include non-playing sessions for dispatching.
690 boolean useNotPlayingSessions = (mLastMediaButtonReceiver == null
691 && mRestoredMediaButtonReceiver == null);
692 return mPriorityStack.getDefaultMediaButtonSession(useNotPlayingSessions);
RoboErikc8f92d12015-01-05 16:48:07 -0800693 }
RoboErik4646d282014-05-13 10:13:04 -0700694 }
695
RoboErik2e7a9162014-06-04 16:53:45 -0700696 final class SessionsListenerRecord implements IBinder.DeathRecipient {
697 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700698 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700699 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700700 private final int mPid;
701 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700702
RoboErik7aef77b2014-08-08 15:56:54 -0700703 public SessionsListenerRecord(IActiveSessionsListener listener,
704 ComponentName componentName,
705 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700706 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700707 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700708 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700709 mPid = pid;
710 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700711 }
712
713 @Override
714 public void binderDied() {
715 synchronized (mLock) {
716 mSessionsListeners.remove(this);
717 }
718 }
719 }
720
RoboErik7aef77b2014-08-08 15:56:54 -0700721 final class SettingsObserver extends ContentObserver {
722 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
723 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
724
725 private SettingsObserver() {
726 super(null);
727 }
728
729 private void observe() {
730 mContentResolver.registerContentObserver(mSecureSettingsUri,
731 false, this, UserHandle.USER_ALL);
732 }
733
734 @Override
735 public void onChange(boolean selfChange, Uri uri) {
736 updateActiveSessionListeners();
737 }
738 }
739
RoboErik07c70772014-03-20 13:33:52 -0700740 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700741 private static final String EXTRA_WAKELOCK_ACQUIRED =
742 "android.media.AudioService.WAKELOCK_ACQUIRED";
743 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
744
RoboErik9a9d0b52014-05-20 14:53:39 -0700745 private boolean mVoiceButtonDown = false;
746 private boolean mVoiceButtonHandled = false;
747
RoboErik07c70772014-03-20 13:33:52 -0700748 @Override
RoboErika5b02322014-05-07 17:05:49 -0700749 public ISession createSession(String packageName, ISessionCallback cb, String tag,
750 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800751 final int pid = Binder.getCallingPid();
752 final int uid = Binder.getCallingUid();
753 final long token = Binder.clearCallingIdentity();
754 try {
755 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700756 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
757 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800758 if (cb == null) {
759 throw new IllegalArgumentException("Controller callback cannot be null");
760 }
RoboErika5b02322014-05-07 17:05:49 -0700761 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
762 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700763 } finally {
764 Binder.restoreCallingIdentity(token);
765 }
766 }
767
768 @Override
RoboErika5b02322014-05-07 17:05:49 -0700769 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700770 final int pid = Binder.getCallingPid();
771 final int uid = Binder.getCallingUid();
772 final long token = Binder.clearCallingIdentity();
773
774 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700775 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700776 ArrayList<IBinder> binders = new ArrayList<IBinder>();
777 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900778 if (resolvedUserId == UserHandle.USER_ALL) {
779 int size = mUserRecords.size();
780 for (int i = 0; i < size; i++) {
781 List<MediaSessionRecord> records =
782 mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(
783 resolvedUserId);
784 for (MediaSessionRecord record : records) {
785 binders.add(record.getControllerBinder().asBinder());
786 }
787 }
788 } else {
789 FullUserRecord user = getFullUserRecordLocked(resolvedUserId);
790 if (user == null) {
791 Log.w(TAG, "getSessions failed. Unknown user " + userId);
792 return binders;
793 }
794 List<MediaSessionRecord> records = user.mPriorityStack
795 .getActiveSessions(resolvedUserId);
796 for (MediaSessionRecord record : records) {
797 binders.add(record.getControllerBinder().asBinder());
798 }
RoboErike7880d82014-04-30 12:48:25 -0700799 }
800 }
801 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800802 } finally {
803 Binder.restoreCallingIdentity(token);
804 }
805 }
RoboErika278ea72014-04-24 14:49:01 -0700806
RoboErik2e7a9162014-06-04 16:53:45 -0700807 @Override
808 public void addSessionsListener(IActiveSessionsListener listener,
809 ComponentName componentName, int userId) throws RemoteException {
810 final int pid = Binder.getCallingPid();
811 final int uid = Binder.getCallingUid();
812 final long token = Binder.clearCallingIdentity();
813
814 try {
815 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
816 synchronized (mLock) {
817 int index = findIndexOfSessionsListenerLocked(listener);
818 if (index != -1) {
819 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
820 return;
821 }
822 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700823 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700824 try {
825 listener.asBinder().linkToDeath(record, 0);
826 } catch (RemoteException e) {
827 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
828 return;
829 }
830 mSessionsListeners.add(record);
831 }
832 } finally {
833 Binder.restoreCallingIdentity(token);
834 }
835 }
836
837 @Override
838 public void removeSessionsListener(IActiveSessionsListener listener)
839 throws RemoteException {
840 synchronized (mLock) {
841 int index = findIndexOfSessionsListenerLocked(listener);
842 if (index != -1) {
843 SessionsListenerRecord record = mSessionsListeners.remove(index);
844 try {
845 record.mListener.asBinder().unlinkToDeath(record, 0);
846 } catch (Exception e) {
847 // ignore exceptions, the record is being removed
848 }
849 }
850 }
851 }
852
RoboErik8a2cfc32014-05-16 11:19:38 -0700853 /**
854 * Handles the dispatching of the media button events to one of the
855 * registered listeners, or if there was none, broadcast an
856 * ACTION_MEDIA_BUTTON intent to the rest of the system.
857 *
858 * @param keyEvent a non-null KeyEvent whose key code is one of the
859 * supported media buttons
860 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
861 * while this key event is dispatched.
862 */
863 @Override
864 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
865 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
866 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
867 return;
868 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700869
RoboErik8a2cfc32014-05-16 11:19:38 -0700870 final int pid = Binder.getCallingPid();
871 final int uid = Binder.getCallingUid();
872 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700873 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700874 if (DEBUG) {
875 Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
876 + keyEvent);
877 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700878 if (!isUserSetupComplete()) {
879 // Global media key handling can have the side-effect of starting new
880 // activities which is undesirable while setup is in progress.
881 Slog.i(TAG, "Not dispatching media key event because user "
882 + "setup is in progress.");
883 return;
884 }
885
RoboErik8a2cfc32014-05-16 11:19:38 -0700886 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900887 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +0900888 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
889 // Prevent dispatching key event through reflection while the global
890 // priority session is active.
891 Slog.i(TAG, "Only the system can dispatch media key event "
892 + "to the global priority session.");
893 return;
894 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900895 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900896 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +0900897 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900898 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900899 }
900 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900901 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim98003d32017-02-24 18:33:04 +0900902 new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
903 return;
904 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900905 Log.w(TAG, "Failed to send " + keyEvent
906 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900907 }
908 }
909 }
Jaewan Kim51255012017-02-24 16:19:14 +0900910 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800911 handleVoiceKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700912 } else {
Jaewan Kim98003d32017-02-24 18:33:04 +0900913 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700914 }
915 }
916 } finally {
917 Binder.restoreCallingIdentity(token);
918 }
919 }
920
RoboErika278ea72014-04-24 14:49:01 -0700921 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +0900922 public void setCallback(ICallback callback) {
923 final int pid = Binder.getCallingPid();
924 final int uid = Binder.getCallingUid();
925 final long token = Binder.clearCallingIdentity();
926 try {
927 if (uid != Process.BLUETOOTH_UID) {
928 throw new SecurityException("Only Bluetooth service processes can set"
929 + " Callback");
930 }
931 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900932 int userId = UserHandle.getUserId(uid);
933 FullUserRecord user = getFullUserRecordLocked(userId);
934 if (user == null || user.mFullUserId != userId) {
935 Log.w(TAG, "Only the full user can set the callback"
936 + ", userId=" + userId);
937 return;
938 }
939 user.mCallback = callback;
940 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +0900941 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900942 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900943 return;
944 }
945 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900946 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +0900947 new IBinder.DeathRecipient() {
948 @Override
949 public void binderDied() {
950 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900951 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +0900952 }
953 }
954 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +0900955 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +0900956 } catch (RemoteException e) {
957 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +0900958 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +0900959 }
960 }
961 } finally {
962 Binder.restoreCallingIdentity(token);
963 }
964 }
965
966 @Override
Jaewan Kim50269362016-12-23 11:22:02 +0900967 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
968 final int pid = Binder.getCallingPid();
969 final int uid = Binder.getCallingUid();
970 final long token = Binder.clearCallingIdentity();
971 try {
972 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
973 if (getContext().checkPermission(
974 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
975 != PackageManager.PERMISSION_GRANTED) {
976 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
977 " permission.");
978 }
979
980 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900981 int userId = UserHandle.getUserId(uid);
982 FullUserRecord user = getFullUserRecordLocked(userId);
983 if (user == null || user.mFullUserId != userId) {
984 Log.w(TAG, "Only the full user can set the volume key long-press listener"
985 + ", userId=" + userId);
986 return;
987 }
Jaewan Kim50269362016-12-23 11:22:02 +0900988 if (user.mOnVolumeKeyLongPressListener != null &&
989 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900990 Log.w(TAG, "The volume key long-press listener cannot be reset"
991 + " by another app , mOnVolumeKeyLongPressListener="
992 + user.mOnVolumeKeyLongPressListenerUid
993 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +0900994 return;
995 }
996
997 user.mOnVolumeKeyLongPressListener = listener;
998 user.mOnVolumeKeyLongPressListenerUid = uid;
999
Jaewan Kima7dce192017-02-16 17:10:54 +09001000 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001001 + listener + " is set by " + getCallingPackageName(uid));
1002
1003 if (user.mOnVolumeKeyLongPressListener != null) {
1004 try {
1005 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1006 new IBinder.DeathRecipient() {
1007 @Override
1008 public void binderDied() {
1009 synchronized (mLock) {
1010 user.mOnVolumeKeyLongPressListener = null;
1011 }
1012 }
1013 }, 0);
1014 } catch (RemoteException e) {
1015 Log.w(TAG, "Failed to set death recipient "
1016 + user.mOnVolumeKeyLongPressListener);
1017 user.mOnVolumeKeyLongPressListener = null;
1018 }
1019 }
1020 }
1021 } finally {
1022 Binder.restoreCallingIdentity(token);
1023 }
1024 }
1025
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001026 @Override
1027 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1028 final int pid = Binder.getCallingPid();
1029 final int uid = Binder.getCallingUid();
1030 final long token = Binder.clearCallingIdentity();
1031 try {
1032 // Enforce SET_MEDIA_KEY_LISTENER permission.
1033 if (getContext().checkPermission(
1034 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1035 != PackageManager.PERMISSION_GRANTED) {
1036 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1037 " permission.");
1038 }
1039
1040 synchronized (mLock) {
1041 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001042 FullUserRecord user = getFullUserRecordLocked(userId);
1043 if (user == null || user.mFullUserId != userId) {
1044 Log.w(TAG, "Only the full user can set the media key listener"
1045 + ", userId=" + userId);
1046 return;
1047 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001048 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001049 Log.w(TAG, "The media key listener cannot be reset by another app. "
1050 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1051 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001052 return;
1053 }
1054
1055 user.mOnMediaKeyListener = listener;
1056 user.mOnMediaKeyListenerUid = uid;
1057
Jaewan Kima7dce192017-02-16 17:10:54 +09001058 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001059 + " is set by " + getCallingPackageName(uid));
1060
1061 if (user.mOnMediaKeyListener != null) {
1062 try {
1063 user.mOnMediaKeyListener.asBinder().linkToDeath(
1064 new IBinder.DeathRecipient() {
1065 @Override
1066 public void binderDied() {
1067 synchronized (mLock) {
1068 user.mOnMediaKeyListener = null;
1069 }
1070 }
1071 }, 0);
1072 } catch (RemoteException e) {
1073 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1074 user.mOnMediaKeyListener = null;
1075 }
1076 }
1077 }
1078 } finally {
1079 Binder.restoreCallingIdentity(token);
1080 }
1081 }
1082
Jaewan Kim50269362016-12-23 11:22:02 +09001083 /**
1084 * Handles the dispatching of the volume button events to one of the
1085 * registered listeners. If there's a volume key long-press listener and
1086 * there's no active global priority session, long-pressess will be sent to the
1087 * long-press listener instead of adjusting volume.
1088 *
1089 * @param keyEvent a non-null KeyEvent whose key code is one of the
1090 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1091 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1092 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1093 * @param stream stream type to adjust volume.
1094 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1095 */
1096 @Override
1097 public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
1098 if (keyEvent == null ||
1099 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1100 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1101 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1102 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1103 return;
1104 }
1105
1106 final int pid = Binder.getCallingPid();
1107 final int uid = Binder.getCallingUid();
1108 final long token = Binder.clearCallingIdentity();
1109
1110 if (DEBUG) {
1111 Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
1112 + keyEvent);
1113 }
1114
1115 try {
1116 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001117 if (isGlobalPriorityActiveLocked()
1118 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001119 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1120 } else {
1121 // TODO: Consider the case when both volume up and down keys are pressed
1122 // at the same time.
1123 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1124 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001125 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001126 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1127 KeyEvent.obtain(keyEvent);
1128 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1129 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001130 mHandler.sendMessageDelayed(
1131 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001132 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1133 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001134 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001135 }
1136 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001137 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001138 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001139 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001140 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001141 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001142 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001143 }
1144 dispatchVolumeKeyLongPressLocked(keyEvent);
1145 }
1146 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001147 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001148 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
1149 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1150 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001151 // Short-press. Should change volume.
1152 dispatchVolumeKeyEventLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001153 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1154 mCurrentFullUserRecord.mInitialDownVolumeStream,
1155 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001156 dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
1157 } else {
1158 dispatchVolumeKeyLongPressLocked(keyEvent);
1159 }
1160 }
1161 }
1162 }
1163 } finally {
1164 Binder.restoreCallingIdentity(token);
1165 }
1166 }
1167
Jaewan Kim50269362016-12-23 11:22:02 +09001168 private void dispatchVolumeKeyEventLocked(
1169 KeyEvent keyEvent, int stream, boolean musicOnly) {
1170 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1171 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1172 int direction = 0;
1173 boolean isMute = false;
1174 switch (keyEvent.getKeyCode()) {
1175 case KeyEvent.KEYCODE_VOLUME_UP:
1176 direction = AudioManager.ADJUST_RAISE;
1177 break;
1178 case KeyEvent.KEYCODE_VOLUME_DOWN:
1179 direction = AudioManager.ADJUST_LOWER;
1180 break;
1181 case KeyEvent.KEYCODE_VOLUME_MUTE:
1182 isMute = true;
1183 break;
1184 }
1185 if (down || up) {
1186 int flags = AudioManager.FLAG_FROM_KEY;
1187 if (musicOnly) {
1188 // This flag is used when the screen is off to only affect active media.
1189 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1190 } else {
1191 // These flags are consistent with the home screen
1192 if (up) {
1193 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1194 } else {
1195 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1196 }
1197 }
1198 if (direction != 0) {
1199 // If this is action up we want to send a beep for non-music events
1200 if (up) {
1201 direction = 0;
1202 }
1203 dispatchAdjustVolumeLocked(stream, direction, flags);
1204 } else if (isMute) {
1205 if (down && keyEvent.getRepeatCount() == 0) {
1206 dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
1207 }
1208 }
1209 }
1210 }
1211
1212 @Override
RoboErik7c82ced2014-12-04 17:39:08 -08001213 public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
RoboErikb69ffd42014-05-30 14:57:59 -07001214 final long token = Binder.clearCallingIdentity();
1215 try {
1216 synchronized (mLock) {
Jaewan Kim50269362016-12-23 11:22:02 +09001217 dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001218 }
1219 } finally {
1220 Binder.restoreCallingIdentity(token);
1221 }
1222 }
1223
1224 @Override
RoboErik19c95182014-06-23 15:38:48 -07001225 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1226 final int pid = Binder.getCallingPid();
1227 final int uid = Binder.getCallingUid();
1228 final long token = Binder.clearCallingIdentity();
1229 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001230 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001231 mRvc = rvc;
1232 } finally {
1233 Binder.restoreCallingIdentity(token);
1234 }
1235 }
1236
1237 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001238 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001239 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001240 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001241 }
RoboErikde9ba392014-09-26 12:51:01 -07001242 }
1243
1244 @Override
RoboErika278ea72014-04-24 14:49:01 -07001245 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1246 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
1247 != PackageManager.PERMISSION_GRANTED) {
1248 pw.println("Permission Denial: can't dump MediaSessionService from from pid="
1249 + Binder.getCallingPid()
1250 + ", uid=" + Binder.getCallingUid());
1251 return;
1252 }
1253
1254 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1255 pw.println();
1256
1257 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001258 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001259 pw.println("Global priority session is " + mGlobalPrioritySession);
RoboErik4646d282014-05-13 10:13:04 -07001260 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001261 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001262 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001263 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001264 }
1265 }
1266 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001267
RoboErik2e7a9162014-06-04 16:53:45 -07001268 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1269 final int uid) {
1270 String packageName = null;
1271 if (componentName != null) {
1272 // If they gave us a component name verify they own the
1273 // package
1274 packageName = componentName.getPackageName();
1275 enforcePackageName(packageName, uid);
1276 }
1277 // Check that they can make calls on behalf of the user and
1278 // get the final user id
1279 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1280 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1281 // Check if they have the permissions or their component is
1282 // enabled for the user they're calling from.
1283 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1284 return resolvedUserId;
1285 }
1286
Jaewan Kim50269362016-12-23 11:22:02 +09001287 private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001288 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1289 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001290
RoboErik9c785402014-11-11 16:52:26 -08001291 boolean preferSuggestedStream = false;
1292 if (isValidLocalStreamType(suggestedStream)
1293 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1294 preferSuggestedStream = true;
1295 }
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001296 if (DEBUG) {
1297 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1298 + flags + ", suggestedStream=" + suggestedStream
1299 + ", preferSuggestedStream=" + preferSuggestedStream);
1300 }
RoboErik9c785402014-11-11 16:52:26 -08001301 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001302 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1303 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001304 if (DEBUG) {
1305 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001306 }
RoboErikb7c014c2014-07-22 15:58:22 -07001307 return;
RoboErik3c45c292014-07-08 16:47:31 -07001308 }
Shibin George19e84042016-06-14 20:42:13 +05301309
1310 // Execute mAudioService.adjustSuggestedStreamVolume() on
1311 // handler thread of MediaSessionService.
1312 // This will release the MediaSessionService.mLock sooner and avoid
1313 // a potential deadlock between MediaSessionService.mLock and
1314 // ActivityManagerService lock.
1315 mHandler.post(new Runnable() {
1316 @Override
1317 public void run() {
1318 try {
1319 String packageName = getContext().getOpPackageName();
1320 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1321 flags, packageName, TAG);
1322 } catch (RemoteException e) {
1323 Log.e(TAG, "Error adjusting default volume.", e);
1324 }
1325 }
1326 });
RoboErikb69ffd42014-05-30 14:57:59 -07001327 } else {
RoboErik0dac35a2014-08-12 15:48:49 -07001328 session.adjustVolume(direction, flags, getContext().getPackageName(),
Jaewan Kim8f729082016-06-21 12:36:26 +09001329 Process.SYSTEM_UID, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001330 }
1331 }
1332
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001333 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001334 int action = keyEvent.getAction();
1335 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1336 if (action == KeyEvent.ACTION_DOWN) {
1337 if (keyEvent.getRepeatCount() == 0) {
1338 mVoiceButtonDown = true;
1339 mVoiceButtonHandled = false;
1340 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1341 mVoiceButtonHandled = true;
1342 startVoiceInput(needWakeLock);
1343 }
1344 } else if (action == KeyEvent.ACTION_UP) {
1345 if (mVoiceButtonDown) {
1346 mVoiceButtonDown = false;
1347 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1348 // Resend the down then send this event through
1349 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim98003d32017-02-24 18:33:04 +09001350 dispatchMediaKeyEventLocked(downEvent, needWakeLock);
1351 dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001352 }
1353 }
1354 }
1355 }
1356
Jaewan Kim98003d32017-02-24 18:33:04 +09001357 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001358 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001359 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001360 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001361 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001362 }
1363 if (needWakeLock) {
1364 mKeyEventReceiver.aquireWakeLockLocked();
1365 }
Jaewan Kim50269362016-12-23 11:22:02 +09001366 // 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 -07001367 session.sendMediaButton(keyEvent,
1368 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Jaewan Kim8f729082016-06-21 12:36:26 +09001369 mKeyEventReceiver, Process.SYSTEM_UID,
Donghyun Cho1ea56832016-02-23 16:30:07 +09001370 getContext().getPackageName());
Jaewan Kima7dce192017-02-16 17:10:54 +09001371 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001372 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001373 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
1374 keyEvent,
Jaewan Kimbd16f452017-02-03 16:21:38 +09001375 new MediaSession.Token(session.getControllerBinder()));
1376 } catch (RemoteException e) {
1377 Log.w(TAG, "Failed to send callback", e);
1378 }
1379 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001380 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1381 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1382 if (needWakeLock) {
1383 mKeyEventReceiver.aquireWakeLockLocked();
1384 }
1385 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1386 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1387 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1388 try {
1389 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1390 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1391 if (DEBUG_KEY_EVENT) {
1392 Log.d(TAG, "Sending " + keyEvent
1393 + " to the last known pendingIntent " + receiver);
1394 }
1395 receiver.send(getContext(),
1396 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1397 mediaButtonIntent, mKeyEventReceiver, mHandler);
1398 if (mCurrentFullUserRecord.mCallback != null) {
1399 ComponentName componentName = mCurrentFullUserRecord
1400 .mLastMediaButtonReceiver.getIntent().getComponent();
1401 if (componentName != null) {
1402 mCurrentFullUserRecord.mCallback
1403 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1404 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001405 }
RoboErikc8f92d12015-01-05 16:48:07 -08001406 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001407 } else {
1408 ComponentName receiver =
1409 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1410 if (DEBUG_KEY_EVENT) {
1411 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1412 + receiver);
1413 }
1414 mediaButtonIntent.setComponent(receiver);
1415 getContext().sendBroadcastAsUser(mediaButtonIntent,
1416 UserHandle.of(mCurrentFullUserRecord.mRestoredMediaButtonReceiverUserId));
1417 if (mCurrentFullUserRecord.mCallback != null) {
1418 mCurrentFullUserRecord.mCallback
1419 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1420 keyEvent, receiver);
1421 }
RoboErikb214efb2014-07-24 13:20:30 -07001422 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001423 } catch (CanceledException e) {
1424 Log.i(TAG, "Error sending key event to media button receiver "
1425 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1426 } catch (RemoteException e) {
1427 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001428 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001429 } else {
Jaewan Kim8f729082016-06-21 12:36:26 +09001430 if (DEBUG) {
1431 Log.d(TAG, "Sending media key ordered broadcast");
1432 }
1433 if (needWakeLock) {
1434 mMediaEventWakeLock.acquire();
1435 }
1436 // Fallback to legacy behavior
1437 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
1438 keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1439 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
1440 if (needWakeLock) {
1441 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
1442 }
1443 // Send broadcast only to the full user.
1444 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
1445 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
RoboErik9a9d0b52014-05-20 14:53:39 -07001446 }
1447 }
1448
1449 private void startVoiceInput(boolean needWakeLock) {
1450 Intent voiceIntent = null;
1451 // select which type of search to launch:
1452 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1453 // - device locked or screen off: action is
1454 // ACTION_VOICE_SEARCH_HANDS_FREE
1455 // with EXTRA_SECURE set to true if the device is securely locked
1456 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1457 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1458 if (!isLocked && pm.isScreenOn()) {
1459 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1460 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1461 } else {
1462 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1463 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1464 isLocked && mKeyguardManager.isKeyguardSecure());
1465 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1466 }
1467 // start the search activity
1468 if (needWakeLock) {
1469 mMediaEventWakeLock.acquire();
1470 }
1471 try {
1472 if (voiceIntent != null) {
1473 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1474 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001475 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001476 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1477 }
1478 } catch (ActivityNotFoundException e) {
1479 Log.w(TAG, "No activity for search: " + e);
1480 } finally {
1481 if (needWakeLock) {
1482 mMediaEventWakeLock.release();
1483 }
1484 }
1485 }
1486
1487 private boolean isVoiceKey(int keyCode) {
1488 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
1489 }
1490
Jeff Brown38d3feb2015-03-19 18:26:30 -07001491 private boolean isUserSetupComplete() {
1492 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1493 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1494 }
1495
RoboErik9c785402014-11-11 16:52:26 -08001496 // we only handle public stream types, which are 0-5
1497 private boolean isValidLocalStreamType(int streamType) {
1498 return streamType >= AudioManager.STREAM_VOICE_CALL
1499 && streamType <= AudioManager.STREAM_NOTIFICATION;
1500 }
1501
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001502 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
1503 private KeyEvent mKeyEvent;
1504 private boolean mNeedWakeLock;
1505 private boolean mHandled;
1506
1507 private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
1508 super(mHandler);
1509 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
1510 mKeyEvent = keyEvent;
1511 mNeedWakeLock = needWakeLock;
1512 }
1513
1514 @Override
1515 public void run() {
1516 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1517 dispatchMediaKeyEvent();
1518 }
1519
1520 @Override
1521 protected void onReceiveResult(int resultCode, Bundle resultData) {
1522 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1523 mHandled = true;
1524 mHandler.removeCallbacks(this);
1525 return;
1526 }
1527 dispatchMediaKeyEvent();
1528 }
1529
1530 private void dispatchMediaKeyEvent() {
1531 if (mHandled) {
1532 return;
1533 }
1534 mHandled = true;
1535 mHandler.removeCallbacks(this);
1536 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001537 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001538 && isVoiceKey(mKeyEvent.getKeyCode())) {
1539 handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
1540 } else {
1541 dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
1542 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001543 }
1544 }
1545 }
1546
RoboErik418c10c2014-05-19 09:25:25 -07001547 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1548
RoboErikb214efb2014-07-24 13:20:30 -07001549 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1550 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001551 private final Handler mHandler;
1552 private int mRefCount = 0;
1553 private int mLastTimeoutId = 0;
1554
1555 public KeyEventWakeLockReceiver(Handler handler) {
1556 super(handler);
1557 mHandler = handler;
1558 }
1559
1560 public void onTimeout() {
1561 synchronized (mLock) {
1562 if (mRefCount == 0) {
1563 // We've already released it, so just return
1564 return;
1565 }
1566 mLastTimeoutId++;
1567 mRefCount = 0;
1568 releaseWakeLockLocked();
1569 }
1570 }
1571
1572 public void aquireWakeLockLocked() {
1573 if (mRefCount == 0) {
1574 mMediaEventWakeLock.acquire();
1575 }
1576 mRefCount++;
1577 mHandler.removeCallbacks(this);
1578 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1579
1580 }
1581
1582 @Override
1583 public void run() {
1584 onTimeout();
1585 }
1586
RoboErik8a2cfc32014-05-16 11:19:38 -07001587 @Override
1588 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001589 if (resultCode < mLastTimeoutId) {
1590 // Ignore results from calls that were before the last
1591 // timeout, just in case.
1592 return;
1593 } else {
1594 synchronized (mLock) {
1595 if (mRefCount > 0) {
1596 mRefCount--;
1597 if (mRefCount == 0) {
1598 releaseWakeLockLocked();
1599 }
1600 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001601 }
1602 }
1603 }
RoboErik418c10c2014-05-19 09:25:25 -07001604
1605 private void releaseWakeLockLocked() {
1606 mMediaEventWakeLock.release();
1607 mHandler.removeCallbacks(this);
1608 }
RoboErikb214efb2014-07-24 13:20:30 -07001609
1610 @Override
1611 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1612 String resultData, Bundle resultExtras) {
1613 onReceiveResult(resultCode, null);
1614 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001615 };
1616
1617 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1618 @Override
1619 public void onReceive(Context context, Intent intent) {
1620 if (intent == null) {
1621 return;
1622 }
1623 Bundle extras = intent.getExtras();
1624 if (extras == null) {
1625 return;
1626 }
1627 synchronized (mLock) {
1628 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1629 && mMediaEventWakeLock.isHeld()) {
1630 mMediaEventWakeLock.release();
1631 }
1632 }
1633 }
1634 };
RoboErik01fe6612014-02-13 14:19:04 -08001635 }
1636
RoboErik2e7a9162014-06-04 16:53:45 -07001637 final class MessageHandler extends Handler {
1638 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001639 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
RoboErik2e7a9162014-06-04 16:53:45 -07001640
1641 @Override
1642 public void handleMessage(Message msg) {
1643 switch (msg.what) {
1644 case MSG_SESSIONS_CHANGED:
1645 pushSessionsChanged(msg.arg1);
1646 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001647 case MSG_VOLUME_INITIAL_DOWN:
1648 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001649 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001650 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1651 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1652 // Mark that the key is already handled.
1653 user.mInitialDownVolumeKeyEvent = null;
1654 }
1655 }
1656 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001657 }
1658 }
1659
1660 public void post(int what, int arg1, int arg2) {
1661 obtainMessage(what, arg1, arg2).sendToTarget();
1662 }
1663 }
RoboErik01fe6612014-02-13 14:19:04 -08001664}