blob: c18a79fc183c175f3000867fee7075a223c6992b [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
RoboErike7880d82014-04-30 12:48:25 -070019import android.app.ActivityManager;
Julia Reynoldsb852e562017-06-06 16:14:18 -040020import android.app.INotificationManager;
RoboErik9a9d0b52014-05-20 14:53:39 -070021import android.app.KeyguardManager;
RoboErikb214efb2014-07-24 13:20:30 -070022import android.app.PendingIntent;
23import android.app.PendingIntent.CanceledException;
RoboErik9a9d0b52014-05-20 14:53:39 -070024import android.content.ActivityNotFoundException;
RoboErik8a2cfc32014-05-16 11:19:38 -070025import android.content.BroadcastReceiver;
RoboErike7880d82014-04-30 12:48:25 -070026import android.content.ComponentName;
RoboErik6f0e4dd2014-06-17 16:56:27 -070027import android.content.ContentResolver;
RoboErik01fe6612014-02-13 14:19:04 -080028import android.content.Context;
RoboErik8a2cfc32014-05-16 11:19:38 -070029import android.content.Intent;
RoboErika278ea72014-04-24 14:49:01 -070030import android.content.pm.PackageManager;
Jaewan Kimf7a77062018-01-27 01:34:24 +090031import android.content.pm.PackageManager.NameNotFoundException;
Jaewan Kima7dce192017-02-16 17:10:54 +090032import android.content.pm.UserInfo;
RoboErik7aef77b2014-08-08 15:56:54 -070033import android.database.ContentObserver;
RoboErik3c45c292014-07-08 16:47:31 -070034import android.media.AudioManager;
Sungsoo Lim875e6972017-11-03 02:22:35 +000035import android.media.AudioPlaybackConfiguration;
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 Kimbd16f452017-02-03 16:21:38 +090040import android.media.session.ICallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080041import android.media.session.IOnMediaKeyListener;
Jaewan Kim50269362016-12-23 11:22:02 +090042import android.media.session.IOnVolumeKeyLongPressListener;
RoboErik07c70772014-03-20 13:33:52 -070043import android.media.session.ISession;
44import android.media.session.ISessionCallback;
45import android.media.session.ISessionManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070046import android.media.session.MediaSession;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080047import android.media.session.MediaSessionManager;
RoboErik7aef77b2014-08-08 15:56:54 -070048import android.net.Uri;
RoboErik01fe6612014-02-13 14:19:04 -080049import android.os.Binder;
RoboErik8a2cfc32014-05-16 11:19:38 -070050import android.os.Bundle;
RoboErik8ae0f342014-02-24 18:02:08 -080051import android.os.Handler;
RoboErike7880d82014-04-30 12:48:25 -070052import android.os.IBinder;
RoboErik2e7a9162014-06-04 16:53:45 -070053import android.os.Message;
RoboErik8a2cfc32014-05-16 11:19:38 -070054import android.os.PowerManager;
Jaewan Kim8f729082016-06-21 12:36:26 +090055import android.os.Process;
RoboErik01fe6612014-02-13 14:19:04 -080056import android.os.RemoteException;
RoboErik8a2cfc32014-05-16 11:19:38 -070057import android.os.ResultReceiver;
RoboErikb69ffd42014-05-30 14:57:59 -070058import android.os.ServiceManager;
RoboErike7880d82014-04-30 12:48:25 -070059import android.os.UserHandle;
Jaewan Kim8f729082016-06-21 12:36:26 +090060import android.os.UserManager;
RoboErike7880d82014-04-30 12:48:25 -070061import android.provider.Settings;
RoboErik9a9d0b52014-05-20 14:53:39 -070062import android.speech.RecognizerIntent;
RoboErik01fe6612014-02-13 14:19:04 -080063import android.text.TextUtils;
64import android.util.Log;
Jeff Brown38d3feb2015-03-19 18:26:30 -070065import android.util.Slog;
RoboErik4646d282014-05-13 10:13:04 -070066import android.util.SparseArray;
Jaewan Kima7dce192017-02-16 17:10:54 +090067import android.util.SparseIntArray;
RoboErik8a2cfc32014-05-16 11:19:38 -070068import android.view.KeyEvent;
Jaewan Kimd61a87b2017-02-17 23:14:10 +090069import android.view.ViewConfiguration;
RoboErik01fe6612014-02-13 14:19:04 -080070
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060071import com.android.internal.util.DumpUtils;
RoboErik01fe6612014-02-13 14:19:04 -080072import com.android.server.SystemService;
RoboErika278ea72014-04-24 14:49:01 -070073import com.android.server.Watchdog;
74import com.android.server.Watchdog.Monitor;
RoboErik01fe6612014-02-13 14:19:04 -080075
RoboErika278ea72014-04-24 14:49:01 -070076import java.io.FileDescriptor;
77import java.io.PrintWriter;
RoboErik01fe6612014-02-13 14:19:04 -080078import java.util.ArrayList;
RoboErike7880d82014-04-30 12:48:25 -070079import java.util.List;
RoboErik01fe6612014-02-13 14:19:04 -080080
81/**
82 * System implementation of MediaSessionManager
83 */
RoboErika278ea72014-04-24 14:49:01 -070084public class MediaSessionService extends SystemService implements Monitor {
RoboErik01fe6612014-02-13 14:19:04 -080085 private static final String TAG = "MediaSessionService";
Jaewan Kim92dea332017-02-02 11:52:08 +090086 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jaewan Kim50269362016-12-23 11:22:02 +090087 // Leave log for key event always.
88 private static final boolean DEBUG_KEY_EVENT = true;
RoboErik01fe6612014-02-13 14:19:04 -080089
RoboErik418c10c2014-05-19 09:25:25 -070090 private static final int WAKELOCK_TIMEOUT = 5000;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -080091 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
RoboErik418c10c2014-05-19 09:25:25 -070092
RoboErik01fe6612014-02-13 14:19:04 -080093 private final SessionManagerImpl mSessionManagerImpl;
94
Jaewan Kima7dce192017-02-16 17:10:54 +090095 // Keeps the full user id for each user.
96 private final SparseIntArray mFullUserIds = new SparseIntArray();
97 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
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;
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900103 private final int mLongPressTimeout;
Jaewan Kimfed49502018-03-05 17:51:12 +0900104 private final INotificationManager mNotificationManager;
RoboErik01fe6612014-02-13 14:19:04 -0800105
RoboErik9a9d0b52014-05-20 14:53:39 -0700106 private KeyguardManager mKeyguardManager;
RoboErikb69ffd42014-05-30 14:57:59 -0700107 private IAudioService mAudioService;
RoboErik6f0e4dd2014-06-17 16:56:27 -0700108 private ContentResolver mContentResolver;
RoboErik7aef77b2014-08-08 15:56:54 -0700109 private SettingsObserver mSettingsObserver;
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900110 private boolean mHasFeatureLeanback;
RoboErik9a9d0b52014-05-20 14:53:39 -0700111
Jaewan Kima7dce192017-02-16 17:10:54 +0900112 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
113 // It's always not null after the MediaSessionService is started.
114 private FullUserRecord mCurrentFullUserRecord;
115 private MediaSessionRecord mGlobalPrioritySession;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000116 private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
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();
Julia Reynoldsb852e562017-06-06 16:14:18 -0400128 mNotificationManager = INotificationManager.Stub.asInterface(
129 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
RoboErik01fe6612014-02-13 14:19:04 -0800130 }
131
132 @Override
133 public void onStart() {
134 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
RoboErika278ea72014-04-24 14:49:01 -0700135 Watchdog.getInstance().addMonitor(this);
RoboErik9a9d0b52014-05-20 14:53:39 -0700136 mKeyguardManager =
137 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
RoboErikb69ffd42014-05-30 14:57:59 -0700138 mAudioService = getAudioService();
Sungsoo Lim875e6972017-11-03 02:22:35 +0000139 mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
140 mAudioPlayerStateMonitor.registerListener(
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900141 (config, isRemoved) -> {
142 if (isRemoved || !config.isActive() || config.getPlayerType()
143 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
144 return;
Jaewan Kim92dea332017-02-02 11:52:08 +0900145 }
Sungsoo Lim2afdbc42017-11-01 13:45:59 +0900146 synchronized (mLock) {
147 FullUserRecord user = getFullUserRecordLocked(
148 UserHandle.getUserId(config.getClientUid()));
149 if (user != null) {
150 user.mPriorityStack.updateMediaButtonSessionIfNeeded();
151 }
152 }
153 }, null /* handler */);
Sungsoo Lim875e6972017-11-03 02:22:35 +0000154 mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
RoboErik6f0e4dd2014-06-17 16:56:27 -0700155 mContentResolver = getContext().getContentResolver();
RoboErik7aef77b2014-08-08 15:56:54 -0700156 mSettingsObserver = new SettingsObserver();
157 mSettingsObserver.observe();
Jaewan Kimfdb612e2017-07-01 09:23:41 +0900158 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
159 PackageManager.FEATURE_LEANBACK);
RoboErikc8f92d12015-01-05 16:48:07 -0800160
161 updateUser();
RoboErikb69ffd42014-05-30 14:57:59 -0700162 }
163
164 private IAudioService getAudioService() {
165 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
166 return IAudioService.Stub.asInterface(b);
RoboErik07c70772014-03-20 13:33:52 -0700167 }
168
Jaewan Kima7dce192017-02-16 17:10:54 +0900169 private boolean isGlobalPriorityActiveLocked() {
170 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
171 }
172
RoboErika8f95142014-05-05 14:23:49 -0700173 public void updateSession(MediaSessionRecord record) {
RoboErike7880d82014-04-30 12:48:25 -0700174 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900175 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
Jaewan Kim101b4d52017-05-18 13:23:11 +0900176 if (user == null) {
177 Log.w(TAG, "Unknown session updated. Ignoring.");
RoboErik4646d282014-05-13 10:13:04 -0700178 return;
179 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900180 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900181 if (DEBUG_KEY_EVENT) {
182 Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
183 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900184 user.pushAddressedPlayerChangedLocked();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900185 } else {
186 if (!user.mPriorityStack.contains(record)) {
187 Log.w(TAG, "Unknown session updated. Ignoring.");
188 return;
189 }
190 user.mPriorityStack.onSessionStateChange(record);
Jaewan Kima7dce192017-02-16 17:10:54 +0900191 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900192 mHandler.postSessionsChanged(record.getUserId());
RoboErike7880d82014-04-30 12:48:25 -0700193 }
194 }
195
Jaewan Kimfa85b602017-10-10 16:49:58 +0900196 public void setGlobalPrioritySession(MediaSessionRecord record) {
197 synchronized (mLock) {
198 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
199 if (mGlobalPrioritySession != record) {
200 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
201 + " to " + record);
202 mGlobalPrioritySession = record;
203 if (user != null && user.mPriorityStack.contains(record)) {
204 // Handle the global priority session separately.
205 // Otherwise, it can be the media button session regardless of the active state
206 // because it or other system components might have been the lastly played media
207 // app.
208 user.mPriorityStack.removeSession(record);
209 }
210 }
211 }
212 }
213
Jaewan Kim101b4d52017-05-18 13:23:11 +0900214 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
Jaewan Kimda74a152017-10-03 23:58:11 +0900215 List<MediaSessionRecord> records = new ArrayList<>();
Jaewan Kim101b4d52017-05-18 13:23:11 +0900216 if (userId == UserHandle.USER_ALL) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900217 int size = mUserRecords.size();
218 for (int i = 0; i < size; i++) {
219 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
220 }
221 } else {
222 FullUserRecord user = getFullUserRecordLocked(userId);
223 if (user == null) {
224 Log.w(TAG, "getSessions failed. Unknown user " + userId);
Jaewan Kimda74a152017-10-03 23:58:11 +0900225 return records;
Jaewan Kim101b4d52017-05-18 13:23:11 +0900226 }
Jaewan Kimda74a152017-10-03 23:58:11 +0900227 records.addAll(user.mPriorityStack.getActiveSessions(userId));
Jaewan Kim101b4d52017-05-18 13:23:11 +0900228 }
229
230 // Return global priority session at the first whenever it's asked.
231 if (isGlobalPriorityActiveLocked()
232 && (userId == UserHandle.USER_ALL
233 || userId == mGlobalPrioritySession.getUserId())) {
234 records.add(0, mGlobalPrioritySession);
235 }
236 return records;
237 }
238
RoboErik9c5b7cb2015-01-15 15:09:09 -0800239 /**
Hyundo Moona055f132017-01-13 15:31:06 +0900240 * Tells the system UI that volume has changed on an active remote session.
RoboErik9c5b7cb2015-01-15 15:09:09 -0800241 */
242 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
Hyundo Moona055f132017-01-13 15:31:06 +0900243 if (mRvc == null || !session.isActive()) {
RoboErik9c5b7cb2015-01-15 15:09:09 -0800244 return;
245 }
246 try {
247 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
248 } catch (Exception e) {
249 Log.wtf(TAG, "Error sending volume change to system UI.", e);
250 }
251 }
252
Jaewan Kim92dea332017-02-02 11:52:08 +0900253 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
RoboErika8f95142014-05-05 14:23:49 -0700254 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900255 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
256 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik4646d282014-05-13 10:13:04 -0700257 Log.d(TAG, "Unknown session changed playback state. Ignoring.");
258 return;
259 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900260 user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
RoboErika8f95142014-05-05 14:23:49 -0700261 }
262 }
263
RoboErik19c95182014-06-23 15:38:48 -0700264 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
265 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900266 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
267 if (user == null || !user.mPriorityStack.contains(record)) {
RoboErik19c95182014-06-23 15:38:48 -0700268 Log.d(TAG, "Unknown session changed playback type. Ignoring.");
269 return;
270 }
271 pushRemoteVolumeUpdateLocked(record.getUserId());
272 }
273 }
274
RoboErika278ea72014-04-24 14:49:01 -0700275 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900276 public void onStartUser(int userId) {
277 if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700278 updateUser();
279 }
280
281 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900282 public void onSwitchUser(int userId) {
283 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700284 updateUser();
285 }
286
287 @Override
Jaewan Kim8f729082016-06-21 12:36:26 +0900288 public void onStopUser(int userId) {
289 if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
RoboErik4646d282014-05-13 10:13:04 -0700290 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900291 FullUserRecord user = getFullUserRecordLocked(userId);
RoboErik4646d282014-05-13 10:13:04 -0700292 if (user != null) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900293 if (user.mFullUserId == userId) {
294 user.destroySessionsForUserLocked(UserHandle.USER_ALL);
295 mUserRecords.remove(userId);
296 } else {
297 user.destroySessionsForUserLocked(userId);
298 }
RoboErik4646d282014-05-13 10:13:04 -0700299 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900300 updateUser();
RoboErik4646d282014-05-13 10:13:04 -0700301 }
302 }
303
304 @Override
RoboErika278ea72014-04-24 14:49:01 -0700305 public void monitor() {
306 synchronized (mLock) {
307 // Check for deadlock
308 }
309 }
310
RoboErik4646d282014-05-13 10:13:04 -0700311 protected void enforcePhoneStatePermission(int pid, int uid) {
312 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
313 != PackageManager.PERMISSION_GRANTED) {
314 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
315 }
316 }
317
RoboErik01fe6612014-02-13 14:19:04 -0800318 void sessionDied(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700319 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800320 destroySessionLocked(session);
321 }
322 }
323
324 void destroySession(MediaSessionRecord session) {
RoboErika278ea72014-04-24 14:49:01 -0700325 synchronized (mLock) {
RoboErik01fe6612014-02-13 14:19:04 -0800326 destroySessionLocked(session);
327 }
328 }
329
RoboErik4646d282014-05-13 10:13:04 -0700330 private void updateUser() {
331 synchronized (mLock) {
Jaewan Kim8f729082016-06-21 12:36:26 +0900332 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
Jaewan Kima7dce192017-02-16 17:10:54 +0900333 mFullUserIds.clear();
334 List<UserInfo> allUsers = manager.getUsers();
335 if (allUsers != null) {
336 for (UserInfo userInfo : allUsers) {
337 if (userInfo.isManagedProfile()) {
338 mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
339 } else {
340 mFullUserIds.put(userInfo.id, userInfo.id);
341 if (mUserRecords.get(userInfo.id) == null) {
342 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
343 }
344 }
Jaewan Kim8f729082016-06-21 12:36:26 +0900345 }
RoboErik4646d282014-05-13 10:13:04 -0700346 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900347 // Ensure that the current full user exists.
348 int currentFullUserId = ActivityManager.getCurrentUser();
349 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
350 if (mCurrentFullUserRecord == null) {
351 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
352 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
353 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
354 }
355 mFullUserIds.put(currentFullUserId, currentFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700356 }
357 }
358
RoboErik7aef77b2014-08-08 15:56:54 -0700359 private void updateActiveSessionListeners() {
360 synchronized (mLock) {
361 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
362 SessionsListenerRecord listener = mSessionsListeners.get(i);
363 try {
364 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
365 listener.mUserId);
366 } catch (SecurityException e) {
367 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
368 + " is no longer authorized. Disconnecting.");
369 mSessionsListeners.remove(i);
370 try {
371 listener.mListener
372 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
373 } catch (Exception e1) {
374 // ignore
375 }
376 }
377 }
378 }
379 }
380
RoboErik4646d282014-05-13 10:13:04 -0700381 /*
382 * When a session is removed several things need to happen.
383 * 1. We need to remove it from the relevant user.
384 * 2. We need to remove it from the priority stack.
385 * 3. We need to remove it from all sessions.
386 * 4. If this is the system priority session we need to clear it.
387 * 5. We need to unlink to death from the cb binder
388 * 6. We need to tell the session to do any final cleanup (onDestroy)
389 */
RoboErik01fe6612014-02-13 14:19:04 -0800390 private void destroySessionLocked(MediaSessionRecord session) {
Insun Kang30be970a2015-11-26 15:35:44 +0900391 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900392 Log.d(TAG, "Destroying " + session);
Insun Kang30be970a2015-11-26 15:35:44 +0900393 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900394 FullUserRecord user = getFullUserRecordLocked(session.getUserId());
Jaewan Kima7dce192017-02-16 17:10:54 +0900395 if (mGlobalPrioritySession == session) {
396 mGlobalPrioritySession = null;
Jaewan Kim92dea332017-02-02 11:52:08 +0900397 if (session.isActive() && user != null) {
398 user.pushAddressedPlayerChangedLocked();
399 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900400 } else {
401 if (user != null) {
402 user.mPriorityStack.removeSession(session);
403 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900404 }
RoboErik4646d282014-05-13 10:13:04 -0700405
406 try {
407 session.getCallback().asBinder().unlinkToDeath(session, 0);
408 } catch (Exception e) {
409 // ignore exceptions while destroying a session.
410 }
411 session.onDestroy();
Jaewan Kim92dea332017-02-02 11:52:08 +0900412 mHandler.postSessionsChanged(session.getUserId());
RoboErik01fe6612014-02-13 14:19:04 -0800413 }
414
415 private void enforcePackageName(String packageName, int uid) {
416 if (TextUtils.isEmpty(packageName)) {
417 throw new IllegalArgumentException("packageName may not be empty");
418 }
419 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
420 final int packageCount = packages.length;
421 for (int i = 0; i < packageCount; i++) {
422 if (packageName.equals(packages[i])) {
423 return;
424 }
425 }
426 throw new IllegalArgumentException("packageName is not owned by the calling process");
427 }
428
RoboErike7880d82014-04-30 12:48:25 -0700429 /**
430 * Checks a caller's authorization to register an IRemoteControlDisplay.
431 * Authorization is granted if one of the following is true:
432 * <ul>
433 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
434 * permission</li>
RoboErika5b02322014-05-07 17:05:49 -0700435 * <li>the caller's listener is one of the enabled notification listeners
436 * for the caller's user</li>
RoboErike7880d82014-04-30 12:48:25 -0700437 * </ul>
438 */
RoboErika5b02322014-05-07 17:05:49 -0700439 private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
440 int resolvedUserId) {
Hyundo Moonf84c1c02018-03-19 20:33:27 +0900441 if (isCurrentVolumeController(pid, uid)) return;
RoboErike7880d82014-04-30 12:48:25 -0700442 if (getContext()
443 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
444 != PackageManager.PERMISSION_GRANTED
RoboErika5b02322014-05-07 17:05:49 -0700445 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
446 resolvedUserId)) {
RoboErike7880d82014-04-30 12:48:25 -0700447 throw new SecurityException("Missing permission to control media.");
448 }
449 }
450
Hyundo Moonf84c1c02018-03-19 20:33:27 +0900451 private boolean isCurrentVolumeController(int pid, int uid) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -0500452 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
453 pid, uid) == PackageManager.PERMISSION_GRANTED;
John Spurlockbe19ed02015-02-22 10:57:55 -0500454 }
455
456 private void enforceSystemUiPermission(String action, int pid, int uid) {
Hyundo Moonf84c1c02018-03-19 20:33:27 +0900457 if (!isCurrentVolumeController(pid, uid)) {
RoboErik19c95182014-06-23 15:38:48 -0700458 throw new SecurityException("Only system ui may " + action);
459 }
460 }
461
RoboErika5b02322014-05-07 17:05:49 -0700462 /**
463 * This checks if the component is an enabled notification listener for the
464 * specified user. Enabled components may only operate on behalf of the user
465 * they're running as.
466 *
467 * @param compName The component that is enabled.
468 * @param userId The user id of the caller.
469 * @param forUserId The user id they're making the request on behalf of.
470 * @return True if the component is enabled, false otherwise
471 */
472 private boolean isEnabledNotificationListener(ComponentName compName, int userId,
473 int forUserId) {
474 if (userId != forUserId) {
475 // You may not access another user's content as an enabled listener.
476 return false;
477 }
RoboErik51fa6bc2014-06-20 14:59:58 -0700478 if (DEBUG) {
479 Log.d(TAG, "Checking if enabled notification listener " + compName);
480 }
RoboErike7880d82014-04-30 12:48:25 -0700481 if (compName != null) {
Julia Reynoldsb852e562017-06-06 16:14:18 -0400482 try {
483 return mNotificationManager.isNotificationListenerAccessGrantedForUser(
484 compName, userId);
485 } catch(RemoteException e) {
486 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
RoboErike7880d82014-04-30 12:48:25 -0700487 }
488 }
489 return false;
490 }
491
RoboErika5b02322014-05-07 17:05:49 -0700492 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
RoboErik4646d282014-05-13 10:13:04 -0700493 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800494 synchronized (mLock) {
RoboErika5b02322014-05-07 17:05:49 -0700495 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
RoboErik01fe6612014-02-13 14:19:04 -0800496 }
497 }
498
RoboErik4646d282014-05-13 10:13:04 -0700499 /*
500 * When a session is created the following things need to happen.
RoboErik8a2cfc32014-05-16 11:19:38 -0700501 * 1. Its callback binder needs a link to death
RoboErik4646d282014-05-13 10:13:04 -0700502 * 2. It needs to be added to all sessions.
503 * 3. It needs to be added to the priority stack.
504 * 4. It needs to be added to the relevant user record.
505 */
RoboErika5b02322014-05-07 17:05:49 -0700506 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
507 String callerPackageName, ISessionCallback cb, String tag) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900508 FullUserRecord user = getFullUserRecordLocked(userId);
Dongwon Kang8cf39c52016-07-29 13:20:39 -0700509 if (user == null) {
510 Log.wtf(TAG, "Request from invalid user: " + userId);
511 throw new RuntimeException("Session request from invalid user.");
512 }
513
RoboErika5b02322014-05-07 17:05:49 -0700514 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
Jaewan Kim92dea332017-02-02 11:52:08 +0900515 callerPackageName, cb, tag, this, mHandler.getLooper());
RoboErik01fe6612014-02-13 14:19:04 -0800516 try {
517 cb.asBinder().linkToDeath(session, 0);
518 } catch (RemoteException e) {
519 throw new RuntimeException("Media Session owner died prematurely.", e);
520 }
RoboErik4646d282014-05-13 10:13:04 -0700521
Jaewan Kim101b4d52017-05-18 13:23:11 +0900522 user.mPriorityStack.addSession(session);
Jaewan Kim92dea332017-02-02 11:52:08 +0900523 mHandler.postSessionsChanged(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700524
RoboErik01fe6612014-02-13 14:19:04 -0800525 if (DEBUG) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +0900526 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
RoboErik01fe6612014-02-13 14:19:04 -0800527 }
528 return session;
529 }
530
RoboErik2e7a9162014-06-04 16:53:45 -0700531 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
532 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
RoboErika08adb242014-11-21 18:28:18 -0800533 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
RoboErik2e7a9162014-06-04 16:53:45 -0700534 return i;
535 }
536 }
537 return -1;
538 }
539
RoboErik2e7a9162014-06-04 16:53:45 -0700540 private void pushSessionsChanged(int userId) {
541 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900542 FullUserRecord user = getFullUserRecordLocked(userId);
543 if (user == null) {
544 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
545 return;
546 }
Jaewan Kim101b4d52017-05-18 13:23:11 +0900547 List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700548 int size = records.size();
Jeff Browndba34ba2014-06-24 20:46:03 -0700549 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
RoboErik2e7a9162014-06-04 16:53:45 -0700550 for (int i = 0; i < size; i++) {
Jeff Browndba34ba2014-06-24 20:46:03 -0700551 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
RoboErik2e7a9162014-06-04 16:53:45 -0700552 }
RoboErik19c95182014-06-23 15:38:48 -0700553 pushRemoteVolumeUpdateLocked(userId);
RoboErik2e7a9162014-06-04 16:53:45 -0700554 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
555 SessionsListenerRecord record = mSessionsListeners.get(i);
556 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
557 try {
558 record.mListener.onActiveSessionsChanged(tokens);
559 } catch (RemoteException e) {
560 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
561 e);
562 mSessionsListeners.remove(i);
563 }
564 }
565 }
566 }
567 }
568
RoboErik19c95182014-06-23 15:38:48 -0700569 private void pushRemoteVolumeUpdateLocked(int userId) {
570 if (mRvc != null) {
571 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900572 FullUserRecord user = getFullUserRecordLocked(userId);
573 if (user == null) {
574 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
575 return;
576 }
577 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
RoboErik19c95182014-06-23 15:38:48 -0700578 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
579 } catch (RemoteException e) {
580 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
581 }
582 }
583 }
584
Jaewan Kim92dea332017-02-02 11:52:08 +0900585 /**
586 * Called when the media button receiver for the {@param record} is changed.
587 *
588 * @param record the media session whose media button receiver is updated.
589 */
590 public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
591 synchronized (mLock) {
592 FullUserRecord user = getFullUserRecordLocked(record.getUserId());
593 MediaSessionRecord mediaButtonSession =
594 user.mPriorityStack.getMediaButtonSession();
595 if (record == mediaButtonSession) {
596 user.rememberMediaButtonReceiverLocked(mediaButtonSession);
597 }
598 }
599 }
600
Jaewan Kim50269362016-12-23 11:22:02 +0900601 private String getCallingPackageName(int uid) {
602 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
603 if (packages != null && packages.length > 0) {
604 return packages[0];
605 }
606 return "";
607 }
608
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900609 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
Jaewan Kim7b3cae32018-06-07 10:16:22 +0900610 if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
611 return;
612 }
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900613 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900614 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
Jaewan Kimd61a87b2017-02-17 23:14:10 +0900615 } catch (RemoteException e) {
616 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
617 }
618 }
619
Jaewan Kima7dce192017-02-16 17:10:54 +0900620 private FullUserRecord getFullUserRecordLocked(int userId) {
621 int fullUserId = mFullUserIds.get(userId, -1);
622 if (fullUserId < 0) {
623 return null;
624 }
625 return mUserRecords.get(fullUserId);
626 }
627
RoboErik4646d282014-05-13 10:13:04 -0700628 /**
Jaewan Kima7dce192017-02-16 17:10:54 +0900629 * Information about a full user and its corresponding managed profiles.
630 *
631 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
632 * them when he/she presses a media/volume button. So keeping media sessions for them in one
633 * place makes more sense and increases the readability.</p>
634 * <p>The contents of this object is guarded by {@link #mLock}.
RoboErik4646d282014-05-13 10:13:04 -0700635 */
Jaewan Kim92dea332017-02-02 11:52:08 +0900636 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
Jaewan Kima7dce192017-02-16 17:10:54 +0900637 private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
638 private final int mFullUserId;
Jaewan Kim92dea332017-02-02 11:52:08 +0900639 private final MediaSessionStack mPriorityStack;
RoboErikb214efb2014-07-24 13:20:30 -0700640 private PendingIntent mLastMediaButtonReceiver;
RoboErikc8f92d12015-01-05 16:48:07 -0800641 private ComponentName mRestoredMediaButtonReceiver;
Jaewan Kima7dce192017-02-16 17:10:54 +0900642 private int mRestoredMediaButtonReceiverUserId;
RoboErik4646d282014-05-13 10:13:04 -0700643
Jaewan Kim50269362016-12-23 11:22:02 +0900644 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
645 private int mOnVolumeKeyLongPressListenerUid;
646 private KeyEvent mInitialDownVolumeKeyEvent;
647 private int mInitialDownVolumeStream;
648 private boolean mInitialDownMusicOnly;
649
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800650 private IOnMediaKeyListener mOnMediaKeyListener;
651 private int mOnMediaKeyListenerUid;
Jaewan Kima7dce192017-02-16 17:10:54 +0900652 private ICallback mCallback;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800653
Jaewan Kima7dce192017-02-16 17:10:54 +0900654 public FullUserRecord(int fullUserId) {
655 mFullUserId = fullUserId;
Sungsoo Lim875e6972017-11-03 02:22:35 +0000656 mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
Jaewan Kima7dce192017-02-16 17:10:54 +0900657 // Restore the remembered media button receiver before the boot.
658 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
659 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
660 if (mediaButtonReceiver == null) {
661 return;
662 }
663 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
664 if (tokens == null || tokens.length != 2) {
665 return;
666 }
667 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
668 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
RoboErik4646d282014-05-13 10:13:04 -0700669 }
670
Jaewan Kima7dce192017-02-16 17:10:54 +0900671 public void destroySessionsForUserLocked(int userId) {
Jaewan Kim92dea332017-02-02 11:52:08 +0900672 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
Jaewan Kima7dce192017-02-16 17:10:54 +0900673 for (MediaSessionRecord session : sessions) {
RoboErik4646d282014-05-13 10:13:04 -0700674 MediaSessionService.this.destroySessionLocked(session);
RoboErik4646d282014-05-13 10:13:04 -0700675 }
676 }
677
RoboErik4646d282014-05-13 10:13:04 -0700678 public void dumpLocked(PrintWriter pw, String prefix) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900679 pw.print(prefix + "Record for full_user=" + mFullUserId);
680 // Dump managed profile user ids associated with this user.
681 int size = mFullUserIds.size();
682 for (int i = 0; i < size; i++) {
683 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
684 && mFullUserIds.valueAt(i) == mFullUserId) {
685 pw.print(", profile_user=" + mFullUserIds.keyAt(i));
686 }
687 }
688 pw.println();
RoboErik4646d282014-05-13 10:13:04 -0700689 String indent = prefix + " ";
Jaewan Kima7dce192017-02-16 17:10:54 +0900690 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
691 pw.println(indent + "Volume key long-press listener package: " +
Jaewan Kim50269362016-12-23 11:22:02 +0900692 getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
Jaewan Kim6e2b01c2017-01-19 16:33:14 -0800693 pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
694 pw.println(indent + "Media key listener package: " +
695 getCallingPackageName(mOnMediaKeyListenerUid));
Jaewan Kima7dce192017-02-16 17:10:54 +0900696 pw.println(indent + "Callback: " + mCallback);
697 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
698 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
699 mPriorityStack.dump(pw, indent);
700 }
701
Jaewan Kim92dea332017-02-02 11:52:08 +0900702 @Override
703 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
704 MediaSessionRecord newMediaButtonSession) {
705 if (DEBUG_KEY_EVENT) {
Jaewan Kim98e4aaf2017-05-12 17:06:47 +0900706 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
Jaewan Kim92dea332017-02-02 11:52:08 +0900707 }
708 synchronized (mLock) {
709 if (oldMediaButtonSession != null) {
710 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
711 }
712 if (newMediaButtonSession != null) {
713 rememberMediaButtonReceiverLocked(newMediaButtonSession);
714 mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
715 }
716 pushAddressedPlayerChangedLocked();
717 }
718 }
719
720 // Remember media button receiver and keep it in the persistent storage.
721 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900722 PendingIntent receiver = record.getMediaButtonReceiver();
Jaewan Kima7dce192017-02-16 17:10:54 +0900723 mLastMediaButtonReceiver = receiver;
Jaewan Kim92dea332017-02-02 11:52:08 +0900724 mRestoredMediaButtonReceiver = null;
725 String componentName = "";
726 if (receiver != null) {
727 ComponentName component = receiver.getIntent().getComponent();
728 if (component != null
729 && record.getPackageName().equals(component.getPackageName())) {
730 componentName = component.flattenToString();
731 }
RoboErik4646d282014-05-13 10:13:04 -0700732 }
Jaewan Kim92dea332017-02-02 11:52:08 +0900733 Settings.Secure.putStringForUser(mContentResolver,
734 Settings.System.MEDIA_BUTTON_RECEIVER,
735 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
736 mFullUserId);
RoboErik4646d282014-05-13 10:13:04 -0700737 }
RoboErikc8f92d12015-01-05 16:48:07 -0800738
Jaewan Kima7dce192017-02-16 17:10:54 +0900739 private void pushAddressedPlayerChangedLocked() {
740 if (mCallback == null) {
741 return;
RoboErikc8f92d12015-01-05 16:48:07 -0800742 }
Jaewan Kima7dce192017-02-16 17:10:54 +0900743 try {
744 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
745 if (mediaButtonSession != null) {
746 mCallback.onAddressedPlayerChangedToMediaSession(
747 new MediaSession.Token(mediaButtonSession.getControllerBinder()));
748 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
749 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
750 mCurrentFullUserRecord.mLastMediaButtonReceiver
751 .getIntent().getComponent());
752 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
753 mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
754 mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
755 }
756 } catch (RemoteException e) {
757 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
758 }
759 }
760
761 private MediaSessionRecord getMediaButtonSessionLocked() {
Jaewan Kim92dea332017-02-02 11:52:08 +0900762 return isGlobalPriorityActiveLocked()
763 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
RoboErikc8f92d12015-01-05 16:48:07 -0800764 }
RoboErik4646d282014-05-13 10:13:04 -0700765 }
766
RoboErik2e7a9162014-06-04 16:53:45 -0700767 final class SessionsListenerRecord implements IBinder.DeathRecipient {
768 private final IActiveSessionsListener mListener;
RoboErik7aef77b2014-08-08 15:56:54 -0700769 private final ComponentName mComponentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700770 private final int mUserId;
RoboErik7aef77b2014-08-08 15:56:54 -0700771 private final int mPid;
772 private final int mUid;
RoboErik2e7a9162014-06-04 16:53:45 -0700773
RoboErik7aef77b2014-08-08 15:56:54 -0700774 public SessionsListenerRecord(IActiveSessionsListener listener,
775 ComponentName componentName,
776 int userId, int pid, int uid) {
RoboErik2e7a9162014-06-04 16:53:45 -0700777 mListener = listener;
RoboErik7aef77b2014-08-08 15:56:54 -0700778 mComponentName = componentName;
RoboErik2e7a9162014-06-04 16:53:45 -0700779 mUserId = userId;
RoboErik7aef77b2014-08-08 15:56:54 -0700780 mPid = pid;
781 mUid = uid;
RoboErik2e7a9162014-06-04 16:53:45 -0700782 }
783
784 @Override
785 public void binderDied() {
786 synchronized (mLock) {
787 mSessionsListeners.remove(this);
788 }
789 }
790 }
791
RoboErik7aef77b2014-08-08 15:56:54 -0700792 final class SettingsObserver extends ContentObserver {
793 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
794 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
795
796 private SettingsObserver() {
797 super(null);
798 }
799
800 private void observe() {
801 mContentResolver.registerContentObserver(mSecureSettingsUri,
802 false, this, UserHandle.USER_ALL);
803 }
804
805 @Override
806 public void onChange(boolean selfChange, Uri uri) {
807 updateActiveSessionListeners();
808 }
809 }
810
RoboErik07c70772014-03-20 13:33:52 -0700811 class SessionManagerImpl extends ISessionManager.Stub {
RoboErik8a2cfc32014-05-16 11:19:38 -0700812 private static final String EXTRA_WAKELOCK_ACQUIRED =
813 "android.media.AudioService.WAKELOCK_ACQUIRED";
814 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
815
RoboErik9a9d0b52014-05-20 14:53:39 -0700816 private boolean mVoiceButtonDown = false;
817 private boolean mVoiceButtonHandled = false;
818
RoboErik07c70772014-03-20 13:33:52 -0700819 @Override
RoboErika5b02322014-05-07 17:05:49 -0700820 public ISession createSession(String packageName, ISessionCallback cb, String tag,
821 int userId) throws RemoteException {
RoboErik01fe6612014-02-13 14:19:04 -0800822 final int pid = Binder.getCallingPid();
823 final int uid = Binder.getCallingUid();
824 final long token = Binder.clearCallingIdentity();
825 try {
826 enforcePackageName(packageName, uid);
RoboErika5b02322014-05-07 17:05:49 -0700827 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
828 false /* allowAll */, true /* requireFull */, "createSession", packageName);
RoboErik01fe6612014-02-13 14:19:04 -0800829 if (cb == null) {
830 throw new IllegalArgumentException("Controller callback cannot be null");
831 }
RoboErika5b02322014-05-07 17:05:49 -0700832 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
833 .getSessionBinder();
RoboErike7880d82014-04-30 12:48:25 -0700834 } finally {
835 Binder.restoreCallingIdentity(token);
836 }
837 }
838
839 @Override
RoboErika5b02322014-05-07 17:05:49 -0700840 public List<IBinder> getSessions(ComponentName componentName, int userId) {
RoboErike7880d82014-04-30 12:48:25 -0700841 final int pid = Binder.getCallingPid();
842 final int uid = Binder.getCallingUid();
843 final long token = Binder.clearCallingIdentity();
844
845 try {
RoboErik2e7a9162014-06-04 16:53:45 -0700846 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
RoboErike7880d82014-04-30 12:48:25 -0700847 ArrayList<IBinder> binders = new ArrayList<IBinder>();
848 synchronized (mLock) {
Jaewan Kim101b4d52017-05-18 13:23:11 +0900849 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
850 for (MediaSessionRecord record : records) {
851 binders.add(record.getControllerBinder().asBinder());
RoboErike7880d82014-04-30 12:48:25 -0700852 }
853 }
854 return binders;
RoboErik01fe6612014-02-13 14:19:04 -0800855 } finally {
856 Binder.restoreCallingIdentity(token);
857 }
858 }
RoboErika278ea72014-04-24 14:49:01 -0700859
RoboErik2e7a9162014-06-04 16:53:45 -0700860 @Override
861 public void addSessionsListener(IActiveSessionsListener listener,
862 ComponentName componentName, int userId) throws RemoteException {
863 final int pid = Binder.getCallingPid();
864 final int uid = Binder.getCallingUid();
865 final long token = Binder.clearCallingIdentity();
866
867 try {
868 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
869 synchronized (mLock) {
870 int index = findIndexOfSessionsListenerLocked(listener);
871 if (index != -1) {
872 Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
873 return;
874 }
875 SessionsListenerRecord record = new SessionsListenerRecord(listener,
RoboErik7aef77b2014-08-08 15:56:54 -0700876 componentName, resolvedUserId, pid, uid);
RoboErik2e7a9162014-06-04 16:53:45 -0700877 try {
878 listener.asBinder().linkToDeath(record, 0);
879 } catch (RemoteException e) {
880 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
881 return;
882 }
883 mSessionsListeners.add(record);
884 }
885 } finally {
886 Binder.restoreCallingIdentity(token);
887 }
888 }
889
890 @Override
891 public void removeSessionsListener(IActiveSessionsListener listener)
892 throws RemoteException {
893 synchronized (mLock) {
894 int index = findIndexOfSessionsListenerLocked(listener);
895 if (index != -1) {
896 SessionsListenerRecord record = mSessionsListeners.remove(index);
897 try {
898 record.mListener.asBinder().unlinkToDeath(record, 0);
899 } catch (Exception e) {
900 // ignore exceptions, the record is being removed
901 }
902 }
903 }
904 }
905
RoboErik8a2cfc32014-05-16 11:19:38 -0700906 /**
907 * Handles the dispatching of the media button events to one of the
908 * registered listeners, or if there was none, broadcast an
909 * ACTION_MEDIA_BUTTON intent to the rest of the system.
910 *
Jaewan Kim77748b62018-05-03 19:43:33 +0900911 * @param packageName The caller package
912 * @param asSystemService {@code true} if the event sent to the session as if it was come
913 * from the system service instead of the app process. This helps sessions to
914 * distinguish between the key injection by the app and key events from the
915 * hardware devices. Should be used only when the volume key events aren't handled
916 * by foreground activity. {@code false} otherwise to tell session about the real
917 * caller.
RoboErik8a2cfc32014-05-16 11:19:38 -0700918 * @param keyEvent a non-null KeyEvent whose key code is one of the
919 * supported media buttons
920 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
921 * while this key event is dispatched.
922 */
923 @Override
Jaewan Kim77748b62018-05-03 19:43:33 +0900924 public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
925 KeyEvent keyEvent, boolean needWakeLock) {
RoboErik8a2cfc32014-05-16 11:19:38 -0700926 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
927 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
928 return;
929 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700930
RoboErik8a2cfc32014-05-16 11:19:38 -0700931 final int pid = Binder.getCallingPid();
932 final int uid = Binder.getCallingUid();
933 final long token = Binder.clearCallingIdentity();
RoboErik8a2cfc32014-05-16 11:19:38 -0700934 try {
Jeff Brown221a8272015-03-23 13:53:09 -0700935 if (DEBUG) {
Jaewan Kim77748b62018-05-03 19:43:33 +0900936 Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid
937 + ", uid=" + uid + ", asSystem=" + asSystemService + ", event="
Jeff Brown221a8272015-03-23 13:53:09 -0700938 + keyEvent);
939 }
Jeff Brown38d3feb2015-03-19 18:26:30 -0700940 if (!isUserSetupComplete()) {
941 // Global media key handling can have the side-effect of starting new
942 // activities which is undesirable while setup is in progress.
943 Slog.i(TAG, "Not dispatching media key event because user "
944 + "setup is in progress.");
945 return;
946 }
947
RoboErik8a2cfc32014-05-16 11:19:38 -0700948 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900949 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +0900950 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
951 // Prevent dispatching key event through reflection while the global
952 // priority session is active.
953 Slog.i(TAG, "Only the system can dispatch media key event "
954 + "to the global priority session.");
955 return;
956 }
Jaewan Kim98003d32017-02-24 18:33:04 +0900957 if (!isGlobalPriorityActive) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900958 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
Jaewan Kim98003d32017-02-24 18:33:04 +0900959 if (DEBUG_KEY_EVENT) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900960 Log.d(TAG, "Send " + keyEvent + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900961 }
962 try {
Jaewan Kima7dce192017-02-16 17:10:54 +0900963 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
Jaewan Kim77748b62018-05-03 19:43:33 +0900964 new MediaKeyListenerResultReceiver(packageName, pid, uid,
965 asSystemService, keyEvent, needWakeLock));
Jaewan Kim98003d32017-02-24 18:33:04 +0900966 return;
967 } catch (RemoteException e) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900968 Log.w(TAG, "Failed to send " + keyEvent
969 + " to the media key listener");
Jaewan Kim98003d32017-02-24 18:33:04 +0900970 }
971 }
972 }
Jaewan Kim51255012017-02-24 16:19:14 +0900973 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
Jaewan Kim77748b62018-05-03 19:43:33 +0900974 handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
975 needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700976 } else {
Jaewan Kim77748b62018-05-03 19:43:33 +0900977 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
978 keyEvent, needWakeLock);
RoboErik8a2cfc32014-05-16 11:19:38 -0700979 }
980 }
981 } finally {
982 Binder.restoreCallingIdentity(token);
983 }
984 }
985
RoboErika278ea72014-04-24 14:49:01 -0700986 @Override
Jaewan Kimbd16f452017-02-03 16:21:38 +0900987 public void setCallback(ICallback callback) {
988 final int pid = Binder.getCallingPid();
989 final int uid = Binder.getCallingUid();
990 final long token = Binder.clearCallingIdentity();
991 try {
Amith Yamasanie259ad22017-04-24 11:30:19 -0700992 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
Jaewan Kimbd16f452017-02-03 16:21:38 +0900993 throw new SecurityException("Only Bluetooth service processes can set"
994 + " Callback");
995 }
996 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +0900997 int userId = UserHandle.getUserId(uid);
998 FullUserRecord user = getFullUserRecordLocked(userId);
999 if (user == null || user.mFullUserId != userId) {
1000 Log.w(TAG, "Only the full user can set the callback"
1001 + ", userId=" + userId);
1002 return;
1003 }
1004 user.mCallback = callback;
1005 Log.d(TAG, "The callback " + user.mCallback
Jaewan Kimbd16f452017-02-03 16:21:38 +09001006 + " is set by " + getCallingPackageName(uid));
Jaewan Kima7dce192017-02-16 17:10:54 +09001007 if (user.mCallback == null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001008 return;
1009 }
1010 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001011 user.mCallback.asBinder().linkToDeath(
Jaewan Kimbd16f452017-02-03 16:21:38 +09001012 new IBinder.DeathRecipient() {
1013 @Override
1014 public void binderDied() {
1015 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001016 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001017 }
1018 }
1019 }, 0);
Jaewan Kima7dce192017-02-16 17:10:54 +09001020 user.pushAddressedPlayerChangedLocked();
Jaewan Kimbd16f452017-02-03 16:21:38 +09001021 } catch (RemoteException e) {
1022 Log.w(TAG, "Failed to set callback", e);
Jaewan Kima7dce192017-02-16 17:10:54 +09001023 user.mCallback = null;
Jaewan Kimbd16f452017-02-03 16:21:38 +09001024 }
1025 }
1026 } finally {
1027 Binder.restoreCallingIdentity(token);
1028 }
1029 }
1030
1031 @Override
Jaewan Kim50269362016-12-23 11:22:02 +09001032 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
1033 final int pid = Binder.getCallingPid();
1034 final int uid = Binder.getCallingUid();
1035 final long token = Binder.clearCallingIdentity();
1036 try {
1037 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
1038 if (getContext().checkPermission(
1039 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
1040 != PackageManager.PERMISSION_GRANTED) {
1041 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
1042 " permission.");
1043 }
1044
1045 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001046 int userId = UserHandle.getUserId(uid);
1047 FullUserRecord user = getFullUserRecordLocked(userId);
1048 if (user == null || user.mFullUserId != userId) {
1049 Log.w(TAG, "Only the full user can set the volume key long-press listener"
1050 + ", userId=" + userId);
1051 return;
1052 }
Jaewan Kim50269362016-12-23 11:22:02 +09001053 if (user.mOnVolumeKeyLongPressListener != null &&
1054 user.mOnVolumeKeyLongPressListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001055 Log.w(TAG, "The volume key long-press listener cannot be reset"
1056 + " by another app , mOnVolumeKeyLongPressListener="
1057 + user.mOnVolumeKeyLongPressListenerUid
1058 + ", uid=" + uid);
Jaewan Kim50269362016-12-23 11:22:02 +09001059 return;
1060 }
1061
1062 user.mOnVolumeKeyLongPressListener = listener;
1063 user.mOnVolumeKeyLongPressListenerUid = uid;
1064
Jaewan Kima7dce192017-02-16 17:10:54 +09001065 Log.d(TAG, "The volume key long-press listener "
Jaewan Kim50269362016-12-23 11:22:02 +09001066 + listener + " is set by " + getCallingPackageName(uid));
1067
1068 if (user.mOnVolumeKeyLongPressListener != null) {
1069 try {
1070 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
1071 new IBinder.DeathRecipient() {
1072 @Override
1073 public void binderDied() {
1074 synchronized (mLock) {
1075 user.mOnVolumeKeyLongPressListener = null;
1076 }
1077 }
1078 }, 0);
1079 } catch (RemoteException e) {
1080 Log.w(TAG, "Failed to set death recipient "
1081 + user.mOnVolumeKeyLongPressListener);
1082 user.mOnVolumeKeyLongPressListener = null;
1083 }
1084 }
1085 }
1086 } finally {
1087 Binder.restoreCallingIdentity(token);
1088 }
1089 }
1090
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001091 @Override
1092 public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
1093 final int pid = Binder.getCallingPid();
1094 final int uid = Binder.getCallingUid();
1095 final long token = Binder.clearCallingIdentity();
1096 try {
1097 // Enforce SET_MEDIA_KEY_LISTENER permission.
1098 if (getContext().checkPermission(
1099 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
1100 != PackageManager.PERMISSION_GRANTED) {
1101 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
1102 " permission.");
1103 }
1104
1105 synchronized (mLock) {
1106 int userId = UserHandle.getUserId(uid);
Jaewan Kima7dce192017-02-16 17:10:54 +09001107 FullUserRecord user = getFullUserRecordLocked(userId);
1108 if (user == null || user.mFullUserId != userId) {
1109 Log.w(TAG, "Only the full user can set the media key listener"
1110 + ", userId=" + userId);
1111 return;
1112 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001113 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001114 Log.w(TAG, "The media key listener cannot be reset by another app. "
1115 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
1116 + ", uid=" + uid);
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001117 return;
1118 }
1119
1120 user.mOnMediaKeyListener = listener;
1121 user.mOnMediaKeyListenerUid = uid;
1122
Jaewan Kima7dce192017-02-16 17:10:54 +09001123 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001124 + " is set by " + getCallingPackageName(uid));
1125
1126 if (user.mOnMediaKeyListener != null) {
1127 try {
1128 user.mOnMediaKeyListener.asBinder().linkToDeath(
1129 new IBinder.DeathRecipient() {
1130 @Override
1131 public void binderDied() {
1132 synchronized (mLock) {
1133 user.mOnMediaKeyListener = null;
1134 }
1135 }
1136 }, 0);
1137 } catch (RemoteException e) {
1138 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
1139 user.mOnMediaKeyListener = null;
1140 }
1141 }
1142 }
1143 } finally {
1144 Binder.restoreCallingIdentity(token);
1145 }
1146 }
1147
Jaewan Kim50269362016-12-23 11:22:02 +09001148 /**
1149 * Handles the dispatching of the volume button events to one of the
1150 * registered listeners. If there's a volume key long-press listener and
1151 * there's no active global priority session, long-pressess will be sent to the
1152 * long-press listener instead of adjusting volume.
1153 *
Jaewan Kim77748b62018-05-03 19:43:33 +09001154 * @param packageName The caller package.
1155 * @param asSystemService {@code true} if the event sent to the session as if it was come
1156 * from the system service instead of the app process. This helps sessions to
1157 * distinguish between the key injection by the app and key events from the
1158 * hardware devices. Should be used only when the volume key events aren't handled
1159 * by foreground activity. {@code false} otherwise to tell session about the real
1160 * caller.
Jaewan Kim50269362016-12-23 11:22:02 +09001161 * @param keyEvent a non-null KeyEvent whose key code is one of the
1162 * {@link KeyEvent#KEYCODE_VOLUME_UP},
1163 * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
1164 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
1165 * @param stream stream type to adjust volume.
1166 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
1167 */
1168 @Override
Jaewan Kim77748b62018-05-03 19:43:33 +09001169 public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService,
1170 KeyEvent keyEvent, int stream, boolean musicOnly) {
Jaewan Kim50269362016-12-23 11:22:02 +09001171 if (keyEvent == null ||
1172 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
1173 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
1174 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
1175 Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
1176 return;
1177 }
1178
1179 final int pid = Binder.getCallingPid();
1180 final int uid = Binder.getCallingUid();
1181 final long token = Binder.clearCallingIdentity();
1182
Jaewan Kimb2781e72017-03-02 09:57:09 +09001183 if (DEBUG_KEY_EVENT) {
Jaewan Kim77748b62018-05-03 19:43:33 +09001184 Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid="
1185 + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001186 }
1187
1188 try {
1189 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001190 if (isGlobalPriorityActiveLocked()
1191 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
Jaewan Kim77748b62018-05-03 19:43:33 +09001192 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
1193 keyEvent, stream, musicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001194 } else {
1195 // TODO: Consider the case when both volume up and down keys are pressed
1196 // at the same time.
1197 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
1198 if (keyEvent.getRepeatCount() == 0) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001199 // Keeps the copy of the KeyEvent because it can be reused.
Jaewan Kima7dce192017-02-16 17:10:54 +09001200 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
1201 KeyEvent.obtain(keyEvent);
1202 mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
1203 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001204 mHandler.sendMessageDelayed(
1205 mHandler.obtainMessage(
Jaewan Kima7dce192017-02-16 17:10:54 +09001206 MessageHandler.MSG_VOLUME_INITIAL_DOWN,
1207 mCurrentFullUserRecord.mFullUserId, 0),
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001208 mLongPressTimeout);
Jaewan Kim50269362016-12-23 11:22:02 +09001209 }
1210 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001211 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001212 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001213 dispatchVolumeKeyLongPressLocked(
Jaewan Kima7dce192017-02-16 17:10:54 +09001214 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
Jaewan Kim50269362016-12-23 11:22:02 +09001215 // Mark that the key is already handled.
Jaewan Kima7dce192017-02-16 17:10:54 +09001216 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
Jaewan Kim50269362016-12-23 11:22:02 +09001217 }
1218 dispatchVolumeKeyLongPressLocked(keyEvent);
1219 }
1220 } else { // if up
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001221 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
Jaewan Kima7dce192017-02-16 17:10:54 +09001222 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
1223 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
1224 .getDownTime() == keyEvent.getDownTime()) {
Jaewan Kim50269362016-12-23 11:22:02 +09001225 // Short-press. Should change volume.
Jaewan Kim77748b62018-05-03 19:43:33 +09001226 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
Jaewan Kima7dce192017-02-16 17:10:54 +09001227 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
1228 mCurrentFullUserRecord.mInitialDownVolumeStream,
1229 mCurrentFullUserRecord.mInitialDownMusicOnly);
Jaewan Kim77748b62018-05-03 19:43:33 +09001230 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
1231 keyEvent, stream, musicOnly);
Jaewan Kim50269362016-12-23 11:22:02 +09001232 } else {
1233 dispatchVolumeKeyLongPressLocked(keyEvent);
1234 }
1235 }
1236 }
1237 }
1238 } finally {
1239 Binder.restoreCallingIdentity(token);
1240 }
1241 }
1242
Jaewan Kim77748b62018-05-03 19:43:33 +09001243 private void dispatchVolumeKeyEventLocked(String packageName, int pid, int uid,
1244 boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
Jaewan Kim50269362016-12-23 11:22:02 +09001245 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
1246 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
1247 int direction = 0;
1248 boolean isMute = false;
1249 switch (keyEvent.getKeyCode()) {
1250 case KeyEvent.KEYCODE_VOLUME_UP:
1251 direction = AudioManager.ADJUST_RAISE;
1252 break;
1253 case KeyEvent.KEYCODE_VOLUME_DOWN:
1254 direction = AudioManager.ADJUST_LOWER;
1255 break;
1256 case KeyEvent.KEYCODE_VOLUME_MUTE:
1257 isMute = true;
1258 break;
1259 }
1260 if (down || up) {
1261 int flags = AudioManager.FLAG_FROM_KEY;
1262 if (musicOnly) {
1263 // This flag is used when the screen is off to only affect active media.
1264 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
1265 } else {
1266 // These flags are consistent with the home screen
1267 if (up) {
1268 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
1269 } else {
1270 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
1271 }
1272 }
1273 if (direction != 0) {
1274 // If this is action up we want to send a beep for non-music events
1275 if (up) {
1276 direction = 0;
1277 }
Jaewan Kim77748b62018-05-03 19:43:33 +09001278 dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
1279 direction, flags);
Jaewan Kim50269362016-12-23 11:22:02 +09001280 } else if (isMute) {
1281 if (down && keyEvent.getRepeatCount() == 0) {
Jaewan Kim77748b62018-05-03 19:43:33 +09001282 dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
1283 AudioManager.ADJUST_TOGGLE_MUTE, flags);
Jaewan Kim50269362016-12-23 11:22:02 +09001284 }
1285 }
1286 }
1287 }
1288
1289 @Override
Jaewan Kim77748b62018-05-03 19:43:33 +09001290 public void dispatchAdjustVolume(String packageName, int suggestedStream, int delta,
1291 int flags) {
1292 final int pid = Binder.getCallingPid();
1293 final int uid = Binder.getCallingUid();
RoboErikb69ffd42014-05-30 14:57:59 -07001294 final long token = Binder.clearCallingIdentity();
1295 try {
1296 synchronized (mLock) {
Jaewan Kim77748b62018-05-03 19:43:33 +09001297 dispatchAdjustVolumeLocked(packageName, pid, uid, false,
1298 suggestedStream, delta, flags);
RoboErikb69ffd42014-05-30 14:57:59 -07001299 }
1300 } finally {
1301 Binder.restoreCallingIdentity(token);
1302 }
1303 }
1304
1305 @Override
RoboErik19c95182014-06-23 15:38:48 -07001306 public void setRemoteVolumeController(IRemoteVolumeController rvc) {
1307 final int pid = Binder.getCallingPid();
1308 final int uid = Binder.getCallingUid();
1309 final long token = Binder.clearCallingIdentity();
1310 try {
John Spurlockeb69e242015-02-17 17:15:04 -05001311 enforceSystemUiPermission("listen for volume changes", pid, uid);
RoboErik19c95182014-06-23 15:38:48 -07001312 mRvc = rvc;
1313 } finally {
1314 Binder.restoreCallingIdentity(token);
1315 }
1316 }
1317
1318 @Override
RoboErikde9ba392014-09-26 12:51:01 -07001319 public boolean isGlobalPriorityActive() {
Jaewan Kim51255012017-02-24 16:19:14 +09001320 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001321 return isGlobalPriorityActiveLocked();
Jaewan Kim51255012017-02-24 16:19:14 +09001322 }
RoboErikde9ba392014-09-26 12:51:01 -07001323 }
1324
1325 @Override
RoboErika278ea72014-04-24 14:49:01 -07001326 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001327 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
RoboErika278ea72014-04-24 14:49:01 -07001328
1329 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
1330 pw.println();
1331
1332 synchronized (mLock) {
RoboErika08adb242014-11-21 18:28:18 -08001333 pw.println(mSessionsListeners.size() + " sessions listeners.");
Jaewan Kima7dce192017-02-16 17:10:54 +09001334 pw.println("Global priority session is " + mGlobalPrioritySession);
Jaewan Kim101b4d52017-05-18 13:23:11 +09001335 if (mGlobalPrioritySession != null) {
1336 mGlobalPrioritySession.dump(pw, " ");
1337 }
RoboErik4646d282014-05-13 10:13:04 -07001338 pw.println("User Records:");
Jaewan Kime0ca3f32017-02-16 15:52:39 +09001339 int count = mUserRecords.size();
RoboErika278ea72014-04-24 14:49:01 -07001340 for (int i = 0; i < count; i++) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001341 mUserRecords.valueAt(i).dumpLocked(pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001342 }
Sungsoo Lim875e6972017-11-03 02:22:35 +00001343 mAudioPlayerStateMonitor.dump(getContext(), pw, "");
RoboErika278ea72014-04-24 14:49:01 -07001344 }
1345 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001346
Jaewan Kim7e9e4d92018-02-13 22:20:41 +09001347 /**
Jaewan Kimfed49502018-03-05 17:51:12 +09001348 * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
1349 * permission or an enabled notification listener)
1350 *
Hyundo Moonf84c1c02018-03-19 20:33:27 +09001351 * @param controllerPackageName package name of the controller app
1352 * @param controllerPid pid of the controller app
1353 * @param controllerUid uid of the controller app
Jaewan Kimfed49502018-03-05 17:51:12 +09001354 */
1355 @Override
Hyundo Moonf84c1c02018-03-19 20:33:27 +09001356 public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
1357 throws RemoteException {
1358 final int uid = Binder.getCallingUid();
Jaewan Kimfed49502018-03-05 17:51:12 +09001359 final long token = Binder.clearCallingIdentity();
1360 try {
Jaewan Kim03cba652018-04-04 16:25:35 +09001361 int controllerUserId = UserHandle.getUserId(controllerUid);
1362 int controllerUidFromPackageName;
1363 try {
1364 controllerUidFromPackageName = getContext().getPackageManager()
1365 .getPackageUidAsUser(controllerPackageName, controllerUserId);
1366 } catch (NameNotFoundException e) {
1367 if (DEBUG) {
1368 Log.d(TAG, "Package " + controllerPackageName + " doesn't exist");
1369 }
1370 return false;
1371 }
1372 if (controllerUidFromPackageName != controllerUid) {
1373 if (DEBUG) {
1374 Log.d(TAG, "Package name " + controllerPackageName
1375 + " doesn't match with the uid " + controllerUid);
1376 }
1377 return false;
1378 }
Hyundo Moonf84c1c02018-03-19 20:33:27 +09001379 return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
1380 controllerPid, controllerUid);
Jaewan Kimfed49502018-03-05 17:51:12 +09001381 } finally {
1382 Binder.restoreCallingIdentity(token);
1383 }
Jaewan Kimfed49502018-03-05 17:51:12 +09001384 }
1385
Hyundo Moonf84c1c02018-03-19 20:33:27 +09001386 // For MediaSession
RoboErik2e7a9162014-06-04 16:53:45 -07001387 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
1388 final int uid) {
1389 String packageName = null;
1390 if (componentName != null) {
1391 // If they gave us a component name verify they own the
1392 // package
1393 packageName = componentName.getPackageName();
1394 enforcePackageName(packageName, uid);
1395 }
1396 // Check that they can make calls on behalf of the user and
1397 // get the final user id
1398 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
1399 true /* allowAll */, true /* requireFull */, "getSessions", packageName);
1400 // Check if they have the permissions or their component is
1401 // enabled for the user they're calling from.
1402 enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
1403 return resolvedUserId;
1404 }
1405
Hyundo Moonf84c1c02018-03-19 20:33:27 +09001406 private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
1407 int pid, int uid) throws RemoteException {
1408 // Allow API calls from the System UI
1409 if (isCurrentVolumeController(pid, uid)) {
1410 return true;
1411 }
1412
1413 // Check if it's system server or has MEDIA_CONTENT_CONTROL.
1414 // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
1415 // check here.
1416 if (uid == Process.SYSTEM_UID || getContext().checkPermission(
1417 android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
1418 == PackageManager.PERMISSION_GRANTED) {
1419 return true;
1420 } else if (DEBUG) {
1421 Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
1422 }
1423
1424 // You may not access another user's content as an enabled listener.
1425 final int userId = UserHandle.getUserId(uid);
1426 if (resolvedUserId != userId) {
1427 return false;
1428 }
1429
1430 // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
1431 // String pkgName) to notification team for optimization
1432 final List<ComponentName> enabledNotificationListeners =
1433 mNotificationManager.getEnabledNotificationListeners(userId);
1434 if (enabledNotificationListeners != null) {
1435 for (int i = 0; i < enabledNotificationListeners.size(); i++) {
1436 if (TextUtils.equals(packageName,
1437 enabledNotificationListeners.get(i).getPackageName())) {
1438 return true;
1439 }
1440 }
1441 }
1442 if (DEBUG) {
1443 Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
1444 + "notification listener");
1445 }
1446 return false;
1447 }
1448
Jaewan Kim77748b62018-05-03 19:43:33 +09001449 private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid,
1450 boolean asSystemService, int suggestedStream, int direction, int flags) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001451 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
1452 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
Jaewan Kim50269362016-12-23 11:22:02 +09001453
RoboErik9c785402014-11-11 16:52:26 -08001454 boolean preferSuggestedStream = false;
1455 if (isValidLocalStreamType(suggestedStream)
1456 && AudioSystem.isStreamActive(suggestedStream, 0)) {
1457 preferSuggestedStream = true;
1458 }
Jaewan Kimb2781e72017-03-02 09:57:09 +09001459 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001460 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
1461 + flags + ", suggestedStream=" + suggestedStream
1462 + ", preferSuggestedStream=" + preferSuggestedStream);
1463 }
RoboErik9c785402014-11-11 16:52:26 -08001464 if (session == null || preferSuggestedStream) {
RoboErik94c716e2014-09-14 13:54:31 -07001465 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
1466 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
RoboErik3c45c292014-07-08 16:47:31 -07001467 if (DEBUG) {
1468 Log.d(TAG, "No active session to adjust, skipping media only volume event");
RoboErik3c45c292014-07-08 16:47:31 -07001469 }
RoboErikb7c014c2014-07-22 15:58:22 -07001470 return;
RoboErik3c45c292014-07-08 16:47:31 -07001471 }
Shibin George19e84042016-06-14 20:42:13 +05301472
1473 // Execute mAudioService.adjustSuggestedStreamVolume() on
1474 // handler thread of MediaSessionService.
1475 // This will release the MediaSessionService.mLock sooner and avoid
1476 // a potential deadlock between MediaSessionService.mLock and
1477 // ActivityManagerService lock.
1478 mHandler.post(new Runnable() {
1479 @Override
1480 public void run() {
1481 try {
1482 String packageName = getContext().getOpPackageName();
1483 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
1484 flags, packageName, TAG);
Jaewan Kim4f5e8052018-07-24 17:31:10 +09001485 } catch (RemoteException|SecurityException e) {
Shibin George19e84042016-06-14 20:42:13 +05301486 Log.e(TAG, "Error adjusting default volume.", e);
Hyundo Moon739d6c22017-09-18 17:01:48 +09001487 } catch (IllegalArgumentException e) {
1488 Log.e(TAG, "Cannot adjust volume: direction=" + direction
1489 + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
1490 e);
Shibin George19e84042016-06-14 20:42:13 +05301491 }
1492 }
1493 });
RoboErikb69ffd42014-05-30 14:57:59 -07001494 } else {
Jaewan Kim21c23e32018-05-17 16:47:31 +09001495 session.adjustVolume(packageName, pid, uid, null, asSystemService,
Jaewan Kim77748b62018-05-03 19:43:33 +09001496 direction, flags, true);
RoboErikb69ffd42014-05-30 14:57:59 -07001497 }
1498 }
1499
Jaewan Kim77748b62018-05-03 19:43:33 +09001500 private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
1501 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
RoboErik9a9d0b52014-05-20 14:53:39 -07001502 int action = keyEvent.getAction();
1503 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1504 if (action == KeyEvent.ACTION_DOWN) {
1505 if (keyEvent.getRepeatCount() == 0) {
1506 mVoiceButtonDown = true;
1507 mVoiceButtonHandled = false;
1508 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
1509 mVoiceButtonHandled = true;
1510 startVoiceInput(needWakeLock);
1511 }
1512 } else if (action == KeyEvent.ACTION_UP) {
1513 if (mVoiceButtonDown) {
1514 mVoiceButtonDown = false;
1515 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
1516 // Resend the down then send this event through
1517 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
Jaewan Kim77748b62018-05-03 19:43:33 +09001518 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
1519 downEvent, needWakeLock);
1520 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
1521 keyEvent, needWakeLock);
RoboErik9a9d0b52014-05-20 14:53:39 -07001522 }
1523 }
1524 }
1525 }
1526
Jaewan Kim77748b62018-05-03 19:43:33 +09001527 private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
1528 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001529 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
RoboErik9a9d0b52014-05-20 14:53:39 -07001530 if (session != null) {
Jaewan Kim50269362016-12-23 11:22:02 +09001531 if (DEBUG_KEY_EVENT) {
Jaewan Kim5e1476e2016-07-19 22:25:39 +09001532 Log.d(TAG, "Sending " + keyEvent + " to " + session);
RoboErik9a9d0b52014-05-20 14:53:39 -07001533 }
1534 if (needWakeLock) {
1535 mKeyEventReceiver.aquireWakeLockLocked();
1536 }
Jaewan Kim50269362016-12-23 11:22:02 +09001537 // If we don't need a wakelock use -1 as the id so we won't release it later.
Jaewan Kim77748b62018-05-03 19:43:33 +09001538 session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
RoboErik9a9d0b52014-05-20 14:53:39 -07001539 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
Hyundo Moonb1e344e2018-03-22 17:22:14 +09001540 mKeyEventReceiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001541 if (mCurrentFullUserRecord.mCallback != null) {
Jaewan Kimbd16f452017-02-03 16:21:38 +09001542 try {
Jaewan Kima7dce192017-02-16 17:10:54 +09001543 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
Jaewan Kim77748b62018-05-03 19:43:33 +09001544 keyEvent, new MediaSession.Token(session.getControllerBinder()));
Jaewan Kimbd16f452017-02-03 16:21:38 +09001545 } catch (RemoteException e) {
1546 Log.w(TAG, "Failed to send callback", e);
1547 }
1548 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001549 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
1550 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
1551 if (needWakeLock) {
1552 mKeyEventReceiver.aquireWakeLockLocked();
1553 }
1554 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
1555 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1556 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
Jaewan Kim77748b62018-05-03 19:43:33 +09001557 // TODO: Find a way to also send PID/UID in secure way.
1558 String callerPackageName =
1559 (asSystemService) ? getContext().getPackageName() : packageName;
1560 mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
Jaewan Kima7dce192017-02-16 17:10:54 +09001561 try {
1562 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
1563 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
1564 if (DEBUG_KEY_EVENT) {
1565 Log.d(TAG, "Sending " + keyEvent
Jaewan Kim92dea332017-02-02 11:52:08 +09001566 + " to the last known PendingIntent " + receiver);
Jaewan Kima7dce192017-02-16 17:10:54 +09001567 }
1568 receiver.send(getContext(),
1569 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
1570 mediaButtonIntent, mKeyEventReceiver, mHandler);
1571 if (mCurrentFullUserRecord.mCallback != null) {
1572 ComponentName componentName = mCurrentFullUserRecord
1573 .mLastMediaButtonReceiver.getIntent().getComponent();
1574 if (componentName != null) {
1575 mCurrentFullUserRecord.mCallback
1576 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1577 keyEvent, componentName);
Jaewan Kimbd16f452017-02-03 16:21:38 +09001578 }
RoboErikc8f92d12015-01-05 16:48:07 -08001579 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001580 } else {
1581 ComponentName receiver =
1582 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
1583 if (DEBUG_KEY_EVENT) {
1584 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
1585 + receiver);
1586 }
1587 mediaButtonIntent.setComponent(receiver);
1588 getContext().sendBroadcastAsUser(mediaButtonIntent,
Jaewan Kim92dea332017-02-02 11:52:08 +09001589 UserHandle.of(mCurrentFullUserRecord
1590 .mRestoredMediaButtonReceiverUserId));
Jaewan Kima7dce192017-02-16 17:10:54 +09001591 if (mCurrentFullUserRecord.mCallback != null) {
1592 mCurrentFullUserRecord.mCallback
1593 .onMediaKeyEventDispatchedToMediaButtonReceiver(
1594 keyEvent, receiver);
1595 }
RoboErikb214efb2014-07-24 13:20:30 -07001596 }
Jaewan Kima7dce192017-02-16 17:10:54 +09001597 } catch (CanceledException e) {
1598 Log.i(TAG, "Error sending key event to media button receiver "
1599 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
1600 } catch (RemoteException e) {
1601 Log.w(TAG, "Failed to send callback", e);
RoboErik9a9d0b52014-05-20 14:53:39 -07001602 }
RoboErik9a9d0b52014-05-20 14:53:39 -07001603 }
1604 }
1605
1606 private void startVoiceInput(boolean needWakeLock) {
1607 Intent voiceIntent = null;
1608 // select which type of search to launch:
1609 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
1610 // - device locked or screen off: action is
1611 // ACTION_VOICE_SEARCH_HANDS_FREE
1612 // with EXTRA_SECURE set to true if the device is securely locked
1613 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1614 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
1615 if (!isLocked && pm.isScreenOn()) {
1616 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
1617 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
1618 } else {
1619 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
1620 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
1621 isLocked && mKeyguardManager.isKeyguardSecure());
1622 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
1623 }
1624 // start the search activity
1625 if (needWakeLock) {
1626 mMediaEventWakeLock.acquire();
1627 }
1628 try {
1629 if (voiceIntent != null) {
1630 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1631 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Jaewan Kim8f729082016-06-21 12:36:26 +09001632 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
RoboErik9a9d0b52014-05-20 14:53:39 -07001633 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
1634 }
1635 } catch (ActivityNotFoundException e) {
1636 Log.w(TAG, "No activity for search: " + e);
1637 } finally {
1638 if (needWakeLock) {
1639 mMediaEventWakeLock.release();
1640 }
1641 }
1642 }
1643
1644 private boolean isVoiceKey(int keyCode) {
Jaewan Kimba18d8e2017-05-12 17:37:57 +09001645 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
Jaewan Kimfdb612e2017-07-01 09:23:41 +09001646 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
RoboErik9a9d0b52014-05-20 14:53:39 -07001647 }
1648
Jeff Brown38d3feb2015-03-19 18:26:30 -07001649 private boolean isUserSetupComplete() {
1650 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
1651 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
1652 }
1653
RoboErik9c785402014-11-11 16:52:26 -08001654 // we only handle public stream types, which are 0-5
1655 private boolean isValidLocalStreamType(int streamType) {
1656 return streamType >= AudioManager.STREAM_VOICE_CALL
1657 && streamType <= AudioManager.STREAM_NOTIFICATION;
1658 }
1659
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001660 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
Jaewan Kim77748b62018-05-03 19:43:33 +09001661 private final String mPackageName;
1662 private final int mPid;
1663 private final int mUid;
1664 private final boolean mAsSystemService;
1665 private final KeyEvent mKeyEvent;
1666 private final boolean mNeedWakeLock;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001667 private boolean mHandled;
1668
Jaewan Kim77748b62018-05-03 19:43:33 +09001669 private MediaKeyListenerResultReceiver(String packageName, int pid, int uid,
1670 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001671 super(mHandler);
1672 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
Jaewan Kim77748b62018-05-03 19:43:33 +09001673 mPackageName = packageName;
1674 mPid = pid;
1675 mUid = uid;
1676 mAsSystemService = asSystemService;
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001677 mKeyEvent = keyEvent;
1678 mNeedWakeLock = needWakeLock;
1679 }
1680
1681 @Override
1682 public void run() {
1683 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
1684 dispatchMediaKeyEvent();
1685 }
1686
1687 @Override
1688 protected void onReceiveResult(int resultCode, Bundle resultData) {
1689 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
1690 mHandled = true;
1691 mHandler.removeCallbacks(this);
1692 return;
1693 }
1694 dispatchMediaKeyEvent();
1695 }
1696
1697 private void dispatchMediaKeyEvent() {
1698 if (mHandled) {
1699 return;
1700 }
1701 mHandled = true;
1702 mHandler.removeCallbacks(this);
1703 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001704 if (!isGlobalPriorityActiveLocked()
Jaewan Kim98003d32017-02-24 18:33:04 +09001705 && isVoiceKey(mKeyEvent.getKeyCode())) {
Jaewan Kim77748b62018-05-03 19:43:33 +09001706 handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
1707 mKeyEvent, mNeedWakeLock);
Jaewan Kim98003d32017-02-24 18:33:04 +09001708 } else {
Jaewan Kim77748b62018-05-03 19:43:33 +09001709 dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
1710 mKeyEvent, mNeedWakeLock);
Jaewan Kim98003d32017-02-24 18:33:04 +09001711 }
Jaewan Kim6e2b01c2017-01-19 16:33:14 -08001712 }
1713 }
1714 }
1715
RoboErik418c10c2014-05-19 09:25:25 -07001716 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1717
RoboErikb214efb2014-07-24 13:20:30 -07001718 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1719 PendingIntent.OnFinished {
RoboErik418c10c2014-05-19 09:25:25 -07001720 private final Handler mHandler;
1721 private int mRefCount = 0;
1722 private int mLastTimeoutId = 0;
1723
1724 public KeyEventWakeLockReceiver(Handler handler) {
1725 super(handler);
1726 mHandler = handler;
1727 }
1728
1729 public void onTimeout() {
1730 synchronized (mLock) {
1731 if (mRefCount == 0) {
1732 // We've already released it, so just return
1733 return;
1734 }
1735 mLastTimeoutId++;
1736 mRefCount = 0;
1737 releaseWakeLockLocked();
1738 }
1739 }
1740
1741 public void aquireWakeLockLocked() {
1742 if (mRefCount == 0) {
1743 mMediaEventWakeLock.acquire();
1744 }
1745 mRefCount++;
1746 mHandler.removeCallbacks(this);
1747 mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1748
1749 }
1750
1751 @Override
1752 public void run() {
1753 onTimeout();
1754 }
1755
RoboErik8a2cfc32014-05-16 11:19:38 -07001756 @Override
1757 protected void onReceiveResult(int resultCode, Bundle resultData) {
RoboErik418c10c2014-05-19 09:25:25 -07001758 if (resultCode < mLastTimeoutId) {
1759 // Ignore results from calls that were before the last
1760 // timeout, just in case.
1761 return;
1762 } else {
1763 synchronized (mLock) {
1764 if (mRefCount > 0) {
1765 mRefCount--;
1766 if (mRefCount == 0) {
1767 releaseWakeLockLocked();
1768 }
1769 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001770 }
1771 }
1772 }
RoboErik418c10c2014-05-19 09:25:25 -07001773
1774 private void releaseWakeLockLocked() {
1775 mMediaEventWakeLock.release();
1776 mHandler.removeCallbacks(this);
1777 }
RoboErikb214efb2014-07-24 13:20:30 -07001778
1779 @Override
1780 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1781 String resultData, Bundle resultExtras) {
1782 onReceiveResult(resultCode, null);
1783 }
RoboErik8a2cfc32014-05-16 11:19:38 -07001784 };
1785
1786 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
1787 @Override
1788 public void onReceive(Context context, Intent intent) {
1789 if (intent == null) {
1790 return;
1791 }
1792 Bundle extras = intent.getExtras();
1793 if (extras == null) {
1794 return;
1795 }
1796 synchronized (mLock) {
1797 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
1798 && mMediaEventWakeLock.isHeld()) {
1799 mMediaEventWakeLock.release();
1800 }
1801 }
1802 }
1803 };
RoboErik01fe6612014-02-13 14:19:04 -08001804 }
1805
RoboErik2e7a9162014-06-04 16:53:45 -07001806 final class MessageHandler extends Handler {
1807 private static final int MSG_SESSIONS_CHANGED = 1;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001808 private static final int MSG_VOLUME_INITIAL_DOWN = 2;
Jaewan Kim92dea332017-02-02 11:52:08 +09001809 private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
RoboErik2e7a9162014-06-04 16:53:45 -07001810
1811 @Override
1812 public void handleMessage(Message msg) {
1813 switch (msg.what) {
1814 case MSG_SESSIONS_CHANGED:
Jaewan Kim92dea332017-02-02 11:52:08 +09001815 pushSessionsChanged((int) msg.obj);
RoboErik2e7a9162014-06-04 16:53:45 -07001816 break;
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001817 case MSG_VOLUME_INITIAL_DOWN:
1818 synchronized (mLock) {
Jaewan Kima7dce192017-02-16 17:10:54 +09001819 FullUserRecord user = mUserRecords.get((int) msg.arg1);
Jaewan Kimd61a87b2017-02-17 23:14:10 +09001820 if (user != null && user.mInitialDownVolumeKeyEvent != null) {
1821 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
1822 // Mark that the key is already handled.
1823 user.mInitialDownVolumeKeyEvent = null;
1824 }
1825 }
1826 break;
RoboErik2e7a9162014-06-04 16:53:45 -07001827 }
1828 }
1829
Jaewan Kim92dea332017-02-02 11:52:08 +09001830 public void postSessionsChanged(int userId) {
1831 // Use object instead of the arguments when posting message to remove pending requests.
1832 Integer userIdInteger = mIntegerCache.get(userId);
1833 if (userIdInteger == null) {
1834 userIdInteger = Integer.valueOf(userId);
1835 mIntegerCache.put(userId, userIdInteger);
1836 }
1837 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
1838 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
RoboErik2e7a9162014-06-04 16:53:45 -07001839 }
1840 }
RoboErik01fe6612014-02-13 14:19:04 -08001841}