MediaSessionService.java revision 6f0e4ddd66fcdcc13944d8970d0b560e2626508b
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 17package com.android.server.media; 18 19import android.Manifest; 20import android.app.Activity; 21import android.app.ActivityManager; 22import android.app.KeyguardManager; 23import android.content.ActivityNotFoundException; 24import android.content.BroadcastReceiver; 25import android.content.ComponentName; 26import android.content.ContentResolver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.pm.PackageManager; 30import android.media.AudioManager; 31import android.media.IAudioService; 32import android.media.routeprovider.RouteRequest; 33import android.media.session.IActiveSessionsListener; 34import android.media.session.ISession; 35import android.media.session.ISessionCallback; 36import android.media.session.ISessionManager; 37import android.media.session.MediaSessionToken; 38import android.media.session.RouteInfo; 39import android.media.session.RouteOptions; 40import android.media.session.MediaSession; 41import android.os.Binder; 42import android.os.Bundle; 43import android.os.Handler; 44import android.os.IBinder; 45import android.os.Message; 46import android.os.PowerManager; 47import android.os.RemoteException; 48import android.os.ResultReceiver; 49import android.os.ServiceManager; 50import android.os.UserHandle; 51import android.provider.Settings; 52import android.speech.RecognizerIntent; 53import android.text.TextUtils; 54import android.util.Log; 55import android.util.SparseArray; 56import android.view.KeyEvent; 57 58import com.android.server.SystemService; 59import com.android.server.Watchdog; 60import com.android.server.Watchdog.Monitor; 61 62import java.io.FileDescriptor; 63import java.io.PrintWriter; 64import java.util.ArrayList; 65import java.util.List; 66 67/** 68 * System implementation of MediaSessionManager 69 */ 70public class MediaSessionService extends SystemService implements Monitor { 71 private static final String TAG = "MediaSessionService"; 72 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 73 74 private static final int WAKELOCK_TIMEOUT = 5000; 75 76 private final SessionManagerImpl mSessionManagerImpl; 77 // private final MediaRouteProviderWatcher mRouteProviderWatcher; 78 private final MediaSessionStack mPriorityStack; 79 80 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>(); 81 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>(); 82 private final ArrayList<SessionsListenerRecord> mSessionsListeners 83 = new ArrayList<SessionsListenerRecord>(); 84 // private final ArrayList<MediaRouteProviderProxy> mProviders 85 // = new ArrayList<MediaRouteProviderProxy>(); 86 private final Object mLock = new Object(); 87 private final MessageHandler mHandler = new MessageHandler(); 88 private final PowerManager.WakeLock mMediaEventWakeLock; 89 90 private KeyguardManager mKeyguardManager; 91 private IAudioService mAudioService; 92 private ContentResolver mContentResolver; 93 94 private MediaSessionRecord mPrioritySession; 95 private int mCurrentUserId = -1; 96 97 // Used to keep track of the current request to show routes for a specific 98 // session so we drop late callbacks properly. 99 private int mShowRoutesRequestId = 0; 100 101 // TODO refactor to have per user state for providers. See 102 // MediaRouterService for an example 103 104 public MediaSessionService(Context context) { 105 super(context); 106 mSessionManagerImpl = new SessionManagerImpl(); 107 mPriorityStack = new MediaSessionStack(); 108 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 109 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); 110 } 111 112 @Override 113 public void onStart() { 114 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); 115 Watchdog.getInstance().addMonitor(this); 116 updateUser(); 117 mKeyguardManager = 118 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); 119 mAudioService = getAudioService(); 120 mContentResolver = getContext().getContentResolver(); 121 } 122 123 private IAudioService getAudioService() { 124 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 125 return IAudioService.Stub.asInterface(b); 126 } 127 128 /** 129 * Should trigger showing the Media route picker dialog. Right now it just 130 * kicks off a query to all the providers to get routes. 131 * 132 * @param record The session to show the picker for. 133 */ 134 public void showRoutePickerForSession(MediaSessionRecord record) { 135 // TODO for now just toggle the route to test (we will only have one 136 // match for now) 137 synchronized (mLock) { 138 if (!mAllSessions.contains(record)) { 139 Log.d(TAG, "Unknown session tried to show route picker. Ignoring."); 140 return; 141 } 142 RouteInfo current = record.getRoute(); 143 UserRecord user = mUserRecords.get(record.getUserId()); 144 if (current != null) { 145 // For now send null to mean the local route 146 MediaRouteProviderProxy proxy = user.getProviderLocked(current.getProvider()); 147 if (proxy != null) { 148 proxy.removeSession(record); 149 } 150 record.selectRoute(null); 151 return; 152 } 153 ArrayList<MediaRouteProviderProxy> providers = user.getProvidersLocked(); 154 mShowRoutesRequestId++; 155 for (int i = providers.size() - 1; i >= 0; i--) { 156 MediaRouteProviderProxy provider = providers.get(i); 157 provider.getRoutes(record, mShowRoutesRequestId); 158 } 159 } 160 } 161 162 /** 163 * Connect a session to the given route. 164 * 165 * @param session The session to connect. 166 * @param route The route to connect to. 167 * @param options The options to use for the connection. 168 */ 169 public void connectToRoute(MediaSessionRecord session, RouteInfo route, 170 RouteOptions options) { 171 synchronized (mLock) { 172 if (!mAllSessions.contains(session)) { 173 Log.d(TAG, "Unknown session attempting to connect to route. Ignoring"); 174 return; 175 } 176 UserRecord user = mUserRecords.get(session.getUserId()); 177 if (user == null) { 178 Log.wtf(TAG, "connectToRoute: User " + session.getUserId() + " does not exist."); 179 return; 180 } 181 MediaRouteProviderProxy proxy = user.getProviderLocked(route.getProvider()); 182 if (proxy == null) { 183 Log.w(TAG, "Provider for route " + route.getName() + " does not exist."); 184 return; 185 } 186 RouteRequest request = new RouteRequest(session.getSessionInfo(), options, true); 187 proxy.connectToRoute(session, route, request); 188 } 189 } 190 191 public void updateSession(MediaSessionRecord record) { 192 synchronized (mLock) { 193 if (!mAllSessions.contains(record)) { 194 Log.d(TAG, "Unknown session updated. Ignoring."); 195 return; 196 } 197 mPriorityStack.onSessionStateChange(record); 198 if (record.isSystemPriority()) { 199 if (record.isActive()) { 200 if (mPrioritySession != null) { 201 Log.w(TAG, "Replacing existing priority session with a new session"); 202 } 203 mPrioritySession = record; 204 } else { 205 if (mPrioritySession == record) { 206 mPrioritySession = null; 207 } 208 } 209 } 210 } 211 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); 212 } 213 214 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { 215 boolean updateSessions = false; 216 synchronized (mLock) { 217 if (!mAllSessions.contains(record)) { 218 Log.d(TAG, "Unknown session changed playback state. Ignoring."); 219 return; 220 } 221 updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState); 222 } 223 if (updateSessions) { 224 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); 225 } 226 } 227 228 @Override 229 public void onStartUser(int userHandle) { 230 updateUser(); 231 } 232 233 @Override 234 public void onSwitchUser(int userHandle) { 235 updateUser(); 236 } 237 238 @Override 239 public void onStopUser(int userHandle) { 240 synchronized (mLock) { 241 UserRecord user = mUserRecords.get(userHandle); 242 if (user != null) { 243 destroyUserLocked(user); 244 } 245 } 246 } 247 248 @Override 249 public void monitor() { 250 synchronized (mLock) { 251 // Check for deadlock 252 } 253 } 254 255 protected void enforcePhoneStatePermission(int pid, int uid) { 256 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) 257 != PackageManager.PERMISSION_GRANTED) { 258 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); 259 } 260 } 261 262 void sessionDied(MediaSessionRecord session) { 263 synchronized (mLock) { 264 destroySessionLocked(session); 265 } 266 } 267 268 void destroySession(MediaSessionRecord session) { 269 synchronized (mLock) { 270 destroySessionLocked(session); 271 } 272 } 273 274 private void updateUser() { 275 synchronized (mLock) { 276 int userId = ActivityManager.getCurrentUser(); 277 if (mCurrentUserId != userId) { 278 final int oldUserId = mCurrentUserId; 279 mCurrentUserId = userId; // do this first 280 281 UserRecord oldUser = mUserRecords.get(oldUserId); 282 if (oldUser != null) { 283 oldUser.stopLocked(); 284 } 285 286 UserRecord newUser = getOrCreateUser(userId); 287 newUser.startLocked(); 288 } 289 } 290 } 291 292 /** 293 * Stop the user and unbind from everything. 294 * 295 * @param user The user to dispose of 296 */ 297 private void destroyUserLocked(UserRecord user) { 298 user.stopLocked(); 299 user.destroyLocked(); 300 mUserRecords.remove(user.mUserId); 301 } 302 303 /* 304 * When a session is removed several things need to happen. 305 * 1. We need to remove it from the relevant user. 306 * 2. We need to remove it from the priority stack. 307 * 3. We need to remove it from all sessions. 308 * 4. If this is the system priority session we need to clear it. 309 * 5. We need to unlink to death from the cb binder 310 * 6. We need to tell the session to do any final cleanup (onDestroy) 311 */ 312 private void destroySessionLocked(MediaSessionRecord session) { 313 int userId = session.getUserId(); 314 UserRecord user = mUserRecords.get(userId); 315 if (user != null) { 316 user.removeSessionLocked(session); 317 } 318 319 mPriorityStack.removeSession(session); 320 mAllSessions.remove(session); 321 if (session == mPrioritySession) { 322 mPrioritySession = null; 323 } 324 325 try { 326 session.getCallback().asBinder().unlinkToDeath(session, 0); 327 } catch (Exception e) { 328 // ignore exceptions while destroying a session. 329 } 330 session.onDestroy(); 331 332 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0); 333 } 334 335 private void enforcePackageName(String packageName, int uid) { 336 if (TextUtils.isEmpty(packageName)) { 337 throw new IllegalArgumentException("packageName may not be empty"); 338 } 339 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 340 final int packageCount = packages.length; 341 for (int i = 0; i < packageCount; i++) { 342 if (packageName.equals(packages[i])) { 343 return; 344 } 345 } 346 throw new IllegalArgumentException("packageName is not owned by the calling process"); 347 } 348 349 /** 350 * Checks a caller's authorization to register an IRemoteControlDisplay. 351 * Authorization is granted if one of the following is true: 352 * <ul> 353 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL 354 * permission</li> 355 * <li>the caller's listener is one of the enabled notification listeners 356 * for the caller's user</li> 357 * </ul> 358 */ 359 private void enforceMediaPermissions(ComponentName compName, int pid, int uid, 360 int resolvedUserId) { 361 if (getContext() 362 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) 363 != PackageManager.PERMISSION_GRANTED 364 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), 365 resolvedUserId)) { 366 throw new SecurityException("Missing permission to control media."); 367 } 368 } 369 370 /** 371 * This checks if the component is an enabled notification listener for the 372 * specified user. Enabled components may only operate on behalf of the user 373 * they're running as. 374 * 375 * @param compName The component that is enabled. 376 * @param userId The user id of the caller. 377 * @param forUserId The user id they're making the request on behalf of. 378 * @return True if the component is enabled, false otherwise 379 */ 380 private boolean isEnabledNotificationListener(ComponentName compName, int userId, 381 int forUserId) { 382 if (userId != forUserId) { 383 // You may not access another user's content as an enabled listener. 384 return false; 385 } 386 if (compName != null) { 387 final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver, 388 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, 389 userId); 390 if (enabledNotifListeners != null) { 391 final String[] components = enabledNotifListeners.split(":"); 392 for (int i = 0; i < components.length; i++) { 393 final ComponentName component = 394 ComponentName.unflattenFromString(components[i]); 395 if (component != null) { 396 if (compName.equals(component)) { 397 if (DEBUG) { 398 Log.d(TAG, "ok to get sessions: " + component + 399 " is authorized notification listener"); 400 } 401 return true; 402 } 403 } 404 } 405 } 406 if (DEBUG) { 407 Log.d(TAG, "not ok to get sessions, " + compName + 408 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId); 409 } 410 } 411 return false; 412 } 413 414 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, 415 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException { 416 synchronized (mLock) { 417 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); 418 } 419 } 420 421 /* 422 * When a session is created the following things need to happen. 423 * 1. Its callback binder needs a link to death 424 * 2. It needs to be added to all sessions. 425 * 3. It needs to be added to the priority stack. 426 * 4. It needs to be added to the relevant user record. 427 */ 428 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, 429 String callerPackageName, ISessionCallback cb, String tag) { 430 431 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, 432 callerPackageName, cb, tag, this, mHandler); 433 try { 434 cb.asBinder().linkToDeath(session, 0); 435 } catch (RemoteException e) { 436 throw new RuntimeException("Media Session owner died prematurely.", e); 437 } 438 439 mAllSessions.add(session); 440 mPriorityStack.addSession(session); 441 442 UserRecord user = getOrCreateUser(userId); 443 user.addSessionLocked(session); 444 445 mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0); 446 447 if (DEBUG) { 448 Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag); 449 } 450 return session; 451 } 452 453 private UserRecord getOrCreateUser(int userId) { 454 UserRecord user = mUserRecords.get(userId); 455 if (user == null) { 456 user = new UserRecord(getContext(), userId); 457 mUserRecords.put(userId, user); 458 } 459 return user; 460 } 461 462 private int findIndexOfSessionForIdLocked(String sessionId) { 463 for (int i = mAllSessions.size() - 1; i >= 0; i--) { 464 MediaSessionRecord session = mAllSessions.get(i); 465 if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) { 466 return i; 467 } 468 } 469 return -1; 470 } 471 472 private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { 473 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 474 if (mSessionsListeners.get(i).mListener == listener) { 475 return i; 476 } 477 } 478 return -1; 479 } 480 481 private boolean isSessionDiscoverable(MediaSessionRecord record) { 482 // TODO probably want to check more than if it's active. 483 return record.isActive(); 484 } 485 486 private void pushSessionsChanged(int userId) { 487 synchronized (mLock) { 488 List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId); 489 int size = records.size(); 490 if (size > 0) { 491 persistMediaButtonReceiverLocked(records.get(0)); 492 } 493 ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>(); 494 for (int i = 0; i < size; i++) { 495 tokens.add(new MediaSessionToken(records.get(i).getControllerBinder())); 496 } 497 for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { 498 SessionsListenerRecord record = mSessionsListeners.get(i); 499 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { 500 try { 501 record.mListener.onActiveSessionsChanged(tokens); 502 } catch (RemoteException e) { 503 Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", 504 e); 505 mSessionsListeners.remove(i); 506 } 507 } 508 } 509 } 510 } 511 512 private void persistMediaButtonReceiverLocked(MediaSessionRecord record) { 513 ComponentName receiver = record.getMediaButtonReceiver(); 514 if (receiver != null) { 515 Settings.System.putStringForUser(mContentResolver, 516 Settings.System.MEDIA_BUTTON_RECEIVER, 517 receiver == null ? "" : receiver.flattenToString(), 518 UserHandle.USER_CURRENT); 519 } 520 } 521 522 private MediaRouteProviderProxy.RoutesListener mRoutesCallback 523 = new MediaRouteProviderProxy.RoutesListener() { 524 @Override 525 public void onRoutesUpdated(String sessionId, ArrayList<RouteInfo> routes, 526 int reqId) { 527 // TODO for now select the first route to test, eventually add the 528 // new routes to the dialog if it is still open 529 synchronized (mLock) { 530 int index = findIndexOfSessionForIdLocked(sessionId); 531 if (index != -1 && routes != null && routes.size() > 0) { 532 MediaSessionRecord record = mAllSessions.get(index); 533 RouteInfo route = routes.get(0); 534 record.selectRoute(route); 535 UserRecord user = mUserRecords.get(record.getUserId()); 536 MediaRouteProviderProxy provider = user.getProviderLocked(route.getProvider()); 537 provider.addSession(record); 538 } 539 } 540 } 541 542 @Override 543 public void onRouteConnected(String sessionId, RouteInfo route, 544 RouteRequest options, RouteConnectionRecord connection) { 545 synchronized (mLock) { 546 int index = findIndexOfSessionForIdLocked(sessionId); 547 if (index != -1) { 548 MediaSessionRecord session = mAllSessions.get(index); 549 session.setRouteConnected(route, options.getConnectionOptions(), connection); 550 } 551 } 552 } 553 }; 554 555 /** 556 * Information about a particular user. The contents of this object is 557 * guarded by mLock. 558 */ 559 final class UserRecord { 560 private final int mUserId; 561 private final MediaRouteProviderWatcher mRouteProviderWatcher; 562 private final ArrayList<MediaRouteProviderProxy> mProviders 563 = new ArrayList<MediaRouteProviderProxy>(); 564 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); 565 566 public UserRecord(Context context, int userId) { 567 mUserId = userId; 568 mRouteProviderWatcher = new MediaRouteProviderWatcher(context, 569 mProviderWatcherCallback, mHandler, userId); 570 } 571 572 public void startLocked() { 573 mRouteProviderWatcher.start(); 574 } 575 576 public void stopLocked() { 577 mRouteProviderWatcher.stop(); 578 updateInterestLocked(); 579 } 580 581 public void destroyLocked() { 582 for (int i = mSessions.size() - 1; i >= 0; i--) { 583 MediaSessionRecord session = mSessions.get(i); 584 MediaSessionService.this.destroySessionLocked(session); 585 if (session.isConnected()) { 586 session.disconnect(MediaSession.DISCONNECT_REASON_USER_STOPPING); 587 } 588 } 589 } 590 591 public ArrayList<MediaRouteProviderProxy> getProvidersLocked() { 592 return mProviders; 593 } 594 595 public ArrayList<MediaSessionRecord> getSessionsLocked() { 596 return mSessions; 597 } 598 599 public void addSessionLocked(MediaSessionRecord session) { 600 mSessions.add(session); 601 updateInterestLocked(); 602 } 603 604 public void removeSessionLocked(MediaSessionRecord session) { 605 mSessions.remove(session); 606 RouteInfo route = session.getRoute(); 607 if (route != null) { 608 MediaRouteProviderProxy provider = getProviderLocked(route.getProvider()); 609 if (provider != null) { 610 provider.removeSession(session); 611 } 612 } 613 updateInterestLocked(); 614 } 615 616 public void dumpLocked(PrintWriter pw, String prefix) { 617 pw.println(prefix + "Record for user " + mUserId); 618 String indent = prefix + " "; 619 int size = mProviders.size(); 620 pw.println(indent + size + " Providers:"); 621 for (int i = 0; i < size; i++) { 622 mProviders.get(i).dump(pw, indent); 623 } 624 pw.println(); 625 size = mSessions.size(); 626 pw.println(indent + size + " Sessions:"); 627 for (int i = 0; i < size; i++) { 628 // Just print the session info, the full session dump will 629 // already be in the list of all sessions. 630 pw.println(indent + mSessions.get(i).getSessionInfo()); 631 } 632 } 633 634 public void updateInterestLocked() { 635 // TODO go through the sessions and build up the set of interfaces 636 // we're interested in. Update the provider watcher. 637 // For now, just express interest in all providers for the current 638 // user 639 boolean interested = mUserId == mCurrentUserId; 640 for (int i = mProviders.size() - 1; i >= 0; i--) { 641 mProviders.get(i).setInterested(interested); 642 } 643 } 644 645 private MediaRouteProviderProxy getProviderLocked(String providerId) { 646 for (int i = mProviders.size() - 1; i >= 0; i--) { 647 MediaRouteProviderProxy provider = mProviders.get(i); 648 if (TextUtils.equals(providerId, provider.getId())) { 649 return provider; 650 } 651 } 652 return null; 653 } 654 655 private MediaRouteProviderWatcher.Callback mProviderWatcherCallback 656 = new MediaRouteProviderWatcher.Callback() { 657 @Override 658 public void removeProvider(MediaRouteProviderProxy provider) { 659 synchronized (mLock) { 660 mProviders.remove(provider); 661 provider.setRoutesListener(null); 662 provider.setInterested(false); 663 } 664 } 665 666 @Override 667 public void addProvider(MediaRouteProviderProxy provider) { 668 synchronized (mLock) { 669 mProviders.add(provider); 670 provider.setRoutesListener(mRoutesCallback); 671 provider.setInterested(true); 672 } 673 } 674 }; 675 } 676 677 final class SessionsListenerRecord implements IBinder.DeathRecipient { 678 private final IActiveSessionsListener mListener; 679 private final int mUserId; 680 681 public SessionsListenerRecord(IActiveSessionsListener listener, int userId) { 682 mListener = listener; 683 mUserId = userId; 684 } 685 686 @Override 687 public void binderDied() { 688 synchronized (mLock) { 689 mSessionsListeners.remove(this); 690 } 691 } 692 } 693 694 class SessionManagerImpl extends ISessionManager.Stub { 695 private static final String EXTRA_WAKELOCK_ACQUIRED = 696 "android.media.AudioService.WAKELOCK_ACQUIRED"; 697 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number 698 699 private boolean mVoiceButtonDown = false; 700 private boolean mVoiceButtonHandled = false; 701 702 @Override 703 public ISession createSession(String packageName, ISessionCallback cb, String tag, 704 int userId) throws RemoteException { 705 final int pid = Binder.getCallingPid(); 706 final int uid = Binder.getCallingUid(); 707 final long token = Binder.clearCallingIdentity(); 708 try { 709 enforcePackageName(packageName, uid); 710 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 711 false /* allowAll */, true /* requireFull */, "createSession", packageName); 712 if (cb == null) { 713 throw new IllegalArgumentException("Controller callback cannot be null"); 714 } 715 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) 716 .getSessionBinder(); 717 } finally { 718 Binder.restoreCallingIdentity(token); 719 } 720 } 721 722 @Override 723 public List<IBinder> getSessions(ComponentName componentName, int userId) { 724 final int pid = Binder.getCallingPid(); 725 final int uid = Binder.getCallingUid(); 726 final long token = Binder.clearCallingIdentity(); 727 728 try { 729 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 730 ArrayList<IBinder> binders = new ArrayList<IBinder>(); 731 synchronized (mLock) { 732 ArrayList<MediaSessionRecord> records = mPriorityStack 733 .getActiveSessions(resolvedUserId); 734 int size = records.size(); 735 for (int i = 0; i < size; i++) { 736 binders.add(records.get(i).getControllerBinder().asBinder()); 737 } 738 } 739 return binders; 740 } finally { 741 Binder.restoreCallingIdentity(token); 742 } 743 } 744 745 @Override 746 public void addSessionsListener(IActiveSessionsListener listener, 747 ComponentName componentName, int userId) throws RemoteException { 748 final int pid = Binder.getCallingPid(); 749 final int uid = Binder.getCallingUid(); 750 final long token = Binder.clearCallingIdentity(); 751 752 try { 753 int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); 754 synchronized (mLock) { 755 int index = findIndexOfSessionsListenerLocked(listener); 756 if (index != -1) { 757 Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); 758 return; 759 } 760 SessionsListenerRecord record = new SessionsListenerRecord(listener, 761 resolvedUserId); 762 try { 763 listener.asBinder().linkToDeath(record, 0); 764 } catch (RemoteException e) { 765 Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); 766 return; 767 } 768 mSessionsListeners.add(record); 769 } 770 } finally { 771 Binder.restoreCallingIdentity(token); 772 } 773 } 774 775 @Override 776 public void removeSessionsListener(IActiveSessionsListener listener) 777 throws RemoteException { 778 synchronized (mLock) { 779 int index = findIndexOfSessionsListenerLocked(listener); 780 if (index != -1) { 781 SessionsListenerRecord record = mSessionsListeners.remove(index); 782 try { 783 record.mListener.asBinder().unlinkToDeath(record, 0); 784 } catch (Exception e) { 785 // ignore exceptions, the record is being removed 786 } 787 } 788 } 789 } 790 791 /** 792 * Handles the dispatching of the media button events to one of the 793 * registered listeners, or if there was none, broadcast an 794 * ACTION_MEDIA_BUTTON intent to the rest of the system. 795 * 796 * @param keyEvent a non-null KeyEvent whose key code is one of the 797 * supported media buttons 798 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held 799 * while this key event is dispatched. 800 */ 801 @Override 802 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 803 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) { 804 Log.w(TAG, "Attempted to dispatch null or non-media key event."); 805 return; 806 } 807 final int pid = Binder.getCallingPid(); 808 final int uid = Binder.getCallingUid(); 809 final long token = Binder.clearCallingIdentity(); 810 811 try { 812 synchronized (mLock) { 813 MediaSessionRecord session = mPriorityStack 814 .getDefaultMediaButtonSession(mCurrentUserId); 815 if (isVoiceKey(keyEvent.getKeyCode())) { 816 handleVoiceKeyEventLocked(keyEvent, needWakeLock, session); 817 } else { 818 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); 819 } 820 } 821 } finally { 822 Binder.restoreCallingIdentity(token); 823 } 824 } 825 826 @Override 827 public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags) 828 throws RemoteException { 829 final int pid = Binder.getCallingPid(); 830 final int uid = Binder.getCallingUid(); 831 final long token = Binder.clearCallingIdentity(); 832 try { 833 synchronized (mLock) { 834 MediaSessionRecord session = mPriorityStack 835 .getDefaultVolumeSession(mCurrentUserId); 836 dispatchAdjustVolumeByLocked(suggestedStream, delta, flags, session); 837 } 838 } finally { 839 Binder.restoreCallingIdentity(token); 840 } 841 } 842 843 @Override 844 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 845 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP) 846 != PackageManager.PERMISSION_GRANTED) { 847 pw.println("Permission Denial: can't dump MediaSessionService from from pid=" 848 + Binder.getCallingPid() 849 + ", uid=" + Binder.getCallingUid()); 850 return; 851 } 852 853 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); 854 pw.println(); 855 856 synchronized (mLock) { 857 pw.println("Session for calls:" + mPrioritySession); 858 if (mPrioritySession != null) { 859 mPrioritySession.dump(pw, ""); 860 } 861 int count = mAllSessions.size(); 862 pw.println(count + " Sessions:"); 863 for (int i = 0; i < count; i++) { 864 mAllSessions.get(i).dump(pw, ""); 865 pw.println(); 866 } 867 mPriorityStack.dump(pw, ""); 868 869 pw.println("User Records:"); 870 count = mUserRecords.size(); 871 for (int i = 0; i < count; i++) { 872 UserRecord user = mUserRecords.get(i); 873 user.dumpLocked(pw, ""); 874 } 875 } 876 } 877 878 private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, 879 final int uid) { 880 String packageName = null; 881 if (componentName != null) { 882 // If they gave us a component name verify they own the 883 // package 884 packageName = componentName.getPackageName(); 885 enforcePackageName(packageName, uid); 886 } 887 // Check that they can make calls on behalf of the user and 888 // get the final user id 889 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 890 true /* allowAll */, true /* requireFull */, "getSessions", packageName); 891 // Check if they have the permissions or their component is 892 // enabled for the user they're calling from. 893 enforceMediaPermissions(componentName, pid, uid, resolvedUserId); 894 return resolvedUserId; 895 } 896 897 private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags, 898 MediaSessionRecord session) { 899 int direction = 0; 900 int steps = delta; 901 if (delta > 0) { 902 direction = 1; 903 } else if (delta < 0) { 904 direction = -1; 905 steps = -delta; 906 } 907 if (DEBUG) { 908 String sessionInfo = session == null ? null : session.getSessionInfo().toString(); 909 Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags 910 + ", suggestedStream=" + suggestedStream); 911 912 } 913 if (session == null) { 914 try { 915 if (delta == 0) { 916 mAudioService.adjustSuggestedStreamVolume(delta, suggestedStream, flags, 917 getContext().getOpPackageName()); 918 } else { 919 for (int i = 0; i < steps; i++) { 920 mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, 921 flags, getContext().getOpPackageName()); 922 } 923 } 924 } catch (RemoteException e) { 925 Log.e(TAG, "Error adjusting default volume.", e); 926 } 927 } else { 928 if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) { 929 try { 930 if (delta == 0) { 931 mAudioService.adjustSuggestedStreamVolume(delta, 932 session.getAudioStream(), flags, 933 getContext().getOpPackageName()); 934 } else { 935 for (int i = 0; i < steps; i++) { 936 mAudioService.adjustSuggestedStreamVolume(direction, 937 session.getAudioStream(), flags, 938 getContext().getOpPackageName()); 939 } 940 } 941 } catch (RemoteException e) { 942 Log.e(TAG, "Error adjusting volume for stream " 943 + session.getAudioStream(), e); 944 } 945 } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) { 946 session.adjustVolumeBy(delta); 947 } 948 } 949 } 950 951 private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, 952 MediaSessionRecord session) { 953 if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) { 954 // If the phone app has priority just give it the event 955 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); 956 return; 957 } 958 int action = keyEvent.getAction(); 959 boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; 960 if (action == KeyEvent.ACTION_DOWN) { 961 if (keyEvent.getRepeatCount() == 0) { 962 mVoiceButtonDown = true; 963 mVoiceButtonHandled = false; 964 } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) { 965 mVoiceButtonHandled = true; 966 startVoiceInput(needWakeLock); 967 } 968 } else if (action == KeyEvent.ACTION_UP) { 969 if (mVoiceButtonDown) { 970 mVoiceButtonDown = false; 971 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { 972 // Resend the down then send this event through 973 KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); 974 dispatchMediaKeyEventLocked(downEvent, needWakeLock, session); 975 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); 976 } 977 } 978 } 979 } 980 981 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, 982 MediaSessionRecord session) { 983 if (session != null) { 984 if (DEBUG) { 985 Log.d(TAG, "Sending media key to " + session.getSessionInfo()); 986 } 987 if (needWakeLock) { 988 mKeyEventReceiver.aquireWakeLockLocked(); 989 } 990 // If we don't need a wakelock use -1 as the id so we 991 // won't release it later 992 session.sendMediaButton(keyEvent, 993 needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, 994 mKeyEventReceiver); 995 } else { 996 if (needWakeLock) { 997 mMediaEventWakeLock.acquire(); 998 } 999 if (DEBUG) { 1000 Log.d(TAG, "Sending media key ordered broadcast"); 1001 } 1002 // Fallback to legacy behavior 1003 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 1004 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 1005 if (needWakeLock) { 1006 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, 1007 WAKELOCK_RELEASE_ON_FINISHED); 1008 } 1009 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL, 1010 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null); 1011 } 1012 } 1013 1014 private void startVoiceInput(boolean needWakeLock) { 1015 Intent voiceIntent = null; 1016 // select which type of search to launch: 1017 // - screen on and device unlocked: action is ACTION_WEB_SEARCH 1018 // - device locked or screen off: action is 1019 // ACTION_VOICE_SEARCH_HANDS_FREE 1020 // with EXTRA_SECURE set to true if the device is securely locked 1021 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 1022 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); 1023 if (!isLocked && pm.isScreenOn()) { 1024 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); 1025 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH"); 1026 } else { 1027 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); 1028 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, 1029 isLocked && mKeyguardManager.isKeyguardSecure()); 1030 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE"); 1031 } 1032 // start the search activity 1033 if (needWakeLock) { 1034 mMediaEventWakeLock.acquire(); 1035 } 1036 try { 1037 if (voiceIntent != null) { 1038 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1039 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 1040 getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT); 1041 } 1042 } catch (ActivityNotFoundException e) { 1043 Log.w(TAG, "No activity for search: " + e); 1044 } finally { 1045 if (needWakeLock) { 1046 mMediaEventWakeLock.release(); 1047 } 1048 } 1049 } 1050 1051 private boolean isVoiceKey(int keyCode) { 1052 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK; 1053 } 1054 1055 private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); 1056 1057 class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable { 1058 private final Handler mHandler; 1059 private int mRefCount = 0; 1060 private int mLastTimeoutId = 0; 1061 1062 public KeyEventWakeLockReceiver(Handler handler) { 1063 super(handler); 1064 mHandler = handler; 1065 } 1066 1067 public void onTimeout() { 1068 synchronized (mLock) { 1069 if (mRefCount == 0) { 1070 // We've already released it, so just return 1071 return; 1072 } 1073 mLastTimeoutId++; 1074 mRefCount = 0; 1075 releaseWakeLockLocked(); 1076 } 1077 } 1078 1079 public void aquireWakeLockLocked() { 1080 if (mRefCount == 0) { 1081 mMediaEventWakeLock.acquire(); 1082 } 1083 mRefCount++; 1084 mHandler.removeCallbacks(this); 1085 mHandler.postDelayed(this, WAKELOCK_TIMEOUT); 1086 1087 } 1088 1089 @Override 1090 public void run() { 1091 onTimeout(); 1092 } 1093 1094 @Override 1095 protected void onReceiveResult(int resultCode, Bundle resultData) { 1096 if (resultCode < mLastTimeoutId) { 1097 // Ignore results from calls that were before the last 1098 // timeout, just in case. 1099 return; 1100 } else { 1101 synchronized (mLock) { 1102 if (mRefCount > 0) { 1103 mRefCount--; 1104 if (mRefCount == 0) { 1105 releaseWakeLockLocked(); 1106 } 1107 } 1108 } 1109 } 1110 } 1111 1112 private void releaseWakeLockLocked() { 1113 mMediaEventWakeLock.release(); 1114 mHandler.removeCallbacks(this); 1115 } 1116 }; 1117 1118 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { 1119 @Override 1120 public void onReceive(Context context, Intent intent) { 1121 if (intent == null) { 1122 return; 1123 } 1124 Bundle extras = intent.getExtras(); 1125 if (extras == null) { 1126 return; 1127 } 1128 synchronized (mLock) { 1129 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED) 1130 && mMediaEventWakeLock.isHeld()) { 1131 mMediaEventWakeLock.release(); 1132 } 1133 } 1134 } 1135 }; 1136 } 1137 1138 final class MessageHandler extends Handler { 1139 private static final int MSG_SESSIONS_CHANGED = 1; 1140 1141 @Override 1142 public void handleMessage(Message msg) { 1143 switch (msg.what) { 1144 case MSG_SESSIONS_CHANGED: 1145 pushSessionsChanged(msg.arg1); 1146 break; 1147 } 1148 } 1149 1150 public void post(int what, int arg1, int arg2) { 1151 obtainMessage(what, arg1, arg2).sendToTarget(); 1152 } 1153 } 1154} 1155