MediaSessionService.java revision 42ea7eecd149161ed192d3029f0d77d1d08a4aa5
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.content.BroadcastReceiver; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.content.pm.PackageManager; 27import android.media.routeprovider.RouteRequest; 28import android.media.session.ISession; 29import android.media.session.ISessionCallback; 30import android.media.session.ISessionManager; 31import android.media.session.RouteInfo; 32import android.media.session.RouteOptions; 33import android.media.session.MediaSession; 34import android.os.Binder; 35import android.os.Bundle; 36import android.os.Handler; 37import android.os.IBinder; 38import android.os.PowerManager; 39import android.os.RemoteException; 40import android.os.ResultReceiver; 41import android.os.UserHandle; 42import android.provider.Settings; 43import android.text.TextUtils; 44import android.util.Log; 45import android.util.SparseArray; 46import android.view.KeyEvent; 47 48import com.android.server.SystemService; 49import com.android.server.Watchdog; 50import com.android.server.Watchdog.Monitor; 51 52import java.io.FileDescriptor; 53import java.io.PrintWriter; 54import java.util.ArrayList; 55import java.util.List; 56 57/** 58 * System implementation of MediaSessionManager 59 */ 60public class MediaSessionService extends SystemService implements Monitor { 61 private static final String TAG = "MediaSessionService"; 62 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 63 64 private final SessionManagerImpl mSessionManagerImpl; 65 // private final MediaRouteProviderWatcher mRouteProviderWatcher; 66 private final MediaSessionStack mPriorityStack; 67 68 private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>(); 69 private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>(); 70 // private final ArrayList<MediaRouteProviderProxy> mProviders 71 // = new ArrayList<MediaRouteProviderProxy>(); 72 private final Object mLock = new Object(); 73 private final Handler mHandler = new Handler(); 74 private final PowerManager.WakeLock mMediaEventWakeLock; 75 76 private MediaSessionRecord mPrioritySession; 77 private int mCurrentUserId = -1; 78 79 // Used to keep track of the current request to show routes for a specific 80 // session so we drop late callbacks properly. 81 private int mShowRoutesRequestId = 0; 82 83 // TODO refactor to have per user state for providers. See 84 // MediaRouterService for an example 85 86 public MediaSessionService(Context context) { 87 super(context); 88 mSessionManagerImpl = new SessionManagerImpl(); 89 mPriorityStack = new MediaSessionStack(); 90 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 91 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); 92 } 93 94 @Override 95 public void onStart() { 96 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); 97 Watchdog.getInstance().addMonitor(this); 98 updateUser(); 99 } 100 101 /** 102 * Should trigger showing the Media route picker dialog. Right now it just 103 * kicks off a query to all the providers to get routes. 104 * 105 * @param record The session to show the picker for. 106 */ 107 public void showRoutePickerForSession(MediaSessionRecord record) { 108 // TODO for now just toggle the route to test (we will only have one 109 // match for now) 110 synchronized (mLock) { 111 if (!mAllSessions.contains(record)) { 112 Log.d(TAG, "Unknown session tried to show route picker. Ignoring."); 113 return; 114 } 115 RouteInfo current = record.getRoute(); 116 UserRecord user = mUserRecords.get(record.getUserId()); 117 if (current != null) { 118 // For now send null to mean the local route 119 MediaRouteProviderProxy proxy = user.getProviderLocked(current.getProvider()); 120 if (proxy != null) { 121 proxy.removeSession(record); 122 } 123 record.selectRoute(null); 124 return; 125 } 126 ArrayList<MediaRouteProviderProxy> providers = user.getProvidersLocked(); 127 mShowRoutesRequestId++; 128 for (int i = providers.size() - 1; i >= 0; i--) { 129 MediaRouteProviderProxy provider = providers.get(i); 130 provider.getRoutes(record, mShowRoutesRequestId); 131 } 132 } 133 } 134 135 /** 136 * Connect a session to the given route. 137 * 138 * @param session The session to connect. 139 * @param route The route to connect to. 140 * @param options The options to use for the connection. 141 */ 142 public void connectToRoute(MediaSessionRecord session, RouteInfo route, 143 RouteOptions options) { 144 synchronized (mLock) { 145 if (!mAllSessions.contains(session)) { 146 Log.d(TAG, "Unknown session attempting to connect to route. Ignoring"); 147 return; 148 } 149 UserRecord user = mUserRecords.get(session.getUserId()); 150 if (user == null) { 151 Log.wtf(TAG, "connectToRoute: User " + session.getUserId() + " does not exist."); 152 return; 153 } 154 MediaRouteProviderProxy proxy = user.getProviderLocked(route.getProvider()); 155 if (proxy == null) { 156 Log.w(TAG, "Provider for route " + route.getName() + " does not exist."); 157 return; 158 } 159 RouteRequest request = new RouteRequest(session.getSessionInfo(), options, true); 160 proxy.connectToRoute(session, route, request); 161 } 162 } 163 164 public void updateSession(MediaSessionRecord record) { 165 synchronized (mLock) { 166 if (!mAllSessions.contains(record)) { 167 Log.d(TAG, "Unknown session updated. Ignoring."); 168 return; 169 } 170 mPriorityStack.onSessionStateChange(record); 171 if (record.isSystemPriority()) { 172 if (record.isActive()) { 173 if (mPrioritySession != null) { 174 Log.w(TAG, "Replacing existing priority session with a new session"); 175 } 176 mPrioritySession = record; 177 } else { 178 if (mPrioritySession == record) { 179 mPrioritySession = null; 180 } 181 } 182 } 183 } 184 } 185 186 public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { 187 synchronized (mLock) { 188 if (!mAllSessions.contains(record)) { 189 Log.d(TAG, "Unknown session changed playback state. Ignoring."); 190 return; 191 } 192 mPriorityStack.onPlaystateChange(record, oldState, newState); 193 } 194 } 195 196 @Override 197 public void onStartUser(int userHandle) { 198 updateUser(); 199 } 200 201 @Override 202 public void onSwitchUser(int userHandle) { 203 updateUser(); 204 } 205 206 @Override 207 public void onStopUser(int userHandle) { 208 synchronized (mLock) { 209 UserRecord user = mUserRecords.get(userHandle); 210 if (user != null) { 211 destroyUserLocked(user); 212 } 213 } 214 } 215 216 @Override 217 public void monitor() { 218 synchronized (mLock) { 219 // Check for deadlock 220 } 221 } 222 223 protected void enforcePhoneStatePermission(int pid, int uid) { 224 if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) 225 != PackageManager.PERMISSION_GRANTED) { 226 throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); 227 } 228 } 229 230 void sessionDied(MediaSessionRecord session) { 231 synchronized (mLock) { 232 destroySessionLocked(session); 233 } 234 } 235 236 void destroySession(MediaSessionRecord session) { 237 synchronized (mLock) { 238 destroySessionLocked(session); 239 } 240 } 241 242 private void updateUser() { 243 synchronized (mLock) { 244 int userId = ActivityManager.getCurrentUser(); 245 if (mCurrentUserId != userId) { 246 final int oldUserId = mCurrentUserId; 247 mCurrentUserId = userId; // do this first 248 249 UserRecord oldUser = mUserRecords.get(oldUserId); 250 if (oldUser != null) { 251 oldUser.stopLocked(); 252 } 253 254 UserRecord newUser = getOrCreateUser(userId); 255 newUser.startLocked(); 256 } 257 } 258 } 259 260 /** 261 * Stop the user and unbind from everything. 262 * 263 * @param user The user to dispose of 264 */ 265 private void destroyUserLocked(UserRecord user) { 266 user.stopLocked(); 267 user.destroyLocked(); 268 mUserRecords.remove(user.mUserId); 269 } 270 271 /* 272 * When a session is removed several things need to happen. 273 * 1. We need to remove it from the relevant user. 274 * 2. We need to remove it from the priority stack. 275 * 3. We need to remove it from all sessions. 276 * 4. If this is the system priority session we need to clear it. 277 * 5. We need to unlink to death from the cb binder 278 * 6. We need to tell the session to do any final cleanup (onDestroy) 279 */ 280 private void destroySessionLocked(MediaSessionRecord session) { 281 int userId = session.getUserId(); 282 UserRecord user = mUserRecords.get(userId); 283 if (user != null) { 284 user.removeSessionLocked(session); 285 } 286 287 mPriorityStack.removeSession(session); 288 mAllSessions.remove(session); 289 if (session == mPrioritySession) { 290 mPrioritySession = null; 291 } 292 293 try { 294 session.getCallback().asBinder().unlinkToDeath(session, 0); 295 } catch (Exception e) { 296 // ignore exceptions while destroying a session. 297 } 298 session.onDestroy(); 299 } 300 301 private void enforcePackageName(String packageName, int uid) { 302 if (TextUtils.isEmpty(packageName)) { 303 throw new IllegalArgumentException("packageName may not be empty"); 304 } 305 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 306 final int packageCount = packages.length; 307 for (int i = 0; i < packageCount; i++) { 308 if (packageName.equals(packages[i])) { 309 return; 310 } 311 } 312 throw new IllegalArgumentException("packageName is not owned by the calling process"); 313 } 314 315 /** 316 * Checks a caller's authorization to register an IRemoteControlDisplay. 317 * Authorization is granted if one of the following is true: 318 * <ul> 319 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL 320 * permission</li> 321 * <li>the caller's listener is one of the enabled notification listeners 322 * for the caller's user</li> 323 * </ul> 324 */ 325 private void enforceMediaPermissions(ComponentName compName, int pid, int uid, 326 int resolvedUserId) { 327 if (getContext() 328 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) 329 != PackageManager.PERMISSION_GRANTED 330 && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), 331 resolvedUserId)) { 332 throw new SecurityException("Missing permission to control media."); 333 } 334 } 335 336 /** 337 * This checks if the component is an enabled notification listener for the 338 * specified user. Enabled components may only operate on behalf of the user 339 * they're running as. 340 * 341 * @param compName The component that is enabled. 342 * @param userId The user id of the caller. 343 * @param forUserId The user id they're making the request on behalf of. 344 * @return True if the component is enabled, false otherwise 345 */ 346 private boolean isEnabledNotificationListener(ComponentName compName, int userId, 347 int forUserId) { 348 if (userId != forUserId) { 349 // You may not access another user's content as an enabled listener. 350 return false; 351 } 352 if (compName != null) { 353 final String enabledNotifListeners = Settings.Secure.getStringForUser( 354 getContext().getContentResolver(), 355 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, 356 userId); 357 if (enabledNotifListeners != null) { 358 final String[] components = enabledNotifListeners.split(":"); 359 for (int i = 0; i < components.length; i++) { 360 final ComponentName component = 361 ComponentName.unflattenFromString(components[i]); 362 if (component != null) { 363 if (compName.equals(component)) { 364 if (DEBUG) { 365 Log.d(TAG, "ok to get sessions: " + component + 366 " is authorized notification listener"); 367 } 368 return true; 369 } 370 } 371 } 372 } 373 if (DEBUG) { 374 Log.d(TAG, "not ok to get sessions, " + compName + 375 " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId); 376 } 377 } 378 return false; 379 } 380 381 private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, 382 String callerPackageName, ISessionCallback cb, String tag) throws RemoteException { 383 synchronized (mLock) { 384 return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); 385 } 386 } 387 388 /* 389 * When a session is created the following things need to happen. 390 * 1. Its callback binder needs a link to death 391 * 2. It needs to be added to all sessions. 392 * 3. It needs to be added to the priority stack. 393 * 4. It needs to be added to the relevant user record. 394 */ 395 private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, 396 String callerPackageName, ISessionCallback cb, String tag) { 397 398 final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, 399 callerPackageName, cb, tag, this, mHandler); 400 try { 401 cb.asBinder().linkToDeath(session, 0); 402 } catch (RemoteException e) { 403 throw new RuntimeException("Media Session owner died prematurely.", e); 404 } 405 406 mAllSessions.add(session); 407 mPriorityStack.addSession(session); 408 409 UserRecord user = getOrCreateUser(userId); 410 user.addSessionLocked(session); 411 412 if (DEBUG) { 413 Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag); 414 } 415 return session; 416 } 417 418 private UserRecord getOrCreateUser(int userId) { 419 UserRecord user = mUserRecords.get(userId); 420 if (user == null) { 421 user = new UserRecord(getContext(), userId); 422 mUserRecords.put(userId, user); 423 } 424 return user; 425 } 426 427 private int findIndexOfSessionForIdLocked(String sessionId) { 428 for (int i = mAllSessions.size() - 1; i >= 0; i--) { 429 MediaSessionRecord session = mAllSessions.get(i); 430 if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) { 431 return i; 432 } 433 } 434 return -1; 435 } 436 437 private boolean isSessionDiscoverable(MediaSessionRecord record) { 438 // TODO probably want to check more than if it's active. 439 return record.isActive(); 440 } 441 442 private MediaRouteProviderProxy.RoutesListener mRoutesCallback 443 = new MediaRouteProviderProxy.RoutesListener() { 444 @Override 445 public void onRoutesUpdated(String sessionId, ArrayList<RouteInfo> routes, 446 int reqId) { 447 // TODO for now select the first route to test, eventually add the 448 // new routes to the dialog if it is still open 449 synchronized (mLock) { 450 int index = findIndexOfSessionForIdLocked(sessionId); 451 if (index != -1 && routes != null && routes.size() > 0) { 452 MediaSessionRecord record = mAllSessions.get(index); 453 RouteInfo route = routes.get(0); 454 record.selectRoute(route); 455 UserRecord user = mUserRecords.get(record.getUserId()); 456 MediaRouteProviderProxy provider = user.getProviderLocked(route.getProvider()); 457 provider.addSession(record); 458 } 459 } 460 } 461 462 @Override 463 public void onRouteConnected(String sessionId, RouteInfo route, 464 RouteRequest options, RouteConnectionRecord connection) { 465 synchronized (mLock) { 466 int index = findIndexOfSessionForIdLocked(sessionId); 467 if (index != -1) { 468 MediaSessionRecord session = mAllSessions.get(index); 469 session.setRouteConnected(route, options.getConnectionOptions(), connection); 470 } 471 } 472 } 473 }; 474 475 /** 476 * Information about a particular user. The contents of this object is 477 * guarded by mLock. 478 */ 479 final class UserRecord { 480 private final int mUserId; 481 private final MediaRouteProviderWatcher mRouteProviderWatcher; 482 private final ArrayList<MediaRouteProviderProxy> mProviders 483 = new ArrayList<MediaRouteProviderProxy>(); 484 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); 485 486 public UserRecord(Context context, int userId) { 487 mUserId = userId; 488 mRouteProviderWatcher = new MediaRouteProviderWatcher(context, 489 mProviderWatcherCallback, mHandler, userId); 490 } 491 492 public void startLocked() { 493 mRouteProviderWatcher.start(); 494 } 495 496 public void stopLocked() { 497 mRouteProviderWatcher.stop(); 498 updateInterestLocked(); 499 } 500 501 public void destroyLocked() { 502 for (int i = mSessions.size() - 1; i >= 0; i--) { 503 MediaSessionRecord session = mSessions.get(i); 504 MediaSessionService.this.destroySessionLocked(session); 505 if (session.isConnected()) { 506 session.disconnect(MediaSession.DISCONNECT_REASON_USER_STOPPING); 507 } 508 } 509 } 510 511 public ArrayList<MediaRouteProviderProxy> getProvidersLocked() { 512 return mProviders; 513 } 514 515 public ArrayList<MediaSessionRecord> getSessionsLocked() { 516 return mSessions; 517 } 518 519 public void addSessionLocked(MediaSessionRecord session) { 520 mSessions.add(session); 521 updateInterestLocked(); 522 } 523 524 public void removeSessionLocked(MediaSessionRecord session) { 525 mSessions.remove(session); 526 RouteInfo route = session.getRoute(); 527 if (route != null) { 528 MediaRouteProviderProxy provider = getProviderLocked(route.getProvider()); 529 if (provider != null) { 530 provider.removeSession(session); 531 } 532 } 533 updateInterestLocked(); 534 } 535 536 public void dumpLocked(PrintWriter pw, String prefix) { 537 pw.println(prefix + "Record for user " + mUserId); 538 String indent = prefix + " "; 539 int size = mProviders.size(); 540 pw.println(indent + size + " Providers:"); 541 for (int i = 0; i < size; i++) { 542 mProviders.get(i).dump(pw, indent); 543 } 544 pw.println(); 545 size = mSessions.size(); 546 pw.println(indent + size + " Sessions:"); 547 for (int i = 0; i < size; i++) { 548 // Just print the session info, the full session dump will 549 // already be in the list of all sessions. 550 pw.println(indent + mSessions.get(i).getSessionInfo()); 551 } 552 } 553 554 public void updateInterestLocked() { 555 // TODO go through the sessions and build up the set of interfaces 556 // we're interested in. Update the provider watcher. 557 // For now, just express interest in all providers for the current 558 // user 559 boolean interested = mUserId == mCurrentUserId; 560 for (int i = mProviders.size() - 1; i >= 0; i--) { 561 mProviders.get(i).setInterested(interested); 562 } 563 } 564 565 private MediaRouteProviderProxy getProviderLocked(String providerId) { 566 for (int i = mProviders.size() - 1; i >= 0; i--) { 567 MediaRouteProviderProxy provider = mProviders.get(i); 568 if (TextUtils.equals(providerId, provider.getId())) { 569 return provider; 570 } 571 } 572 return null; 573 } 574 575 private MediaRouteProviderWatcher.Callback mProviderWatcherCallback 576 = new MediaRouteProviderWatcher.Callback() { 577 @Override 578 public void removeProvider(MediaRouteProviderProxy provider) { 579 synchronized (mLock) { 580 mProviders.remove(provider); 581 provider.setRoutesListener(null); 582 provider.setInterested(false); 583 } 584 } 585 586 @Override 587 public void addProvider(MediaRouteProviderProxy provider) { 588 synchronized (mLock) { 589 mProviders.add(provider); 590 provider.setRoutesListener(mRoutesCallback); 591 provider.setInterested(true); 592 } 593 } 594 }; 595 } 596 597 class SessionManagerImpl extends ISessionManager.Stub { 598 private static final String EXTRA_WAKELOCK_ACQUIRED = 599 "android.media.AudioService.WAKELOCK_ACQUIRED"; 600 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number 601 602 @Override 603 public ISession createSession(String packageName, ISessionCallback cb, String tag, 604 int userId) throws RemoteException { 605 final int pid = Binder.getCallingPid(); 606 final int uid = Binder.getCallingUid(); 607 final long token = Binder.clearCallingIdentity(); 608 try { 609 enforcePackageName(packageName, uid); 610 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 611 false /* allowAll */, true /* requireFull */, "createSession", packageName); 612 if (cb == null) { 613 throw new IllegalArgumentException("Controller callback cannot be null"); 614 } 615 return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) 616 .getSessionBinder(); 617 } finally { 618 Binder.restoreCallingIdentity(token); 619 } 620 } 621 622 @Override 623 public List<IBinder> getSessions(ComponentName componentName, int userId) { 624 final int pid = Binder.getCallingPid(); 625 final int uid = Binder.getCallingUid(); 626 final long token = Binder.clearCallingIdentity(); 627 628 try { 629 String packageName = null; 630 if (componentName != null) { 631 // If they gave us a component name verify they own the 632 // package 633 packageName = componentName.getPackageName(); 634 enforcePackageName(packageName, uid); 635 } 636 // Check that they can make calls on behalf of the user and 637 // get the final user id 638 int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, 639 true /* allowAll */, true /* requireFull */, "getSessions", packageName); 640 // Check if they have the permissions or their component is 641 // enabled for the user they're calling from. 642 enforceMediaPermissions(componentName, pid, uid, resolvedUserId); 643 ArrayList<IBinder> binders = new ArrayList<IBinder>(); 644 synchronized (mLock) { 645 ArrayList<MediaSessionRecord> records = mPriorityStack 646 .getActiveSessions(resolvedUserId); 647 int size = records.size(); 648 for (int i = 0; i < size; i++) { 649 binders.add(records.get(i).getControllerBinder().asBinder()); 650 } 651 } 652 return binders; 653 } finally { 654 Binder.restoreCallingIdentity(token); 655 } 656 } 657 658 /** 659 * Handles the dispatching of the media button events to one of the 660 * registered listeners, or if there was none, broadcast an 661 * ACTION_MEDIA_BUTTON intent to the rest of the system. 662 * 663 * @param keyEvent a non-null KeyEvent whose key code is one of the 664 * supported media buttons 665 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held 666 * while this key event is dispatched. 667 */ 668 @Override 669 public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 670 if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) { 671 Log.w(TAG, "Attempted to dispatch null or non-media key event."); 672 return; 673 } 674 final int pid = Binder.getCallingPid(); 675 final int uid = Binder.getCallingUid(); 676 final long token = Binder.clearCallingIdentity(); 677 678 try { 679 if (needWakeLock) { 680 mMediaEventWakeLock.acquire(); 681 } 682 synchronized (mLock) { 683 MediaSessionRecord mbSession = mPriorityStack 684 .getDefaultMediaButtonSession(mCurrentUserId); 685 if (mbSession != null) { 686 if (DEBUG) { 687 Log.d(TAG, "Sending media key to " + mbSession.getSessionInfo()); 688 } 689 mbSession.sendMediaButton(keyEvent, 690 needWakeLock ? mKeyEventDoneReceiver : null); 691 } else { 692 if (DEBUG) { 693 Log.d(TAG, "Sending media key ordered broadcast"); 694 } 695 // Fallback to legacy behavior 696 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 697 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 698 if (needWakeLock) { 699 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, 700 WAKELOCK_RELEASE_ON_FINISHED); 701 } 702 getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL, 703 null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null); 704 } 705 } 706 } finally { 707 Binder.restoreCallingIdentity(token); 708 } 709 } 710 711 @Override 712 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 713 if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP) 714 != PackageManager.PERMISSION_GRANTED) { 715 pw.println("Permission Denial: can't dump MediaSessionService from from pid=" 716 + Binder.getCallingPid() 717 + ", uid=" + Binder.getCallingUid()); 718 return; 719 } 720 721 pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); 722 pw.println(); 723 724 synchronized (mLock) { 725 pw.println("Session for calls:" + mPrioritySession); 726 if (mPrioritySession != null) { 727 mPrioritySession.dump(pw, ""); 728 } 729 int count = mAllSessions.size(); 730 pw.println(count + " Sessions:"); 731 for (int i = 0; i < count; i++) { 732 mAllSessions.get(i).dump(pw, ""); 733 pw.println(); 734 } 735 mPriorityStack.dump(pw, ""); 736 737 pw.println("User Records:"); 738 count = mUserRecords.size(); 739 for (int i = 0; i < count; i++) { 740 UserRecord user = mUserRecords.get(i); 741 user.dumpLocked(pw, ""); 742 } 743 } 744 } 745 746 ResultReceiver mKeyEventDoneReceiver = new ResultReceiver(mHandler) { 747 @Override 748 protected void onReceiveResult(int resultCode, Bundle resultData) { 749 synchronized (mLock) { 750 if (mMediaEventWakeLock.isHeld()) { 751 mMediaEventWakeLock.release(); 752 } 753 } 754 } 755 }; 756 757 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { 758 @Override 759 public void onReceive(Context context, Intent intent) { 760 if (intent == null) { 761 return; 762 } 763 Bundle extras = intent.getExtras(); 764 if (extras == null) { 765 return; 766 } 767 synchronized (mLock) { 768 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED) 769 && mMediaEventWakeLock.isHeld()) { 770 mMediaEventWakeLock.release(); 771 } 772 } 773 } 774 }; 775 } 776 777} 778