1 /* 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 17 package com.android.server.media; 18 19 import static android.media.SessionToken2.TYPE_SESSION; 20 21 import android.app.ActivityManager; 22 import android.app.AppGlobals; 23 import android.app.INotificationManager; 24 import android.app.KeyguardManager; 25 import android.app.PendingIntent; 26 import android.app.PendingIntent.CanceledException; 27 import android.content.ActivityNotFoundException; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.IPackageManager; 35 import android.content.pm.PackageManager; 36 import android.content.pm.PackageManager.NameNotFoundException; 37 import android.content.pm.ResolveInfo; 38 import android.content.pm.ServiceInfo; 39 import android.content.pm.UserInfo; 40 import android.database.ContentObserver; 41 import android.media.AudioManager; 42 import android.media.AudioPlaybackConfiguration; 43 import android.media.AudioSystem; 44 import android.media.IAudioService; 45 import android.media.IRemoteVolumeController; 46 import android.media.ISessionTokensListener; 47 import android.media.MediaController2; 48 import android.media.MediaLibraryService2; 49 import android.media.MediaSessionService2; 50 import android.media.SessionToken2; 51 import android.media.session.IActiveSessionsListener; 52 import android.media.session.ICallback; 53 import android.media.session.IOnMediaKeyListener; 54 import android.media.session.IOnVolumeKeyLongPressListener; 55 import android.media.session.ISession; 56 import android.media.session.ISessionCallback; 57 import android.media.session.ISessionManager; 58 import android.media.session.MediaSession; 59 import android.media.session.MediaSessionManager; 60 import android.net.Uri; 61 import android.os.Binder; 62 import android.os.Bundle; 63 import android.os.Handler; 64 import android.os.IBinder; 65 import android.os.Message; 66 import android.os.PowerManager; 67 import android.os.Process; 68 import android.os.RemoteException; 69 import android.os.ResultReceiver; 70 import android.os.ServiceManager; 71 import android.os.UserHandle; 72 import android.os.UserManager; 73 import android.provider.Settings; 74 import android.speech.RecognizerIntent; 75 import android.text.TextUtils; 76 import android.util.ArrayMap; 77 import android.util.Log; 78 import android.util.Slog; 79 import android.util.SparseArray; 80 import android.util.SparseIntArray; 81 import android.view.KeyEvent; 82 import android.view.ViewConfiguration; 83 84 import com.android.internal.os.BackgroundThread; 85 import com.android.internal.util.DumpUtils; 86 import com.android.server.SystemService; 87 import com.android.server.Watchdog; 88 import com.android.server.Watchdog.Monitor; 89 90 import java.io.FileDescriptor; 91 import java.io.PrintWriter; 92 import java.util.ArrayList; 93 import java.util.HashSet; 94 import java.util.List; 95 import java.util.Map; 96 import java.util.Set; 97 import java.util.NoSuchElementException; 98 99 /** 100 * System implementation of MediaSessionManager 101 */ 102 public class MediaSessionService extends SystemService implements Monitor { 103 private static final String TAG = "MediaSessionService"; 104 static final boolean USE_MEDIA2_APIS = false; // TODO: Change this to true when we're ready. 105 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 106 // Leave log for key event always. 107 private static final boolean DEBUG_KEY_EVENT = true; 108 109 private static final int WAKELOCK_TIMEOUT = 5000; 110 private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; 111 112 private final SessionManagerImpl mSessionManagerImpl; 113 114 // Keeps the full user id for each user. 115 private final SparseIntArray mFullUserIds = new SparseIntArray(); 116 private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>(); 117 private final ArrayList<SessionsListenerRecord> mSessionsListeners 118 = new ArrayList<SessionsListenerRecord>(); 119 private final Object mLock = new Object(); 120 private final MessageHandler mHandler = new MessageHandler(); 121 private final PowerManager.WakeLock mMediaEventWakeLock; 122 private final int mLongPressTimeout; 123 private final INotificationManager mNotificationManager; 124 private final IPackageManager mPackageManager; 125 126 private KeyguardManager mKeyguardManager; 127 private IAudioService mAudioService; 128 private ContentResolver mContentResolver; 129 private SettingsObserver mSettingsObserver; 130 private boolean mHasFeatureLeanback; 131 132 // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) 133 // It's always not null after the MediaSessionService is started. 134 private FullUserRecord mCurrentFullUserRecord; 135 private MediaSessionRecord mGlobalPrioritySession; 136 private AudioPlayerStateMonitor mAudioPlayerStateMonitor; 137 138 // Used to notify system UI when remote volume was changed. TODO find a 139 // better way to handle this. 140 private IRemoteVolumeController mRvc; 141 142 // MediaSession2 support 143 // TODO(jaewan): Support multi-user and managed profile. (b/73597722) 144 // TODO(jaewan): Make it priority list for handling volume/media key. (b/73760382) 145 private final Map<SessionToken2, MediaController2> mSessionRecords = new ArrayMap<>(); 146 147 private final List<SessionTokensListenerRecord> mSessionTokensListeners = new ArrayList<>(); 148 149 public MediaSessionService(Context context) { 150 super(context); 151 mSessionManagerImpl = new SessionManagerImpl(); 152 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 153 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); 154 mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); 155 mNotificationManager = INotificationManager.Stub.asInterface( 156 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 157 mPackageManager = AppGlobals.getPackageManager(); 158 } 159 160 @Override 161 public void onStart() { 162 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); 163 Watchdog.getInstance().addMonitor(this); 164 mKeyguardManager = 165 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); 166 mAudioService = getAudioService(); 167 mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); 168 mAudioPlayerStateMonitor.registerListener( 169 (config, isRemoved) -> { 170 if (isRemoved || !config.isActive() || config.getPlayerType() 171 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { 172 return; 173 } 174 synchronized (mLock) { 175 FullUserRecord user = getFullUserRecordLocked( 176 UserHandle.getUserId(config.getClientUid())); 177 if (user != null) { 178 user.mPriorityStack.updateMediaButtonSessionIfNeeded(); 179 } 180 } 181 }, null /* handler */); 182 mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); 183 mContentResolver = getContext().getContentResolver(); 184 mSettingsObserver = new SettingsObserver(); 185 mSettingsObserver.observe(); 186 mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature( 187 PackageManager.FEATURE_LEANBACK); 188 189 updateUser(); 190 191 registerPackageBroadcastReceivers(); 192 // TODO(jaewan): Query per users (b/73597722) 193 buildMediaSessionService2List(); 194 } 195 196 private IAudioService getAudioService() { 197 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 198 return IAudioService.Stub.asInterface(b); 199 } 200 201 private boolean isGlobalPriorityActiveLocked() { 202 return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); 203 } 204 205 public void updateSession(MediaSessionRecord record) { 206 synchronized (mLock) { 207 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 208 if (user == null) { 209 Log.w(TAG, "Unknown session updated. Ignoring."); 210 return; 211 } 212 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 213 if (DEBUG_KEY_EVENT) { 214 Log.d(TAG, "Global priority session is updated, active=" + record.isActive()); 215 } 216 user.pushAddressedPlayerChangedLocked(); 217 } else { 218 if (!user.mPriorityStack.contains(record)) { 219 Log.w(TAG, "Unknown session updated. Ignoring."); 220 return; 221 } 222 user.mPriorityStack.onSessionStateChange(record); 223 } 224 mHandler.postSessionsChanged(record.getUserId()); 225 } 226 } 227 228 public void setGlobalPrioritySession(MediaSessionRecord record) { 229 synchronized (mLock) { 230 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 231 if (mGlobalPrioritySession != record) { 232 Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession 233 + " to " + record); 234 mGlobalPrioritySession = record; 235 if (user != null && user.mPriorityStack.contains(record)) { 236 // Handle the global priority session separately. 237 // Otherwise, it can be the media button session regardless of the active state 238 // because it or other system components might have been the lastly played media 239 // app. 240 user.mPriorityStack.removeSession(record); 241 } 242 } 243 } 244 } 245 246 private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { 247 List<MediaSessionRecord> records = new ArrayList<>(); 248 if (userId == UserHandle.USER_ALL) { 249 int size = mUserRecords.size(); 250 for (int i = 0; i < size; i++) { 251 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); 252 } 253 } else { 254 FullUserRecord user = getFullUserRecordLocked(userId); 255 if (user == null) { 256 Log.w(TAG, "getSessions failed. Unknown user " + userId); 257 return records; 258 } 259 records.addAll(user.mPriorityStack.getActiveSessions(userId)); 260 } 261 262 // Return global priority session at the first whenever it's asked. 263 if (isGlobalPriorityActiveLocked() 264 && (userId == UserHandle.USER_ALL 265 || userId == mGlobalPrioritySession.getUserId())) { 266 records.add(0, mGlobalPrioritySession); 267 } 268 return records; 269 } 270 271 /** 272 * Tells the system UI that volume has changed on an active remote session. 273 */ 274 public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { 275 if (mRvc == null || !session.isActive()) { 276 return; 277 } 278 try { 279 mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); 280 } catch (Exception e) { 281 Log.wtf(TAG, "Error sending volume change to system UI.", e); 282 } 283 } 284 285 public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { 286 synchronized (mLock) { 287 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 288 if (user == null || !user.mPriorityStack.contains(record)) { 289 Log.d(TAG, "Unknown session changed playback state. Ignoring."); 290 return; 291 } 292 user.mPriorityStack.onPlaystateChanged(record, oldState, newState); 293 } 294 } 295 296 public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { 297 synchronized (mLock) { 298 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 299 if (user == null || !user.mPriorityStack.contains(record)) { 300 Log.d(TAG, "Unknown session changed playback type. Ignoring."); 301 return; 302 } 303 pushRemoteVolumeUpdateLocked(record.getUserId()); 304 } 305 } 306 307 @Override 308 public void onStartUser(int userId) { 309 if (DEBUG) Log.d(TAG, "onStartUser: " + userId); 310 updateUser(); 311 } 312 313 @Override 314 public void onSwitchUser(int userId) { 315 if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId); 316 updateUser(); 317 } 318 319 @Override 320 public void onStopUser(int userId) { 321 if (DEBUG) Log.d(TAG, "onStopUser: " + userId); 322 synchronized (mLock) { 323 FullUserRecord user = getFullUserRecordLocked(userId); 324 if (user != null) { 325 if (user.mFullUserId == userId) { 326 user.destroySessionsForUserLocked(UserHandle.USER_ALL); 327 mUserRecords.remove(userId); 328 } else { 329 user.destroySessionsForUserLocked(userId); 330 } 331 } 332 updateUser(); 333 } 334 } 335 336 @Override 337 public void monitor() { 338 synchronized (mLock) { 339 // Check for deadlock 340 } 341 } 342 343 protected void enforcePhoneStatePermission(int pid, int uid) { 344 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) 345 != PackageManager.PERMISSION_GRANTED) { 346 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); 347 } 348 } 349 350 void sessionDied(MediaSessionRecord session) { 351 synchronized (mLock) { 352 destroySessionLocked(session); 353 } 354 } 355 356 void destroySession(MediaSessionRecord session) { 357 synchronized (mLock) { 358 destroySessionLocked(session); 359 } 360 } 361 362 private void updateUser() { 363 synchronized (mLock) { 364 UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); 365 mFullUserIds.clear(); 366 List<UserInfo> allUsers = manager.getUsers(); 367 if (allUsers != null) { 368 for (UserInfo userInfo : allUsers) { 369 if (userInfo.isManagedProfile()) { 370 mFullUserIds.put(userInfo.id, userInfo.profileGroupId); 371 } else { 372 mFullUserIds.put(userInfo.id, userInfo.id); 373 if (mUserRecords.get(userInfo.id) == null) { 374 mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id)); 375 } 376 } 377 } 378 } 379 // Ensure that the current full user exists. 380 int currentFullUserId = ActivityManager.getCurrentUser(); 381 mCurrentFullUserRecord = mUserRecords.get(currentFullUserId); 382 if (mCurrentFullUserRecord == null) { 383 Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); 384 mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); 385 mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); 386 } 387 mFullUserIds.put(currentFullUserId, currentFullUserId); 388 } 389 } 390 391 private void updateActiveSessionListeners() { 392 synchronized (mLock) { 393 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 394 SessionsListenerRecord listener = mSessionsListeners.get(i); 395 try { 396 enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid, 397 listener.mUserId); 398 } catch (SecurityException e) { 399 Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName 400 + " is no longer authorized. Disconnecting."); 401 mSessionsListeners.remove(i); 402 try { 403 listener.mListener 404 .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); 405 } catch (Exception e1) { 406 // ignore 407 } 408 } 409 } 410 } 411 } 412 413 /* 414 * When a session is removed several things need to happen. 415 * 1. We need to remove it from the relevant user. 416 * 2. We need to remove it from the priority stack. 417 * 3. We need to remove it from all sessions. 418 * 4. If this is the system priority session we need to clear it. 419 * 5. We need to unlink to death from the cb binder 420 * 6. We need to tell the session to do any final cleanup (onDestroy) 421 */ 422 private void destroySessionLocked(MediaSessionRecord session) { 423 if (DEBUG) { 424 Log.d(TAG, "Destroying " + session); 425 } 426 FullUserRecord user = getFullUserRecordLocked(session.getUserId()); 427 if (mGlobalPrioritySession == session) { 428 mGlobalPrioritySession = null; 429 if (session.isActive() && user != null) { 430 user.pushAddressedPlayerChangedLocked(); 431 } 432 } else { 433 if (user != null) { 434 user.mPriorityStack.removeSession(session); 435 } 436 } 437 438 try { 439 session.getCallback().asBinder().unlinkToDeath(session, 0); 440 } catch (Exception e) { 441 // ignore exceptions while destroying a session. 442 } 443 session.onDestroy(); 444 mHandler.postSessionsChanged(session.getUserId()); 445 } 446 447 private void registerPackageBroadcastReceivers() { 448 // TODO(jaewan): Only consider changed packages when building session service list 449 // when we make this multi-user aware. At that time, 450 // use PackageMonitor.getChangingUserId() to know which user has changed. 451 // (b/73597722) 452 IntentFilter filter = new IntentFilter(); 453 filter.addDataScheme("package"); 454 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 455 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 456 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 457 filter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); 458 filter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); 459 filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 460 filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 461 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 462 463 getContext().registerReceiverAsUser(new BroadcastReceiver() { 464 @Override 465 public void onReceive(Context context, Intent intent) { 466 final int changeUserId = intent.getIntExtra( 467 Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 468 if (changeUserId == UserHandle.USER_NULL) { 469 Log.w(TAG, "Intent broadcast does not contain user handle: "+ intent); 470 return; 471 } 472 // Check if the package is replacing (i.e. reinstalling) 473 final boolean isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 474 // TODO(jaewan): Add multi-user support with this. (b/73597722) 475 // final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); 476 477 if (DEBUG) { 478 Log.d(TAG, "Received change in packages, intent=" + intent); 479 } 480 switch (intent.getAction()) { 481 case Intent.ACTION_PACKAGE_ADDED: 482 case Intent.ACTION_PACKAGE_REMOVED: 483 case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: 484 case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: 485 if (isReplacing) { 486 // Ignore if the package(s) are replacing. In that case, followings will 487 // happen in order. 488 // 1. ACTION_PACKAGE_REMOVED with isReplacing=true 489 // 2. ACTION_PACKAGE_ADDED with isReplacing=true 490 // 3. ACTION_PACKAGE_REPLACED 491 // (Note that ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and 492 // ACTION_EXTERNAL_APPLICATIONS_AVAILABLE will be also called with 493 // isReplacing=true for both ASEC hosted packages and packages in 494 // external storage) 495 // Since we only want to update session service list once, ignore 496 // actions above when replacing. 497 // Replacing will be handled only once with the ACTION_PACKAGE_REPLACED. 498 break; 499 } 500 // pass-through 501 case Intent.ACTION_PACKAGE_CHANGED: 502 case Intent.ACTION_PACKAGES_SUSPENDED: 503 case Intent.ACTION_PACKAGES_UNSUSPENDED: 504 case Intent.ACTION_PACKAGE_REPLACED: 505 buildMediaSessionService2List(); 506 } 507 } 508 }, UserHandle.ALL, filter, null, BackgroundThread.getHandler()); 509 } 510 511 private void buildMediaSessionService2List() { 512 if (!USE_MEDIA2_APIS) { 513 return; 514 } 515 if (DEBUG) { 516 Log.d(TAG, "buildMediaSessionService2List"); 517 } 518 // TODO(jaewan): Also query for managed profile users. (b/73597722) 519 PackageManager manager = getContext().getPackageManager(); 520 List<ResolveInfo> services = new ArrayList<>(); 521 // If multiple actions are declared for a service, browser gets higher priority. 522 List<ResolveInfo> libraryServices = manager.queryIntentServices( 523 new Intent(MediaLibraryService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA); 524 if (libraryServices != null) { 525 services.addAll(libraryServices); 526 } 527 List<ResolveInfo> sessionServices = manager.queryIntentServices( 528 new Intent(MediaSessionService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA); 529 if (sessionServices != null) { 530 services.addAll(sessionServices); 531 } 532 synchronized (mLock) { 533 // List to keep the session services that need be removed because they don't exist 534 // in the 'services' above. 535 boolean notifySessionTokensUpdated = false; 536 Set<SessionToken2> sessionTokensToRemove = new HashSet<>(); 537 for (SessionToken2 token : mSessionRecords.keySet()) { 538 if (token.getType() != TYPE_SESSION) { 539 sessionTokensToRemove.add(token); 540 } 541 } 542 543 for (int i = 0; i < services.size(); i++) { 544 if (services.get(i) == null || services.get(i).serviceInfo == null) { 545 continue; 546 } 547 ServiceInfo serviceInfo = services.get(i).serviceInfo; 548 int uid; 549 try { 550 // TODO(jaewan): Do this per user. (b/73597722) 551 uid = manager.getPackageUid(serviceInfo.packageName, 552 PackageManager.GET_META_DATA); 553 } catch (NameNotFoundException e) { 554 continue; 555 } 556 SessionToken2 token; 557 try { 558 token = new SessionToken2(getContext(), 559 serviceInfo.packageName, serviceInfo.name, uid); 560 } catch (IllegalArgumentException e) { 561 Log.w(TAG, "Invalid session service", e); 562 continue; 563 } 564 // If the token already exists, keep it in the mSessions by removing from 565 // sessionTokensToRemove. 566 if (!sessionTokensToRemove.remove(token)) { 567 // New session service is found. 568 notifySessionTokensUpdated |= addSessionRecordLocked(token); 569 } 570 } 571 for (SessionToken2 token : sessionTokensToRemove) { 572 mSessionRecords.remove(token); 573 notifySessionTokensUpdated |= removeSessionRecordLocked(token); 574 } 575 576 if (notifySessionTokensUpdated) { 577 // TODO(jaewan): Pass proper user id to postSessionTokensUpdated(...) 578 postSessionTokensUpdated(UserHandle.USER_ALL); 579 } 580 } 581 if (DEBUG) { 582 Log.d(TAG, "Found " + mSessionRecords.size() + " session services"); 583 for (SessionToken2 token : mSessionRecords.keySet()) { 584 Log.d(TAG, " " + token); 585 } 586 } 587 } 588 589 private void enforcePackageName(String packageName, int uid) { 590 if (TextUtils.isEmpty(packageName)) { 591 throw new IllegalArgumentException("packageName may not be empty"); 592 } 593 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 594 final int packageCount = packages.length; 595 for (int i = 0; i < packageCount; i++) { 596 if (packageName.equals(packages[i])) { 597 return; 598 } 599 } 600 throw new IllegalArgumentException("packageName is not owned by the calling process"); 601 } 602 603 /** 604 * Checks a caller's authorization to register an IRemoteControlDisplay. 605 * Authorization is granted if one of the following is true: 606 * <ul> 607 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL 608 * permission</li> 609 * <li>the caller's listener is one of the enabled notification listeners 610 * for the caller's user</li> 611 * </ul> 612 */ 613 private void enforceMediaPermissions(ComponentName compName, int pid, int uid, 614 int resolvedUserId) { 615 if (isCurrentVolumeController(pid, uid)) return; 616 if (getContext() 617 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) 618 != PackageManager.PERMISSION_GRANTED 619 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), 620 resolvedUserId)) { 621 throw new SecurityException("Missing permission to control media."); 622 } 623 } 624 625 private boolean isCurrentVolumeController(int pid, int uid) { 626 return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 627 pid, uid) == PackageManager.PERMISSION_GRANTED; 628 } 629 630 private void enforceSystemUiPermission(String action, int pid, int uid) { 631 if (!isCurrentVolumeController(pid, uid)) { 632 throw new SecurityException("Only system ui may " + action); 633 } 634 } 635 636 /** 637 * This checks if the component is an enabled notification listener for the 638 * specified user. Enabled components may only operate on behalf of the user 639 * they're running as. 640 * 641 * @param compName The component that is enabled. 642 * @param userId The user id of the caller. 643 * @param forUserId The user id they're making the request on behalf of. 644 * @return True if the component is enabled, false otherwise 645 */ 646 private boolean isEnabledNotificationListener(ComponentName compName, int userId, 647 int forUserId) { 648 if (userId != forUserId) { 649 // You may not access another user's content as an enabled listener. 650 return false; 651 } 652 if (DEBUG) { 653 Log.d(TAG, "Checking if enabled notification listener " + compName); 654 } 655 if (compName != null) { 656 try { 657 return mNotificationManager.isNotificationListenerAccessGrantedForUser( 658 compName, userId); 659 } catch(RemoteException e) { 660 Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e); 661 } 662 } 663 return false; 664 } 665 666 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, 667 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException { 668 synchronized (mLock) { 669 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); 670 } 671 } 672 673 /* 674 * When a session is created the following things need to happen. 675 * 1. Its callback binder needs a link to death 676 * 2. It needs to be added to all sessions. 677 * 3. It needs to be added to the priority stack. 678 * 4. It needs to be added to the relevant user record. 679 */ 680 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, 681 String callerPackageName, ISessionCallback cb, String tag) { 682 FullUserRecord user = getFullUserRecordLocked(userId); 683 if (user == null) { 684 Log.wtf(TAG, "Request from invalid user: " + userId); 685 throw new RuntimeException("Session request from invalid user."); 686 } 687 688 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, 689 callerPackageName, cb, tag, this, mHandler.getLooper()); 690 try { 691 cb.asBinder().linkToDeath(session, 0); 692 } catch (RemoteException e) { 693 throw new RuntimeException("Media Session owner died prematurely.", e); 694 } 695 696 user.mPriorityStack.addSession(session); 697 mHandler.postSessionsChanged(userId); 698 699 if (DEBUG) { 700 Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); 701 } 702 return session; 703 } 704 705 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { 706 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 707 if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) { 708 return i; 709 } 710 } 711 return -1; 712 } 713 714 private void pushSessionsChanged(int userId) { 715 synchronized (mLock) { 716 FullUserRecord user = getFullUserRecordLocked(userId); 717 if (user == null) { 718 Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId); 719 return; 720 } 721 List<MediaSessionRecord> records = getActiveSessionsLocked(userId); 722 int size = records.size(); 723 ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); 724 for (int i = 0; i < size; i++) { 725 tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); 726 } 727 pushRemoteVolumeUpdateLocked(userId); 728 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 729 SessionsListenerRecord record = mSessionsListeners.get(i); 730 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { 731 try { 732 record.mListener.onActiveSessionsChanged(tokens); 733 } catch (RemoteException e) { 734 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", 735 e); 736 mSessionsListeners.remove(i); 737 } 738 } 739 } 740 } 741 } 742 743 private void pushRemoteVolumeUpdateLocked(int userId) { 744 if (mRvc != null) { 745 try { 746 FullUserRecord user = getFullUserRecordLocked(userId); 747 if (user == null) { 748 Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId); 749 return; 750 } 751 MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); 752 mRvc.updateRemoteController(record == null ? null : record.getControllerBinder()); 753 } catch (RemoteException e) { 754 Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); 755 } 756 } 757 } 758 759 /** 760 * Called when the media button receiver for the {@param record} is changed. 761 * 762 * @param record the media session whose media button receiver is updated. 763 */ 764 public void onMediaButtonReceiverChanged(MediaSessionRecord record) { 765 synchronized (mLock) { 766 FullUserRecord user = getFullUserRecordLocked(record.getUserId()); 767 MediaSessionRecord mediaButtonSession = 768 user.mPriorityStack.getMediaButtonSession(); 769 if (record == mediaButtonSession) { 770 user.rememberMediaButtonReceiverLocked(mediaButtonSession); 771 } 772 } 773 } 774 775 private String getCallingPackageName(int uid) { 776 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 777 if (packages != null && packages.length > 0) { 778 return packages[0]; 779 } 780 return ""; 781 } 782 783 private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) { 784 if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { 785 return; 786 } 787 try { 788 mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent); 789 } catch (RemoteException e) { 790 Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener"); 791 } 792 } 793 794 private FullUserRecord getFullUserRecordLocked(int userId) { 795 int fullUserId = mFullUserIds.get(userId, -1); 796 if (fullUserId < 0) { 797 return null; 798 } 799 return mUserRecords.get(fullUserId); 800 } 801 802 void destroySession2Internal(SessionToken2 token) { 803 synchronized (mLock) { 804 boolean notifySessionTokensUpdated = false; 805 if (token.getType() == SessionToken2.TYPE_SESSION) { 806 notifySessionTokensUpdated |= removeSessionRecordLocked(token); 807 } else { 808 notifySessionTokensUpdated |= addSessionRecordLocked(token); 809 } 810 if (notifySessionTokensUpdated) { 811 postSessionTokensUpdated(UserHandle.getUserId(token.getUid())); 812 } 813 } 814 } 815 816 /** 817 * Information about a full user and its corresponding managed profiles. 818 * 819 * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate 820 * them when he/she presses a media/volume button. So keeping media sessions for them in one 821 * place makes more sense and increases the readability.</p> 822 * <p>The contents of this object is guarded by {@link #mLock}. 823 */ 824 final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener { 825 private static final String COMPONENT_NAME_USER_ID_DELIM = ","; 826 private final int mFullUserId; 827 private final MediaSessionStack mPriorityStack; 828 private PendingIntent mLastMediaButtonReceiver; 829 private ComponentName mRestoredMediaButtonReceiver; 830 private int mRestoredMediaButtonReceiverUserId; 831 832 private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener; 833 private int mOnVolumeKeyLongPressListenerUid; 834 private KeyEvent mInitialDownVolumeKeyEvent; 835 private int mInitialDownVolumeStream; 836 private boolean mInitialDownMusicOnly; 837 838 private IOnMediaKeyListener mOnMediaKeyListener; 839 private int mOnMediaKeyListenerUid; 840 private ICallback mCallback; 841 842 public FullUserRecord(int fullUserId) { 843 mFullUserId = fullUserId; 844 mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); 845 // Restore the remembered media button receiver before the boot. 846 String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver, 847 Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); 848 if (mediaButtonReceiver == null) { 849 return; 850 } 851 String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM); 852 if (tokens == null || tokens.length != 2) { 853 return; 854 } 855 mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]); 856 mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]); 857 } 858 859 public void destroySessionsForUserLocked(int userId) { 860 List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId); 861 for (MediaSessionRecord session : sessions) { 862 MediaSessionService.this.destroySessionLocked(session); 863 } 864 } 865 866 public void dumpLocked(PrintWriter pw, String prefix) { 867 pw.print(prefix + "Record for full_user=" + mFullUserId); 868 // Dump managed profile user ids associated with this user. 869 int size = mFullUserIds.size(); 870 for (int i = 0; i < size; i++) { 871 if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i) 872 && mFullUserIds.valueAt(i) == mFullUserId) { 873 pw.print(", profile_user=" + mFullUserIds.keyAt(i)); 874 } 875 } 876 pw.println(); 877 String indent = prefix + " "; 878 pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener); 879 pw.println(indent + "Volume key long-press listener package: " + 880 getCallingPackageName(mOnVolumeKeyLongPressListenerUid)); 881 pw.println(indent + "Media key listener: " + mOnMediaKeyListener); 882 pw.println(indent + "Media key listener package: " + 883 getCallingPackageName(mOnMediaKeyListenerUid)); 884 pw.println(indent + "Callback: " + mCallback); 885 pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver); 886 pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver); 887 mPriorityStack.dump(pw, indent); 888 } 889 890 @Override 891 public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, 892 MediaSessionRecord newMediaButtonSession) { 893 if (DEBUG_KEY_EVENT) { 894 Log.d(TAG, "Media button session is changed to " + newMediaButtonSession); 895 } 896 synchronized (mLock) { 897 if (oldMediaButtonSession != null) { 898 mHandler.postSessionsChanged(oldMediaButtonSession.getUserId()); 899 } 900 if (newMediaButtonSession != null) { 901 rememberMediaButtonReceiverLocked(newMediaButtonSession); 902 mHandler.postSessionsChanged(newMediaButtonSession.getUserId()); 903 } 904 pushAddressedPlayerChangedLocked(); 905 } 906 } 907 908 // Remember media button receiver and keep it in the persistent storage. 909 public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { 910 PendingIntent receiver = record.getMediaButtonReceiver(); 911 mLastMediaButtonReceiver = receiver; 912 mRestoredMediaButtonReceiver = null; 913 String componentName = ""; 914 if (receiver != null) { 915 ComponentName component = receiver.getIntent().getComponent(); 916 if (component != null 917 && record.getPackageName().equals(component.getPackageName())) { 918 componentName = component.flattenToString(); 919 } 920 } 921 Settings.Secure.putStringForUser(mContentResolver, 922 Settings.System.MEDIA_BUTTON_RECEIVER, 923 componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(), 924 mFullUserId); 925 } 926 927 private void pushAddressedPlayerChangedLocked() { 928 if (mCallback == null) { 929 return; 930 } 931 try { 932 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); 933 if (mediaButtonSession != null) { 934 mCallback.onAddressedPlayerChangedToMediaSession( 935 new MediaSession.Token(mediaButtonSession.getControllerBinder())); 936 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { 937 mCallback.onAddressedPlayerChangedToMediaButtonReceiver( 938 mCurrentFullUserRecord.mLastMediaButtonReceiver 939 .getIntent().getComponent()); 940 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { 941 mCallback.onAddressedPlayerChangedToMediaButtonReceiver( 942 mCurrentFullUserRecord.mRestoredMediaButtonReceiver); 943 } 944 } catch (RemoteException e) { 945 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); 946 } 947 } 948 949 private MediaSessionRecord getMediaButtonSessionLocked() { 950 return isGlobalPriorityActiveLocked() 951 ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); 952 } 953 } 954 955 final class SessionsListenerRecord implements IBinder.DeathRecipient { 956 private final IActiveSessionsListener mListener; 957 private final ComponentName mComponentName; 958 private final int mUserId; 959 private final int mPid; 960 private final int mUid; 961 962 public SessionsListenerRecord(IActiveSessionsListener listener, 963 ComponentName componentName, 964 int userId, int pid, int uid) { 965 mListener = listener; 966 mComponentName = componentName; 967 mUserId = userId; 968 mPid = pid; 969 mUid = uid; 970 } 971 972 @Override 973 public void binderDied() { 974 synchronized (mLock) { 975 mSessionsListeners.remove(this); 976 } 977 } 978 } 979 980 final class SettingsObserver extends ContentObserver { 981 private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( 982 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 983 984 private SettingsObserver() { 985 super(null); 986 } 987 988 private void observe() { 989 mContentResolver.registerContentObserver(mSecureSettingsUri, 990 false, this, UserHandle.USER_ALL); 991 } 992 993 @Override 994 public void onChange(boolean selfChange, Uri uri) { 995 updateActiveSessionListeners(); 996 } 997 } 998 999 class SessionManagerImpl extends ISessionManager.Stub { 1000 private static final String EXTRA_WAKELOCK_ACQUIRED = 1001 "android.media.AudioService.WAKELOCK_ACQUIRED"; 1002 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number 1003 1004 private boolean mVoiceButtonDown = false; 1005 private boolean mVoiceButtonHandled = false; 1006 1007 @Override 1008 public ISession createSession(String packageName, ISessionCallback cb, String tag, 1009 int userId) throws RemoteException { 1010 final int pid = Binder.getCallingPid(); 1011 final int uid = Binder.getCallingUid(); 1012 final long token = Binder.clearCallingIdentity(); 1013 try { 1014 enforcePackageName(packageName, uid); 1015 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 1016 false /* allowAll */, true /* requireFull */, "createSession", packageName); 1017 if (cb == null) { 1018 throw new IllegalArgumentException("Controller callback cannot be null"); 1019 } 1020 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) 1021 .getSessionBinder(); 1022 } finally { 1023 Binder.restoreCallingIdentity(token); 1024 } 1025 } 1026 1027 @Override 1028 public List<IBinder> getSessions(ComponentName componentName, int userId) { 1029 final int pid = Binder.getCallingPid(); 1030 final int uid = Binder.getCallingUid(); 1031 final long token = Binder.clearCallingIdentity(); 1032 1033 try { 1034 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 1035 ArrayList<IBinder> binders = new ArrayList<IBinder>(); 1036 synchronized (mLock) { 1037 List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId); 1038 for (MediaSessionRecord record : records) { 1039 binders.add(record.getControllerBinder().asBinder()); 1040 } 1041 } 1042 return binders; 1043 } finally { 1044 Binder.restoreCallingIdentity(token); 1045 } 1046 } 1047 1048 @Override 1049 public void addSessionsListener(IActiveSessionsListener listener, 1050 ComponentName componentName, int userId) throws RemoteException { 1051 final int pid = Binder.getCallingPid(); 1052 final int uid = Binder.getCallingUid(); 1053 final long token = Binder.clearCallingIdentity(); 1054 1055 try { 1056 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 1057 synchronized (mLock) { 1058 int index = findIndexOfSessionsListenerLocked(listener); 1059 if (index != -1) { 1060 Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); 1061 return; 1062 } 1063 SessionsListenerRecord record = new SessionsListenerRecord(listener, 1064 componentName, resolvedUserId, pid, uid); 1065 try { 1066 listener.asBinder().linkToDeath(record, 0); 1067 } catch (RemoteException e) { 1068 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); 1069 return; 1070 } 1071 mSessionsListeners.add(record); 1072 } 1073 } finally { 1074 Binder.restoreCallingIdentity(token); 1075 } 1076 } 1077 1078 @Override 1079 public void removeSessionsListener(IActiveSessionsListener listener) 1080 throws RemoteException { 1081 synchronized (mLock) { 1082 int index = findIndexOfSessionsListenerLocked(listener); 1083 if (index != -1) { 1084 SessionsListenerRecord record = mSessionsListeners.remove(index); 1085 try { 1086 record.mListener.asBinder().unlinkToDeath(record, 0); 1087 } catch (Exception e) { 1088 // ignore exceptions, the record is being removed 1089 } 1090 } 1091 } 1092 } 1093 1094 /** 1095 * Handles the dispatching of the media button events to one of the 1096 * registered listeners, or if there was none, broadcast an 1097 * ACTION_MEDIA_BUTTON intent to the rest of the system. 1098 * 1099 * @param packageName The caller package 1100 * @param asSystemService {@code true} if the event sent to the session as if it was come 1101 * from the system service instead of the app process. This helps sessions to 1102 * distinguish between the key injection by the app and key events from the 1103 * hardware devices. Should be used only when the volume key events aren't handled 1104 * by foreground activity. {@code false} otherwise to tell session about the real 1105 * caller. 1106 * @param keyEvent a non-null KeyEvent whose key code is one of the 1107 * supported media buttons 1108 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held 1109 * while this key event is dispatched. 1110 */ 1111 @Override 1112 public void dispatchMediaKeyEvent(String packageName, boolean asSystemService, 1113 KeyEvent keyEvent, boolean needWakeLock) { 1114 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) { 1115 Log.w(TAG, "Attempted to dispatch null or non-media key event."); 1116 return; 1117 } 1118 1119 final int pid = Binder.getCallingPid(); 1120 final int uid = Binder.getCallingUid(); 1121 final long token = Binder.clearCallingIdentity(); 1122 try { 1123 if (DEBUG) { 1124 Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid 1125 + ", uid=" + uid + ", asSystem=" + asSystemService + ", event=" 1126 + keyEvent); 1127 } 1128 if (!isUserSetupComplete()) { 1129 // Global media key handling can have the side-effect of starting new 1130 // activities which is undesirable while setup is in progress. 1131 Slog.i(TAG, "Not dispatching media key event because user " 1132 + "setup is in progress."); 1133 return; 1134 } 1135 1136 synchronized (mLock) { 1137 boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked(); 1138 if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) { 1139 // Prevent dispatching key event through reflection while the global 1140 // priority session is active. 1141 Slog.i(TAG, "Only the system can dispatch media key event " 1142 + "to the global priority session."); 1143 return; 1144 } 1145 if (!isGlobalPriorityActive) { 1146 if (mCurrentFullUserRecord.mOnMediaKeyListener != null) { 1147 if (DEBUG_KEY_EVENT) { 1148 Log.d(TAG, "Send " + keyEvent + " to the media key listener"); 1149 } 1150 try { 1151 mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent, 1152 new MediaKeyListenerResultReceiver(packageName, pid, uid, 1153 asSystemService, keyEvent, needWakeLock)); 1154 return; 1155 } catch (RemoteException e) { 1156 Log.w(TAG, "Failed to send " + keyEvent 1157 + " to the media key listener"); 1158 } 1159 } 1160 } 1161 if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) { 1162 handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, 1163 needWakeLock); 1164 } else { 1165 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, 1166 keyEvent, needWakeLock); 1167 } 1168 } 1169 } finally { 1170 Binder.restoreCallingIdentity(token); 1171 } 1172 } 1173 1174 @Override 1175 public void setCallback(ICallback callback) { 1176 final int pid = Binder.getCallingPid(); 1177 final int uid = Binder.getCallingUid(); 1178 final long token = Binder.clearCallingIdentity(); 1179 try { 1180 if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) { 1181 throw new SecurityException("Only Bluetooth service processes can set" 1182 + " Callback"); 1183 } 1184 synchronized (mLock) { 1185 int userId = UserHandle.getUserId(uid); 1186 FullUserRecord user = getFullUserRecordLocked(userId); 1187 if (user == null || user.mFullUserId != userId) { 1188 Log.w(TAG, "Only the full user can set the callback" 1189 + ", userId=" + userId); 1190 return; 1191 } 1192 user.mCallback = callback; 1193 Log.d(TAG, "The callback " + user.mCallback 1194 + " is set by " + getCallingPackageName(uid)); 1195 if (user.mCallback == null) { 1196 return; 1197 } 1198 try { 1199 user.mCallback.asBinder().linkToDeath( 1200 new IBinder.DeathRecipient() { 1201 @Override 1202 public void binderDied() { 1203 synchronized (mLock) { 1204 user.mCallback = null; 1205 } 1206 } 1207 }, 0); 1208 user.pushAddressedPlayerChangedLocked(); 1209 } catch (RemoteException e) { 1210 Log.w(TAG, "Failed to set callback", e); 1211 user.mCallback = null; 1212 } 1213 } 1214 } finally { 1215 Binder.restoreCallingIdentity(token); 1216 } 1217 } 1218 1219 @Override 1220 public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) { 1221 final int pid = Binder.getCallingPid(); 1222 final int uid = Binder.getCallingUid(); 1223 final long token = Binder.clearCallingIdentity(); 1224 try { 1225 // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission. 1226 if (getContext().checkPermission( 1227 android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid) 1228 != PackageManager.PERMISSION_GRANTED) { 1229 throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" + 1230 " permission."); 1231 } 1232 1233 synchronized (mLock) { 1234 int userId = UserHandle.getUserId(uid); 1235 FullUserRecord user = getFullUserRecordLocked(userId); 1236 if (user == null || user.mFullUserId != userId) { 1237 Log.w(TAG, "Only the full user can set the volume key long-press listener" 1238 + ", userId=" + userId); 1239 return; 1240 } 1241 if (user.mOnVolumeKeyLongPressListener != null && 1242 user.mOnVolumeKeyLongPressListenerUid != uid) { 1243 Log.w(TAG, "The volume key long-press listener cannot be reset" 1244 + " by another app , mOnVolumeKeyLongPressListener=" 1245 + user.mOnVolumeKeyLongPressListenerUid 1246 + ", uid=" + uid); 1247 return; 1248 } 1249 1250 user.mOnVolumeKeyLongPressListener = listener; 1251 user.mOnVolumeKeyLongPressListenerUid = uid; 1252 1253 Log.d(TAG, "The volume key long-press listener " 1254 + listener + " is set by " + getCallingPackageName(uid)); 1255 1256 if (user.mOnVolumeKeyLongPressListener != null) { 1257 try { 1258 user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath( 1259 new IBinder.DeathRecipient() { 1260 @Override 1261 public void binderDied() { 1262 synchronized (mLock) { 1263 user.mOnVolumeKeyLongPressListener = null; 1264 } 1265 } 1266 }, 0); 1267 } catch (RemoteException e) { 1268 Log.w(TAG, "Failed to set death recipient " 1269 + user.mOnVolumeKeyLongPressListener); 1270 user.mOnVolumeKeyLongPressListener = null; 1271 } 1272 } 1273 } 1274 } finally { 1275 Binder.restoreCallingIdentity(token); 1276 } 1277 } 1278 1279 @Override 1280 public void setOnMediaKeyListener(IOnMediaKeyListener listener) { 1281 final int pid = Binder.getCallingPid(); 1282 final int uid = Binder.getCallingUid(); 1283 final long token = Binder.clearCallingIdentity(); 1284 try { 1285 // Enforce SET_MEDIA_KEY_LISTENER permission. 1286 if (getContext().checkPermission( 1287 android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid) 1288 != PackageManager.PERMISSION_GRANTED) { 1289 throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" + 1290 " permission."); 1291 } 1292 1293 synchronized (mLock) { 1294 int userId = UserHandle.getUserId(uid); 1295 FullUserRecord user = getFullUserRecordLocked(userId); 1296 if (user == null || user.mFullUserId != userId) { 1297 Log.w(TAG, "Only the full user can set the media key listener" 1298 + ", userId=" + userId); 1299 return; 1300 } 1301 if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) { 1302 Log.w(TAG, "The media key listener cannot be reset by another app. " 1303 + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid 1304 + ", uid=" + uid); 1305 return; 1306 } 1307 1308 user.mOnMediaKeyListener = listener; 1309 user.mOnMediaKeyListenerUid = uid; 1310 1311 Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener 1312 + " is set by " + getCallingPackageName(uid)); 1313 1314 if (user.mOnMediaKeyListener != null) { 1315 try { 1316 user.mOnMediaKeyListener.asBinder().linkToDeath( 1317 new IBinder.DeathRecipient() { 1318 @Override 1319 public void binderDied() { 1320 synchronized (mLock) { 1321 user.mOnMediaKeyListener = null; 1322 } 1323 } 1324 }, 0); 1325 } catch (RemoteException e) { 1326 Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener); 1327 user.mOnMediaKeyListener = null; 1328 } 1329 } 1330 } 1331 } finally { 1332 Binder.restoreCallingIdentity(token); 1333 } 1334 } 1335 1336 /** 1337 * Handles the dispatching of the volume button events to one of the 1338 * registered listeners. If there's a volume key long-press listener and 1339 * there's no active global priority session, long-pressess will be sent to the 1340 * long-press listener instead of adjusting volume. 1341 * 1342 * @param packageName The caller package. 1343 * @param asSystemService {@code true} if the event sent to the session as if it was come 1344 * from the system service instead of the app process. This helps sessions to 1345 * distinguish between the key injection by the app and key events from the 1346 * hardware devices. Should be used only when the volume key events aren't handled 1347 * by foreground activity. {@code false} otherwise to tell session about the real 1348 * caller. 1349 * @param keyEvent a non-null KeyEvent whose key code is one of the 1350 * {@link KeyEvent#KEYCODE_VOLUME_UP}, 1351 * {@link KeyEvent#KEYCODE_VOLUME_DOWN}, 1352 * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}. 1353 * @param stream stream type to adjust volume. 1354 * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume. 1355 */ 1356 @Override 1357 public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, 1358 KeyEvent keyEvent, int stream, boolean musicOnly) { 1359 if (keyEvent == null || 1360 (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP 1361 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN 1362 && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) { 1363 Log.w(TAG, "Attempted to dispatch null or non-volume key event."); 1364 return; 1365 } 1366 1367 final int pid = Binder.getCallingPid(); 1368 final int uid = Binder.getCallingUid(); 1369 final long token = Binder.clearCallingIdentity(); 1370 1371 if (DEBUG_KEY_EVENT) { 1372 Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid=" 1373 + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent); 1374 } 1375 1376 try { 1377 synchronized (mLock) { 1378 if (isGlobalPriorityActiveLocked() 1379 || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { 1380 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService, 1381 keyEvent, stream, musicOnly); 1382 } else { 1383 // TODO: Consider the case when both volume up and down keys are pressed 1384 // at the same time. 1385 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { 1386 if (keyEvent.getRepeatCount() == 0) { 1387 // Keeps the copy of the KeyEvent because it can be reused. 1388 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = 1389 KeyEvent.obtain(keyEvent); 1390 mCurrentFullUserRecord.mInitialDownVolumeStream = stream; 1391 mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly; 1392 mHandler.sendMessageDelayed( 1393 mHandler.obtainMessage( 1394 MessageHandler.MSG_VOLUME_INITIAL_DOWN, 1395 mCurrentFullUserRecord.mFullUserId, 0), 1396 mLongPressTimeout); 1397 } 1398 if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { 1399 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); 1400 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) { 1401 dispatchVolumeKeyLongPressLocked( 1402 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent); 1403 // Mark that the key is already handled. 1404 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null; 1405 } 1406 dispatchVolumeKeyLongPressLocked(keyEvent); 1407 } 1408 } else { // if up 1409 mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); 1410 if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null 1411 && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent 1412 .getDownTime() == keyEvent.getDownTime()) { 1413 // Short-press. Should change volume. 1414 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService, 1415 mCurrentFullUserRecord.mInitialDownVolumeKeyEvent, 1416 mCurrentFullUserRecord.mInitialDownVolumeStream, 1417 mCurrentFullUserRecord.mInitialDownMusicOnly); 1418 dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService, 1419 keyEvent, stream, musicOnly); 1420 } else { 1421 dispatchVolumeKeyLongPressLocked(keyEvent); 1422 } 1423 } 1424 } 1425 } 1426 } finally { 1427 Binder.restoreCallingIdentity(token); 1428 } 1429 } 1430 1431 private void dispatchVolumeKeyEventLocked(String packageName, int pid, int uid, 1432 boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) { 1433 boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; 1434 boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; 1435 int direction = 0; 1436 boolean isMute = false; 1437 switch (keyEvent.getKeyCode()) { 1438 case KeyEvent.KEYCODE_VOLUME_UP: 1439 direction = AudioManager.ADJUST_RAISE; 1440 break; 1441 case KeyEvent.KEYCODE_VOLUME_DOWN: 1442 direction = AudioManager.ADJUST_LOWER; 1443 break; 1444 case KeyEvent.KEYCODE_VOLUME_MUTE: 1445 isMute = true; 1446 break; 1447 } 1448 if (down || up) { 1449 int flags = AudioManager.FLAG_FROM_KEY; 1450 if (musicOnly) { 1451 // This flag is used when the screen is off to only affect active media. 1452 flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY; 1453 } else { 1454 // These flags are consistent with the home screen 1455 if (up) { 1456 flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; 1457 } else { 1458 flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE; 1459 } 1460 } 1461 if (direction != 0) { 1462 // If this is action up we want to send a beep for non-music events 1463 if (up) { 1464 direction = 0; 1465 } 1466 dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream, 1467 direction, flags); 1468 } else if (isMute) { 1469 if (down && keyEvent.getRepeatCount() == 0) { 1470 dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream, 1471 AudioManager.ADJUST_TOGGLE_MUTE, flags); 1472 } 1473 } 1474 } 1475 } 1476 1477 @Override 1478 public void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, 1479 int flags) { 1480 final int pid = Binder.getCallingPid(); 1481 final int uid = Binder.getCallingUid(); 1482 final long token = Binder.clearCallingIdentity(); 1483 try { 1484 synchronized (mLock) { 1485 dispatchAdjustVolumeLocked(packageName, pid, uid, false, 1486 suggestedStream, delta, flags); 1487 } 1488 } finally { 1489 Binder.restoreCallingIdentity(token); 1490 } 1491 } 1492 1493 @Override 1494 public void setRemoteVolumeController(IRemoteVolumeController rvc) { 1495 final int pid = Binder.getCallingPid(); 1496 final int uid = Binder.getCallingUid(); 1497 final long token = Binder.clearCallingIdentity(); 1498 try { 1499 enforceSystemUiPermission("listen for volume changes", pid, uid); 1500 mRvc = rvc; 1501 } finally { 1502 Binder.restoreCallingIdentity(token); 1503 } 1504 } 1505 1506 @Override 1507 public boolean isGlobalPriorityActive() { 1508 synchronized (mLock) { 1509 return isGlobalPriorityActiveLocked(); 1510 } 1511 } 1512 1513 @Override 1514 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 1515 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; 1516 1517 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); 1518 pw.println(); 1519 1520 synchronized (mLock) { 1521 pw.println(mSessionsListeners.size() + " sessions listeners."); 1522 pw.println("Global priority session is " + mGlobalPrioritySession); 1523 if (mGlobalPrioritySession != null) { 1524 mGlobalPrioritySession.dump(pw, " "); 1525 } 1526 pw.println("User Records:"); 1527 int count = mUserRecords.size(); 1528 for (int i = 0; i < count; i++) { 1529 mUserRecords.valueAt(i).dumpLocked(pw, ""); 1530 } 1531 mAudioPlayerStateMonitor.dump(getContext(), pw, ""); 1532 } 1533 } 1534 1535 /** 1536 * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL 1537 * permission or an enabled notification listener) 1538 * 1539 * @param controllerPackageName package name of the controller app 1540 * @param controllerPid pid of the controller app 1541 * @param controllerUid uid of the controller app 1542 */ 1543 @Override 1544 public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid) 1545 throws RemoteException { 1546 final int uid = Binder.getCallingUid(); 1547 final long token = Binder.clearCallingIdentity(); 1548 try { 1549 int controllerUserId = UserHandle.getUserId(controllerUid); 1550 int controllerUidFromPackageName; 1551 try { 1552 controllerUidFromPackageName = getContext().getPackageManager() 1553 .getPackageUidAsUser(controllerPackageName, controllerUserId); 1554 } catch (NameNotFoundException e) { 1555 if (DEBUG) { 1556 Log.d(TAG, "Package " + controllerPackageName + " doesn't exist"); 1557 } 1558 return false; 1559 } 1560 if (controllerUidFromPackageName != controllerUid) { 1561 if (DEBUG) { 1562 Log.d(TAG, "Package name " + controllerPackageName 1563 + " doesn't match with the uid " + controllerUid); 1564 } 1565 return false; 1566 } 1567 return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName, 1568 controllerPid, controllerUid); 1569 } finally { 1570 Binder.restoreCallingIdentity(token); 1571 } 1572 } 1573 1574 /** 1575 * Called when a {@link android.media.MediaSession2} instance is created. 1576 * <p> 1577 * This does two things. 1578 * 1. Keep the newly created session in the service 1579 * 2. Do sanity check to ensure unique id per package, and return result 1580 * 1581 * @param sessionToken SessionToken2 object in bundled form 1582 * @return {@code true} if the session's id isn't used by the package now. {@code false} 1583 * otherwise. 1584 */ 1585 @Override 1586 public boolean createSession2(Bundle sessionToken) { 1587 if (!USE_MEDIA2_APIS) { 1588 return false; 1589 } 1590 final int uid = Binder.getCallingUid(); 1591 final SessionToken2 token = SessionToken2.fromBundle(sessionToken); 1592 if (token == null || token.getUid() != uid) { 1593 Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid() 1594 + " but from uid=" + uid); 1595 } 1596 if (DEBUG) { 1597 Log.d(TAG, "createSession2: " + token); 1598 } 1599 synchronized (mLock) { 1600 MediaController2 controller = mSessionRecords.get(token); 1601 if (controller != null && controller.isConnected()) { 1602 return false; 1603 } 1604 Context context = getContext(); 1605 controller = new MediaController2(context, token, context.getMainExecutor(), 1606 new ControllerCallback(token)); 1607 if (addSessionRecordLocked(token, controller)) { 1608 postSessionTokensUpdated(UserHandle.getUserId(token.getUid())); 1609 } 1610 return true; 1611 } 1612 } 1613 1614 /** 1615 * Called when a {@link android.media.MediaSession2} instance is closed. (i.e. destroyed) 1616 * <p> 1617 * Ideally service should know that a session is destroyed through the 1618 * {@link android.media.MediaController2.ControllerCallback#onDisconnected()}, which is 1619 * asynchronous call. However, we also need synchronous way together to address timing 1620 * issue. If the package recreates the session almost immediately, which happens commonly 1621 * for tests, service will reject the creation through {@link #onSessionCreated(Bundle)} 1622 * if the service hasn't notified previous destroy yet. This synchronous API will address 1623 * the issue. 1624 * 1625 * @param sessionToken SessionToken2 object in bundled form 1626 */ 1627 @Override 1628 public void destroySession2(Bundle sessionToken) { 1629 if (!USE_MEDIA2_APIS) { 1630 return; 1631 } 1632 final int uid = Binder.getCallingUid(); 1633 final SessionToken2 token = SessionToken2.fromBundle(sessionToken); 1634 if (token == null || token.getUid() != uid) { 1635 Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid() 1636 + " but from uid=" + uid); 1637 } 1638 if (DEBUG) { 1639 Log.d(TAG, "destroySession2 " + token); 1640 } 1641 destroySession2Internal(token); 1642 } 1643 1644 // TODO(jaewan): Make this API take userId as an argument (b/73597722) 1645 @Override 1646 public List<Bundle> getSessionTokens(boolean activeSessionOnly, 1647 boolean sessionServiceOnly, String packageName) throws RemoteException { 1648 if (!USE_MEDIA2_APIS) { 1649 return null; 1650 } 1651 final int pid = Binder.getCallingPid(); 1652 final int uid = Binder.getCallingUid(); 1653 final long token = Binder.clearCallingIdentity(); 1654 1655 List<Bundle> tokens = new ArrayList<>(); 1656 try { 1657 verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid); 1658 synchronized (mLock) { 1659 for (Map.Entry<SessionToken2, MediaController2> record 1660 : mSessionRecords.entrySet()) { 1661 boolean isSessionService = (record.getKey().getType() != TYPE_SESSION); 1662 boolean isActive = record.getValue() != null; 1663 if ((activeSessionOnly && !isActive) 1664 || (sessionServiceOnly && !isSessionService)) { 1665 continue; 1666 } 1667 tokens.add(record.getKey().toBundle()); 1668 } 1669 } 1670 } finally { 1671 Binder.restoreCallingIdentity(token); 1672 } 1673 return tokens; 1674 } 1675 1676 @Override 1677 public void addSessionTokensListener(ISessionTokensListener listener, int userId, 1678 String packageName) throws RemoteException { 1679 if (!USE_MEDIA2_APIS) { 1680 return; 1681 } 1682 final int pid = Binder.getCallingPid(); 1683 final int uid = Binder.getCallingUid(); 1684 final long token = Binder.clearCallingIdentity(); 1685 try { 1686 int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid); 1687 synchronized (mLock) { 1688 final SessionTokensListenerRecord record = 1689 new SessionTokensListenerRecord(listener, resolvedUserId); 1690 try { 1691 listener.asBinder().linkToDeath(record, 0); 1692 } catch (RemoteException e) { 1693 } 1694 mSessionTokensListeners.add(record); 1695 } 1696 } finally { 1697 Binder.restoreCallingIdentity(token); 1698 } 1699 } 1700 1701 // TODO(jaewan): Make this API take userId as an argument (b/73597722) 1702 @Override 1703 public void removeSessionTokensListener(ISessionTokensListener listener, 1704 String packageName) throws RemoteException { 1705 if (!USE_MEDIA2_APIS) { 1706 return; 1707 } 1708 final int pid = Binder.getCallingPid(); 1709 final int uid = Binder.getCallingUid(); 1710 final long token = Binder.clearCallingIdentity(); 1711 try { 1712 verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid); 1713 synchronized (mLock) { 1714 IBinder listenerBinder = listener.asBinder(); 1715 for (SessionTokensListenerRecord record : mSessionTokensListeners) { 1716 if (listenerBinder.equals(record.mListener.asBinder())) { 1717 try { 1718 listenerBinder.unlinkToDeath(record, 0); 1719 } catch (NoSuchElementException e) { 1720 } 1721 mSessionTokensListeners.remove(record); 1722 break; 1723 } 1724 } 1725 } 1726 } finally { 1727 Binder.restoreCallingIdentity(token); 1728 } 1729 } 1730 1731 // For MediaSession 1732 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, 1733 final int uid) { 1734 String packageName = null; 1735 if (componentName != null) { 1736 // If they gave us a component name verify they own the 1737 // package 1738 packageName = componentName.getPackageName(); 1739 enforcePackageName(packageName, uid); 1740 } 1741 // Check that they can make calls on behalf of the user and 1742 // get the final user id 1743 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 1744 true /* allowAll */, true /* requireFull */, "getSessions", packageName); 1745 // Check if they have the permissions or their component is 1746 // enabled for the user they're calling from. 1747 enforceMediaPermissions(componentName, pid, uid, resolvedUserId); 1748 return resolvedUserId; 1749 } 1750 1751 // For MediaSession2 1752 private int verifySessionsRequest2(int targetUserId, String callerPackageName, 1753 int callerPid, int callerUid) throws RemoteException { 1754 // Check that they can make calls on behalf of the user and get the final user id. 1755 int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid, 1756 targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens", 1757 callerPackageName); 1758 // Check if they have the permissions or their component is 1759 // enabled for the user they're calling from. 1760 if (!hasMediaControlPermission( 1761 resolvedUserId, callerPackageName, callerPid, callerUid)) { 1762 throw new SecurityException("Missing permission to control media."); 1763 } 1764 return resolvedUserId; 1765 } 1766 1767 // For MediaSession2 1768 private boolean hasMediaControlPermission(int resolvedUserId, String packageName, 1769 int pid, int uid) throws RemoteException { 1770 // Allow API calls from the System UI 1771 if (isCurrentVolumeController(pid, uid)) { 1772 return true; 1773 } 1774 1775 // Check if it's system server or has MEDIA_CONTENT_CONTROL. 1776 // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra 1777 // check here. 1778 if (uid == Process.SYSTEM_UID || getContext().checkPermission( 1779 android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) 1780 == PackageManager.PERMISSION_GRANTED) { 1781 return true; 1782 } else if (DEBUG) { 1783 Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); 1784 } 1785 1786 // You may not access another user's content as an enabled listener. 1787 final int userId = UserHandle.getUserId(uid); 1788 if (resolvedUserId != userId) { 1789 return false; 1790 } 1791 1792 // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( 1793 // String pkgName) to notification team for optimization 1794 final List<ComponentName> enabledNotificationListeners = 1795 mNotificationManager.getEnabledNotificationListeners(userId); 1796 if (enabledNotificationListeners != null) { 1797 for (int i = 0; i < enabledNotificationListeners.size(); i++) { 1798 if (TextUtils.equals(packageName, 1799 enabledNotificationListeners.get(i).getPackageName())) { 1800 return true; 1801 } 1802 } 1803 } 1804 if (DEBUG) { 1805 Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled " 1806 + "notification listener"); 1807 } 1808 return false; 1809 } 1810 1811 private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid, 1812 boolean asSystemService, int suggestedStream, int direction, int flags) { 1813 MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession 1814 : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); 1815 1816 boolean preferSuggestedStream = false; 1817 if (isValidLocalStreamType(suggestedStream) 1818 && AudioSystem.isStreamActive(suggestedStream, 0)) { 1819 preferSuggestedStream = true; 1820 } 1821 if (DEBUG_KEY_EVENT) { 1822 Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags=" 1823 + flags + ", suggestedStream=" + suggestedStream 1824 + ", preferSuggestedStream=" + preferSuggestedStream); 1825 } 1826 if (session == null || preferSuggestedStream) { 1827 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0 1828 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { 1829 if (DEBUG) { 1830 Log.d(TAG, "No active session to adjust, skipping media only volume event"); 1831 } 1832 return; 1833 } 1834 1835 // Execute mAudioService.adjustSuggestedStreamVolume() on 1836 // handler thread of MediaSessionService. 1837 // This will release the MediaSessionService.mLock sooner and avoid 1838 // a potential deadlock between MediaSessionService.mLock and 1839 // ActivityManagerService lock. 1840 mHandler.post(new Runnable() { 1841 @Override 1842 public void run() { 1843 try { 1844 String packageName = getContext().getOpPackageName(); 1845 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, 1846 flags, packageName, TAG); 1847 } catch (RemoteException e) { 1848 Log.e(TAG, "Error adjusting default volume.", e); 1849 } catch (IllegalArgumentException e) { 1850 Log.e(TAG, "Cannot adjust volume: direction=" + direction 1851 + ", suggestedStream=" + suggestedStream + ", flags=" + flags, 1852 e); 1853 } 1854 } 1855 }); 1856 } else { 1857 session.adjustVolume(packageName, pid, uid, null, asSystemService, 1858 direction, flags, true); 1859 } 1860 } 1861 1862 private void handleVoiceKeyEventLocked(String packageName, int pid, int uid, 1863 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { 1864 int action = keyEvent.getAction(); 1865 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; 1866 if (action == KeyEvent.ACTION_DOWN) { 1867 if (keyEvent.getRepeatCount() == 0) { 1868 mVoiceButtonDown = true; 1869 mVoiceButtonHandled = false; 1870 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) { 1871 mVoiceButtonHandled = true; 1872 startVoiceInput(needWakeLock); 1873 } 1874 } else if (action == KeyEvent.ACTION_UP) { 1875 if (mVoiceButtonDown) { 1876 mVoiceButtonDown = false; 1877 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { 1878 // Resend the down then send this event through 1879 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); 1880 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, 1881 downEvent, needWakeLock); 1882 dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, 1883 keyEvent, needWakeLock); 1884 } 1885 } 1886 } 1887 } 1888 1889 private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, 1890 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { 1891 MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); 1892 if (session != null) { 1893 if (DEBUG_KEY_EVENT) { 1894 Log.d(TAG, "Sending " + keyEvent + " to " + session); 1895 } 1896 if (needWakeLock) { 1897 mKeyEventReceiver.aquireWakeLockLocked(); 1898 } 1899 // If we don't need a wakelock use -1 as the id so we won't release it later. 1900 session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent, 1901 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, 1902 mKeyEventReceiver); 1903 if (mCurrentFullUserRecord.mCallback != null) { 1904 try { 1905 mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession( 1906 keyEvent, new MediaSession.Token(session.getControllerBinder())); 1907 } catch (RemoteException e) { 1908 Log.w(TAG, "Failed to send callback", e); 1909 } 1910 } 1911 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null 1912 || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { 1913 if (needWakeLock) { 1914 mKeyEventReceiver.aquireWakeLockLocked(); 1915 } 1916 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 1917 mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1918 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 1919 // TODO: Find a way to also send PID/UID in secure way. 1920 String callerPackageName = 1921 (asSystemService) ? getContext().getPackageName() : packageName; 1922 mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName); 1923 try { 1924 if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { 1925 PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver; 1926 if (DEBUG_KEY_EVENT) { 1927 Log.d(TAG, "Sending " + keyEvent 1928 + " to the last known PendingIntent " + receiver); 1929 } 1930 receiver.send(getContext(), 1931 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, 1932 mediaButtonIntent, mKeyEventReceiver, mHandler); 1933 if (mCurrentFullUserRecord.mCallback != null) { 1934 ComponentName componentName = mCurrentFullUserRecord 1935 .mLastMediaButtonReceiver.getIntent().getComponent(); 1936 if (componentName != null) { 1937 mCurrentFullUserRecord.mCallback 1938 .onMediaKeyEventDispatchedToMediaButtonReceiver( 1939 keyEvent, componentName); 1940 } 1941 } 1942 } else { 1943 ComponentName receiver = 1944 mCurrentFullUserRecord.mRestoredMediaButtonReceiver; 1945 if (DEBUG_KEY_EVENT) { 1946 Log.d(TAG, "Sending " + keyEvent + " to the restored intent " 1947 + receiver); 1948 } 1949 mediaButtonIntent.setComponent(receiver); 1950 getContext().sendBroadcastAsUser(mediaButtonIntent, 1951 UserHandle.of(mCurrentFullUserRecord 1952 .mRestoredMediaButtonReceiverUserId)); 1953 if (mCurrentFullUserRecord.mCallback != null) { 1954 mCurrentFullUserRecord.mCallback 1955 .onMediaKeyEventDispatchedToMediaButtonReceiver( 1956 keyEvent, receiver); 1957 } 1958 } 1959 } catch (CanceledException e) { 1960 Log.i(TAG, "Error sending key event to media button receiver " 1961 + mCurrentFullUserRecord.mLastMediaButtonReceiver, e); 1962 } catch (RemoteException e) { 1963 Log.w(TAG, "Failed to send callback", e); 1964 } 1965 } 1966 } 1967 1968 private void startVoiceInput(boolean needWakeLock) { 1969 Intent voiceIntent = null; 1970 // select which type of search to launch: 1971 // - screen on and device unlocked: action is ACTION_WEB_SEARCH 1972 // - device locked or screen off: action is 1973 // ACTION_VOICE_SEARCH_HANDS_FREE 1974 // with EXTRA_SECURE set to true if the device is securely locked 1975 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 1976 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); 1977 if (!isLocked && pm.isScreenOn()) { 1978 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); 1979 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH"); 1980 } else { 1981 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); 1982 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, 1983 isLocked && mKeyguardManager.isKeyguardSecure()); 1984 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE"); 1985 } 1986 // start the search activity 1987 if (needWakeLock) { 1988 mMediaEventWakeLock.acquire(); 1989 } 1990 try { 1991 if (voiceIntent != null) { 1992 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1993 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 1994 if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent); 1995 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT); 1996 } 1997 } catch (ActivityNotFoundException e) { 1998 Log.w(TAG, "No activity for search: " + e); 1999 } finally { 2000 if (needWakeLock) { 2001 mMediaEventWakeLock.release(); 2002 } 2003 } 2004 } 2005 2006 private boolean isVoiceKey(int keyCode) { 2007 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK 2008 || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 2009 } 2010 2011 private boolean isUserSetupComplete() { 2012 return Settings.Secure.getIntForUser(getContext().getContentResolver(), 2013 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; 2014 } 2015 2016 // we only handle public stream types, which are 0-5 2017 private boolean isValidLocalStreamType(int streamType) { 2018 return streamType >= AudioManager.STREAM_VOICE_CALL 2019 && streamType <= AudioManager.STREAM_NOTIFICATION; 2020 } 2021 2022 private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { 2023 private final String mPackageName; 2024 private final int mPid; 2025 private final int mUid; 2026 private final boolean mAsSystemService; 2027 private final KeyEvent mKeyEvent; 2028 private final boolean mNeedWakeLock; 2029 private boolean mHandled; 2030 2031 private MediaKeyListenerResultReceiver(String packageName, int pid, int uid, 2032 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { 2033 super(mHandler); 2034 mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT); 2035 mPackageName = packageName; 2036 mPid = pid; 2037 mUid = uid; 2038 mAsSystemService = asSystemService; 2039 mKeyEvent = keyEvent; 2040 mNeedWakeLock = needWakeLock; 2041 } 2042 2043 @Override 2044 public void run() { 2045 Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent); 2046 dispatchMediaKeyEvent(); 2047 } 2048 2049 @Override 2050 protected void onReceiveResult(int resultCode, Bundle resultData) { 2051 if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) { 2052 mHandled = true; 2053 mHandler.removeCallbacks(this); 2054 return; 2055 } 2056 dispatchMediaKeyEvent(); 2057 } 2058 2059 private void dispatchMediaKeyEvent() { 2060 if (mHandled) { 2061 return; 2062 } 2063 mHandled = true; 2064 mHandler.removeCallbacks(this); 2065 synchronized (mLock) { 2066 if (!isGlobalPriorityActiveLocked() 2067 && isVoiceKey(mKeyEvent.getKeyCode())) { 2068 handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, 2069 mKeyEvent, mNeedWakeLock); 2070 } else { 2071 dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, 2072 mKeyEvent, mNeedWakeLock); 2073 } 2074 } 2075 } 2076 } 2077 2078 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); 2079 2080 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable, 2081 PendingIntent.OnFinished { 2082 private final Handler mHandler; 2083 private int mRefCount = 0; 2084 private int mLastTimeoutId = 0; 2085 2086 public KeyEventWakeLockReceiver(Handler handler) { 2087 super(handler); 2088 mHandler = handler; 2089 } 2090 2091 public void onTimeout() { 2092 synchronized (mLock) { 2093 if (mRefCount == 0) { 2094 // We've already released it, so just return 2095 return; 2096 } 2097 mLastTimeoutId++; 2098 mRefCount = 0; 2099 releaseWakeLockLocked(); 2100 } 2101 } 2102 2103 public void aquireWakeLockLocked() { 2104 if (mRefCount == 0) { 2105 mMediaEventWakeLock.acquire(); 2106 } 2107 mRefCount++; 2108 mHandler.removeCallbacks(this); 2109 mHandler.postDelayed(this, WAKELOCK_TIMEOUT); 2110 2111 } 2112 2113 @Override 2114 public void run() { 2115 onTimeout(); 2116 } 2117 2118 @Override 2119 protected void onReceiveResult(int resultCode, Bundle resultData) { 2120 if (resultCode < mLastTimeoutId) { 2121 // Ignore results from calls that were before the last 2122 // timeout, just in case. 2123 return; 2124 } else { 2125 synchronized (mLock) { 2126 if (mRefCount > 0) { 2127 mRefCount--; 2128 if (mRefCount == 0) { 2129 releaseWakeLockLocked(); 2130 } 2131 } 2132 } 2133 } 2134 } 2135 2136 private void releaseWakeLockLocked() { 2137 mMediaEventWakeLock.release(); 2138 mHandler.removeCallbacks(this); 2139 } 2140 2141 @Override 2142 public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, 2143 String resultData, Bundle resultExtras) { 2144 onReceiveResult(resultCode, null); 2145 } 2146 }; 2147 2148 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { 2149 @Override 2150 public void onReceive(Context context, Intent intent) { 2151 if (intent == null) { 2152 return; 2153 } 2154 Bundle extras = intent.getExtras(); 2155 if (extras == null) { 2156 return; 2157 } 2158 synchronized (mLock) { 2159 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED) 2160 && mMediaEventWakeLock.isHeld()) { 2161 mMediaEventWakeLock.release(); 2162 } 2163 } 2164 } 2165 }; 2166 } 2167 2168 final class MessageHandler extends Handler { 2169 private static final int MSG_SESSIONS_CHANGED = 1; 2170 private static final int MSG_VOLUME_INITIAL_DOWN = 2; 2171 private static final int MSG_SESSIONS_TOKENS_CHANGED = 3; 2172 private final SparseArray<Integer> mIntegerCache = new SparseArray<>(); 2173 2174 @Override 2175 public void handleMessage(Message msg) { 2176 switch (msg.what) { 2177 case MSG_SESSIONS_CHANGED: 2178 pushSessionsChanged((int) msg.obj); 2179 break; 2180 case MSG_VOLUME_INITIAL_DOWN: 2181 synchronized (mLock) { 2182 FullUserRecord user = mUserRecords.get((int) msg.arg1); 2183 if (user != null && user.mInitialDownVolumeKeyEvent != null) { 2184 dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent); 2185 // Mark that the key is already handled. 2186 user.mInitialDownVolumeKeyEvent = null; 2187 } 2188 } 2189 break; 2190 case MSG_SESSIONS_TOKENS_CHANGED: 2191 pushSessionTokensChanged((int) msg.obj); 2192 break; 2193 } 2194 } 2195 2196 public void postSessionsChanged(int userId) { 2197 // Use object instead of the arguments when posting message to remove pending requests. 2198 Integer userIdInteger = mIntegerCache.get(userId); 2199 if (userIdInteger == null) { 2200 userIdInteger = Integer.valueOf(userId); 2201 mIntegerCache.put(userId, userIdInteger); 2202 } 2203 removeMessages(MSG_SESSIONS_CHANGED, userIdInteger); 2204 obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget(); 2205 } 2206 } 2207 2208 private class ControllerCallback extends MediaController2.ControllerCallback { 2209 2210 private final SessionToken2 mToken; 2211 2212 ControllerCallback(SessionToken2 token) { 2213 mToken = token; 2214 } 2215 2216 @Override 2217 public void onDisconnected(MediaController2 controller) { 2218 destroySession2Internal(mToken); 2219 } 2220 }; 2221 2222 private final class SessionTokensListenerRecord implements IBinder.DeathRecipient { 2223 private final ISessionTokensListener mListener; 2224 private final int mUserId; 2225 2226 public SessionTokensListenerRecord(ISessionTokensListener listener, int userId) { 2227 mListener = listener; 2228 // TODO(jaewan): should userId be mapped through mFullUserIds? (b/73597722) 2229 mUserId = userId; 2230 } 2231 2232 @Override 2233 public void binderDied() { 2234 synchronized (mLock) { 2235 mSessionTokensListeners.remove(this); 2236 } 2237 } 2238 } 2239 2240 private void postSessionTokensUpdated(int userId) { 2241 mHandler.obtainMessage(MessageHandler.MSG_SESSIONS_TOKENS_CHANGED, userId).sendToTarget(); 2242 } 2243 2244 private void pushSessionTokensChanged(int userId) { 2245 synchronized (mLock) { 2246 List<Bundle> tokens = new ArrayList<>(); 2247 for (SessionToken2 token : mSessionRecords.keySet()) { 2248 // TODO(jaewan): Remove the check for UserHandle.USER_ALL (shouldn't happen). 2249 // This happens when called form buildMediaSessionService2List(...). 2250 // (b/73760382) 2251 if (UserHandle.getUserId(token.getUid()) == userId 2252 || UserHandle.USER_ALL == userId) { 2253 tokens.add(token.toBundle()); 2254 } 2255 } 2256 2257 for (SessionTokensListenerRecord record : mSessionTokensListeners) { 2258 // TODO(jaewan): Should userId be mapped through mFullUserIds? (b/73760382) 2259 if (record.mUserId == userId || record.mUserId == UserHandle.USER_ALL) { 2260 try { 2261 record.mListener.onSessionTokensChanged(tokens); 2262 } catch (RemoteException e) { 2263 Log.w(TAG, "Failed to notify session tokens changed", e); 2264 } 2265 } 2266 } 2267 } 2268 } 2269 2270 private boolean addSessionRecordLocked(SessionToken2 token) { 2271 return addSessionRecordLocked(token, null); 2272 } 2273 2274 private boolean addSessionRecordLocked(SessionToken2 token, MediaController2 controller) { 2275 if (mSessionRecords.containsKey(token) && mSessionRecords.get(token) == controller) { 2276 // The key/value pair already exists, no need to update. 2277 return false; 2278 } 2279 2280 mSessionRecords.put(token, controller); 2281 return true; 2282 } 2283 2284 private boolean removeSessionRecordLocked(SessionToken2 token) { 2285 if (!mSessionRecords.containsKey(token)) { 2286 // The key is already removed, no need to remove. 2287 return false; 2288 } 2289 2290 mSessionRecords.remove(token); 2291 return true; 2292 } 2293 } 2294