1 /* 2 * Copyright (C) 2019 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 package com.android.car; 17 18 import static android.car.CarOccupantZoneManager.INVALID_USER_ID; 19 import static android.car.CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID; 20 import static android.car.builtin.os.UserManagerHelper.getMaxRunningUsers; 21 import static android.car.media.CarMediaIntents.EXTRA_MEDIA_COMPONENT; 22 import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE; 23 import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK; 24 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_INVISIBLE; 25 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 26 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED; 27 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_VISIBLE; 28 29 import static com.android.car.CarServiceUtils.assertPermission; 30 import static com.android.car.CarServiceUtils.getCommonHandlerThread; 31 import static com.android.car.CarServiceUtils.getHandlerThread; 32 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 33 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU; 34 35 import android.annotation.NonNull; 36 import android.annotation.Nullable; 37 import android.annotation.TestApi; 38 import android.annotation.UserIdInt; 39 import android.app.ActivityManager; 40 import android.app.usage.UsageStatsManager; 41 import android.car.Car; 42 import android.car.builtin.util.Slogf; 43 import android.car.builtin.util.TimeUtils; 44 import android.car.builtin.util.UsageStatsManagerHelper; 45 import android.car.hardware.power.CarPowerPolicy; 46 import android.car.hardware.power.CarPowerPolicyFilter; 47 import android.car.hardware.power.ICarPowerPolicyListener; 48 import android.car.hardware.power.PowerComponent; 49 import android.car.media.CarMediaManager; 50 import android.car.media.CarMediaManager.MediaSourceMode; 51 import android.car.media.ICarMedia; 52 import android.car.media.ICarMediaSourceListener; 53 import android.car.user.CarUserManager.UserLifecycleListener; 54 import android.car.user.UserLifecycleEventFilter; 55 import android.content.BroadcastReceiver; 56 import android.content.ComponentName; 57 import android.content.Context; 58 import android.content.Intent; 59 import android.content.IntentFilter; 60 import android.content.SharedPreferences; 61 import android.content.pm.PackageManager; 62 import android.content.pm.ResolveInfo; 63 import android.media.session.MediaController; 64 import android.media.session.MediaController.TransportControls; 65 import android.media.session.MediaSession; 66 import android.media.session.MediaSession.Token; 67 import android.media.session.MediaSessionManager; 68 import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener; 69 import android.media.session.PlaybackState; 70 import android.os.Bundle; 71 import android.os.Handler; 72 import android.os.HandlerThread; 73 import android.os.RemoteCallbackList; 74 import android.os.RemoteException; 75 import android.os.UserHandle; 76 import android.os.UserManager; 77 import android.service.media.MediaBrowserService; 78 import android.text.TextUtils; 79 import android.util.Log; 80 import android.util.SparseArray; 81 import android.view.KeyEvent; 82 83 import com.android.car.CarInputService.KeyEventListener; 84 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 85 import com.android.car.internal.os.HandlerExecutor; 86 import com.android.car.internal.util.DebugUtils; 87 import com.android.car.internal.util.IndentingPrintWriter; 88 import com.android.car.power.CarPowerManagementService; 89 import com.android.car.user.CarUserService; 90 import com.android.car.user.UserHandleHelper; 91 import com.android.internal.annotations.GuardedBy; 92 import com.android.internal.annotations.VisibleForTesting; 93 94 import java.util.ArrayDeque; 95 import java.util.ArrayList; 96 import java.util.Arrays; 97 import java.util.Deque; 98 import java.util.HashMap; 99 import java.util.List; 100 import java.util.Map; 101 102 /** 103 * CarMediaService manages the currently active media source for car apps. This is different from 104 * the MediaSessionManager's active sessions, as there can only be one active source in the car, 105 * through both browse and playback. 106 * 107 * In the car, the active media source does not necessarily have an active MediaSession, e.g. if 108 * it were being browsed only. However, that source is still considered the active source, and 109 * should be the source displayed in any Media related UIs (Media Center, home screen, etc). 110 */ 111 public final class CarMediaService extends ICarMedia.Stub implements CarServiceBase { 112 113 private static final String TAG = CarLog.TAG_MEDIA; 114 private static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG); 115 116 private static final String SOURCE_KEY = "media_source_component"; 117 private static final String SOURCE_KEY_SEPARATOR = "_"; 118 private static final String PLAYBACK_STATE_KEY = "playback_state"; 119 private static final String SHARED_PREF = "com.android.car.media.car_media_service"; 120 private static final String COMPONENT_NAME_SEPARATOR = ","; 121 private static final String MEDIA_CONNECTION_ACTION = "com.android.car.media.MEDIA_CONNECTION"; 122 private static final String EXTRA_AUTOPLAY = "com.android.car.media.autoplay"; 123 private static final String LAST_UPDATE_KEY = "last_update"; 124 125 private static final int MEDIA_SOURCE_MODES = 2; 126 127 // XML configuration options for autoplay on media source change. 128 private static final int AUTOPLAY_CONFIG_NEVER = 0; 129 private static final int AUTOPLAY_CONFIG_ALWAYS = 1; 130 // This mode uses the current source's last stored playback state to resume playback 131 private static final int AUTOPLAY_CONFIG_RETAIN_PER_SOURCE = 2; 132 // This mode uses the previous source's playback state to resume playback 133 private static final int AUTOPLAY_CONFIG_RETAIN_PREVIOUS = 3; 134 135 private final Context mContext; 136 private final CarOccupantZoneService mOccupantZoneService; 137 private final CarUserService mUserService; 138 private final UserManager mUserManager; 139 private final MediaSessionManager mMediaSessionManager; 140 private final UsageStatsManager mUsageStatsManager; 141 142 /** 143 * An array to store all per-user media data. 144 * 145 * <p>In most cases there will be one entry for the current user. 146 * On a {@link UserManager#isUsersOnSecondaryDisplaysSupported() MUMD} (multi-user 147 * multi-display) device, there will be multiple entries, one per each visible user. 148 */ 149 // TODO(b/262734537) Specify the initial capacity. 150 @GuardedBy("mLock") 151 private final SparseArray<UserMediaPlayContext> mUserMediaPlayContexts; 152 153 // NOTE: must use getSharedPrefsForWriting() to write to it 154 private SharedPreferences mSharedPrefs; 155 private int mPlayOnMediaSourceChangedConfig; 156 private int mPlayOnBootConfig; 157 private boolean mDefaultIndependentPlaybackConfig; 158 159 private final Handler mCommonThreadHandler = new Handler( 160 getCommonHandlerThread().getLooper()); 161 162 private final HandlerThread mHandlerThread = getHandlerThread( 163 getClass().getSimpleName()); 164 // Handler to receive PlaybackState callbacks from the active media controller. 165 private final Handler mHandler = new Handler(mHandlerThread.getLooper()); 166 private final Object mLock = new Object(); 167 168 private final IntentFilter mPackageUpdateFilter; 169 170 /** 171 * Listens to {@link Intent#ACTION_PACKAGE_REMOVED}, so we can fall back to a previously used 172 * media source when the active source is uninstalled. 173 */ 174 // TODO(b/262734537) Refactor this receiver using PackageMonitor. 175 private final BroadcastReceiver mPackageUpdateReceiver = new BroadcastReceiver() { 176 @Override 177 public void onReceive(Context context, Intent intent) { 178 if (intent.getData() == null) { 179 return; 180 } 181 String intentPackage = intent.getData().getSchemeSpecificPart(); 182 if (DEBUG) { 183 Slogf.d(TAG, "Received a package update for package: %s, action: %s", 184 intentPackage, intent.getAction()); 185 } 186 if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { 187 synchronized (mLock) { 188 int userArraySize = mUserMediaPlayContexts.size(); 189 for (int i = 0; i < userArraySize; i++) { 190 int userId = mUserMediaPlayContexts.keyAt(i); 191 UserMediaPlayContext userMediaContext = mUserMediaPlayContexts.valueAt(i); 192 ComponentName[] primaryComponents = 193 userMediaContext.mPrimaryMediaComponents; 194 for (int j = 0; j < MEDIA_SOURCE_MODES; j++) { 195 if (primaryComponents[j] != null 196 && primaryComponents[j].getPackageName().equals( 197 intentPackage)) { 198 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 199 // If package is being replaced, it may not be removed from 200 // PackageManager queries when we check for available 201 // MediaBrowseServices, so we iterate to find the next available 202 // source. 203 for (ComponentName component 204 : getLastMediaSourcesInternal(j, userId)) { 205 if (!primaryComponents[j].getPackageName() 206 .equals(component.getPackageName())) { 207 userMediaContext.mRemovedMediaSourceComponents[j] = 208 primaryComponents[j]; 209 if (DEBUG) { 210 Slogf.d(TAG, "temporarily replacing updated media " 211 + "source %s for user %d with " 212 + "backup source: %s", 213 primaryComponents[j], userId, component); 214 } 215 setPrimaryMediaSource(component, j, userId); 216 return; 217 } 218 } 219 Slogf.e(TAG, "No available backup media source for user %d", 220 userId); 221 } else { 222 if (DEBUG) { 223 Slogf.d(TAG, "replacing removed media source" 224 + " %s with backup source: %s for user %d", 225 primaryComponents[j], 226 getLastMediaSource(j, userId), userId); 227 } 228 userMediaContext.mRemovedMediaSourceComponents[j] = null; 229 setPrimaryMediaSource(getLastMediaSource(j, userId), j, userId); 230 } 231 } 232 } 233 } 234 } 235 } else if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction()) 236 || Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) { 237 synchronized (mLock) { 238 int userArraySize = mUserMediaPlayContexts.size(); 239 for (int i = 0; i < userArraySize; i++) { 240 int userId = mUserMediaPlayContexts.keyAt(i); 241 ComponentName[] removedComponents = 242 getRemovedMediaSourceComponentsForUser(userId); 243 for (int j = 0; j < MEDIA_SOURCE_MODES; j++) { 244 if (removedComponents[j] != null && removedComponents[j] 245 .getPackageName().equals(intentPackage)) { 246 if (DEBUG) { 247 Slogf.d(TAG, "restoring removed source: %s for user %d", 248 removedComponents[j], userId); 249 } 250 setPrimaryMediaSource(removedComponents[j], j, userId); 251 } 252 } 253 } 254 } 255 } 256 } 257 }; 258 259 private final UserLifecycleListener mUserLifecycleListener = event -> { 260 if (DEBUG) { 261 Slogf.d(TAG, "CarMediaService.onEvent(%s)", event); 262 } 263 264 // Note that we receive different event types based on the platform version, beacause of 265 // the way we build the filter when registering the listener. 266 // 267 // Before U: 268 // Receives USER_SWITCHING and USER UNLOCKED 269 // U and after: 270 // Receives USER_VISIBLE, USER_INVISIBLE, and USER_UNLOCKED 271 // 272 // See the constructor of this class to see how the UserLifecycleEventFilter is built 273 // differently based on the platform version. 274 switch (event.getEventType()) { 275 case USER_LIFECYCLE_EVENT_TYPE_SWITCHING: 276 onUserSwitch(event.getPreviousUserId(), event.getUserId()); 277 break; 278 case USER_LIFECYCLE_EVENT_TYPE_VISIBLE: 279 onUserVisible(event.getUserId()); 280 break; 281 case USER_LIFECYCLE_EVENT_TYPE_UNLOCKED: 282 onUserUnlocked(event.getUserId()); 283 break; 284 case USER_LIFECYCLE_EVENT_TYPE_INVISIBLE: 285 onUserInvisible(event.getUserId()); 286 break; 287 default: 288 break; 289 } 290 }; 291 292 private final ICarPowerPolicyListener mPowerPolicyListener = 293 new ICarPowerPolicyListener.Stub() { 294 @Override 295 public void onPolicyChanged(CarPowerPolicy appliedPolicy, 296 CarPowerPolicy accumulatedPolicy) { 297 boolean shouldBePlaying; 298 MediaController mediaController; 299 boolean isOff = !accumulatedPolicy.isComponentEnabled(PowerComponent.MEDIA); 300 synchronized (mLock) { 301 int userArraySize = mUserMediaPlayContexts.size(); 302 // Apply power policy to all users. 303 for (int i = 0; i < userArraySize; i++) { 304 int userId = mUserMediaPlayContexts.keyAt(i); 305 UserMediaPlayContext userMediaContext = 306 mUserMediaPlayContexts.valueAt(i); 307 boolean isUserPlaying = (userMediaContext.mCurrentPlaybackState 308 == PlaybackState.STATE_PLAYING); 309 userMediaContext.mIsDisabledByPowerPolicy = isOff; 310 if (isOff) { 311 if (!userMediaContext.mWasPreviouslyDisabledByPowerPolicy) { 312 // We're disabling media component. 313 // Remember if we are playing at this transition. 314 userMediaContext.mWasPlayingBeforeDisabled = isUserPlaying; 315 userMediaContext.mWasPreviouslyDisabledByPowerPolicy = true; 316 } 317 shouldBePlaying = false; 318 } else { 319 userMediaContext.mWasPreviouslyDisabledByPowerPolicy = false; 320 shouldBePlaying = userMediaContext.mWasPlayingBeforeDisabled; 321 } 322 if (shouldBePlaying == isUserPlaying) { 323 return; 324 } 325 // Make a change 326 mediaController = userMediaContext.mActiveMediaController; 327 if (mediaController == null) { 328 if (DEBUG) { 329 Slogf.d(TAG, 330 "No active media controller for user %d. Power policy" 331 + " change does not affect this user's media.", userId); 332 } 333 return; 334 } 335 PlaybackState oldState = mediaController.getPlaybackState(); 336 if (oldState == null) { 337 return; 338 } 339 savePlaybackState( 340 // The new state is the same as the old state, except for 341 // play/pause 342 new PlaybackState.Builder(oldState) 343 .setState(shouldBePlaying ? PlaybackState.STATE_PLAYING 344 : PlaybackState.STATE_PAUSED, 345 oldState.getPosition(), 346 oldState.getPlaybackSpeed()) 347 .build(), userId); 348 TransportControls controls = mediaController.getTransportControls(); 349 if (shouldBePlaying) { 350 controls.play(); 351 } else { 352 controls.pause(); 353 } 354 } 355 } 356 } 357 }; 358 359 private final UserHandleHelper mUserHandleHelper; 360 CarMediaService(Context context, CarOccupantZoneService occupantZoneService, CarUserService userService)361 public CarMediaService(Context context, CarOccupantZoneService occupantZoneService, 362 CarUserService userService) { 363 this(context, occupantZoneService, userService, 364 new UserHandleHelper(context, context.getSystemService(UserManager.class))); 365 } 366 367 @VisibleForTesting CarMediaService(Context context, CarOccupantZoneService occupantZoneService, CarUserService userService, @NonNull UserHandleHelper userHandleHelper)368 public CarMediaService(Context context, CarOccupantZoneService occupantZoneService, 369 CarUserService userService, @NonNull UserHandleHelper userHandleHelper) { 370 mContext = context; 371 mUserManager = mContext.getSystemService(UserManager.class); 372 mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class); 373 mUsageStatsManager = mContext.getSystemService(UsageStatsManager.class); 374 mDefaultIndependentPlaybackConfig = mContext.getResources().getBoolean( 375 R.bool.config_mediaSourceIndependentPlayback); 376 mUserMediaPlayContexts = 377 new SparseArray<UserMediaPlayContext>(getMaxRunningUsers(context)); 378 379 mPackageUpdateFilter = new IntentFilter(); 380 mPackageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 381 mPackageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 382 mPackageUpdateFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 383 mPackageUpdateFilter.addDataScheme("package"); 384 385 mOccupantZoneService = occupantZoneService; 386 mUserService = userService; 387 388 // Before U, only listen to USER_SWITCHING and USER_UNLOCKED. 389 // U and after, only listen to USER_VISIBLE, USER_INVISIBLE, and USER_UNLOCKED. 390 UserLifecycleEventFilter.Builder userLifecycleEventFilterBuilder = 391 new UserLifecycleEventFilter.Builder() 392 .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED); 393 if (isPlatformVersionAtLeastU()) { 394 userLifecycleEventFilterBuilder.addEventType(USER_LIFECYCLE_EVENT_TYPE_INVISIBLE) 395 .addEventType(USER_LIFECYCLE_EVENT_TYPE_VISIBLE); 396 } else { 397 userLifecycleEventFilterBuilder.addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING); 398 } 399 mUserService.addUserLifecycleListener(userLifecycleEventFilterBuilder.build(), 400 mUserLifecycleListener); 401 402 mPlayOnMediaSourceChangedConfig = 403 mContext.getResources().getInteger(R.integer.config_mediaSourceChangedAutoplay); 404 mPlayOnBootConfig = mContext.getResources().getInteger(R.integer.config_mediaBootAutoplay); 405 mUserHandleHelper = userHandleHelper; 406 } 407 408 @Override 409 // This method is called from ICarImpl after CarMediaService is created. init()410 public void init() { 411 int currentUserId = ActivityManager.getCurrentUser(); 412 if (DEBUG) { 413 Slogf.d(TAG, "init(): currentUser=%d", currentUserId); 414 } 415 // Initialize media service for the current user. 416 maybeInitUser(currentUserId); 417 setKeyEventListener(); 418 setPowerPolicyListener(); 419 } 420 maybeInitUser(@serIdInt int userId)421 private void maybeInitUser(@UserIdInt int userId) { 422 if (userId == UserHandle.SYSTEM.getIdentifier() && UserManager.isHeadlessSystemUserMode()) { 423 if (DEBUG) { 424 Slogf.d(TAG, "maybeInitUser(%d): No need to initialize for the" 425 + " headless system user", userId); 426 } 427 return; 428 } 429 if (mUserManager.isUserUnlocked(UserHandle.of(userId))) { 430 initUser(userId); 431 } else { 432 synchronized (mLock) { 433 getOrCreateUserMediaPlayContextLocked(userId).mPendingInit = true; 434 } 435 } 436 } 437 438 /** Initializes car media service data for the specified user. */ initUser(@serIdInt int userId)439 private void initUser(@UserIdInt int userId) { 440 if (DEBUG) { 441 Slogf.d(TAG, "initUser(): userId=%d, mSharedPrefs=%s", userId, mSharedPrefs); 442 } 443 UserHandle userHandle = UserHandle.of(userId); 444 445 maybeInitSharedPrefs(userId); 446 447 ComponentName playbackSource; 448 synchronized (mLock) { 449 UserMediaPlayContext userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 450 if (userMediaContext.mContext != null) { 451 userMediaContext.mContext.unregisterReceiver(mPackageUpdateReceiver); 452 } 453 userMediaContext.mContext = mContext.createContextAsUser(userHandle, /* flags= */ 0); 454 userMediaContext.mContext.registerReceiver(mPackageUpdateReceiver, mPackageUpdateFilter, 455 Context.RECEIVER_NOT_EXPORTED); 456 457 boolean isEphemeral = isUserEphemeral(userHandle); 458 if (isEphemeral) { 459 ComponentName defaultMediaSource = getDefaultMediaSource(userId); 460 userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = 461 defaultMediaSource; 462 userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_BROWSE] = 463 defaultMediaSource; 464 playbackSource = defaultMediaSource; 465 } else { 466 playbackSource = getLastMediaSource(MEDIA_SOURCE_MODE_PLAYBACK, userId); 467 userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = 468 playbackSource; 469 userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_BROWSE] = 470 getLastMediaSource(MEDIA_SOURCE_MODE_BROWSE, userId); 471 } 472 userMediaContext.mActiveMediaController = null; 473 474 updateMediaSessionCallbackForUserLocked(userHandle); 475 } 476 notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK, userId); 477 notifyListeners(MEDIA_SOURCE_MODE_BROWSE, userId); 478 479 try { 480 startMediaConnectorService(playbackSource, 481 shouldStartPlayback(mPlayOnBootConfig, userId), userId); 482 } catch (Exception e) { 483 Slogf.e(TAG, e, "Failed to startMediaConnectorService. Source:%s user:%d", 484 playbackSource, userId); 485 } 486 } 487 488 @GuardedBy("mLock") getPrimaryMediaComponentsForUserLocked(@serIdInt int userId)489 private ComponentName[] getPrimaryMediaComponentsForUserLocked(@UserIdInt int userId) { 490 UserMediaPlayContext userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 491 return userMediaContext.mPrimaryMediaComponents; 492 } 493 getRemovedMediaSourceComponentsForUser(@serIdInt int userId)494 private ComponentName[] getRemovedMediaSourceComponentsForUser(@UserIdInt int userId) { 495 synchronized (mLock) { 496 UserMediaPlayContext userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 497 return userMediaContext.mRemovedMediaSourceComponents; 498 } 499 } 500 maybeInitSharedPrefs(@serIdInt int userId)501 private void maybeInitSharedPrefs(@UserIdInt int userId) { 502 // SharedPreferences are shared among different users thus only need initialized once. And 503 // they should be initialized after user 0 is unlocked because SharedPreferences in 504 // credential encrypted storage are not available until after user 0 is unlocked. 505 // initUser() is called when the current foreground user is unlocked, and by that time user 506 // 0 has been unlocked already, so initializing SharedPreferences in initUser() is fine. 507 if (mSharedPrefs != null) { 508 Slogf.i(TAG, "Shared preferences already set (on directory %s)" 509 + " when initializing user %d", mContext.getDataDir(), userId); 510 return; 511 } 512 Slogf.i(TAG, "Getting shared preferences when initializing user %d", userId); 513 mSharedPrefs = mContext.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE); 514 515 // Try to access the properties to make sure they were properly open 516 if (DEBUG) { 517 Slogf.d(TAG, "Number of prefs: %d", mSharedPrefs.getAll().size()); 518 } 519 } 520 521 /** 522 * Starts a service on the current user that binds to the media browser of the current media 523 * source. We start a new service because this one runs on user 0, and MediaBrowser doesn't 524 * provide an API to connect on a specific user. Additionally, this service will attempt to 525 * resume playback using the MediaSession obtained via the media browser connection, which 526 * is more reliable than using active MediaSessions from MediaSessionManager. 527 */ startMediaConnectorService(@ullable ComponentName playbackMediaSource, boolean startPlayback, @UserIdInt int userId)528 private void startMediaConnectorService(@Nullable ComponentName playbackMediaSource, 529 boolean startPlayback, @UserIdInt int userId) { 530 synchronized (mLock) { 531 Context userContext = getOrCreateUserMediaPlayContextLocked(userId).mContext; 532 if (userContext == null) { 533 Slogf.wtf(TAG, 534 "Cannot start MediaConnection service. User %d has not been initialized", 535 userId); 536 return; 537 } 538 Intent serviceStart = new Intent(MEDIA_CONNECTION_ACTION); 539 serviceStart.setPackage( 540 mContext.getResources().getString(R.string.serviceMediaConnection)); 541 serviceStart.putExtra(EXTRA_AUTOPLAY, startPlayback); 542 if (playbackMediaSource != null) { 543 serviceStart.putExtra(EXTRA_MEDIA_COMPONENT, playbackMediaSource.flattenToString()); 544 } 545 546 ComponentName result = userContext.startForegroundService(serviceStart); 547 Slogf.i(TAG, "startMediaConnectorService user: %d, source: %s, result: %s", userId, 548 playbackMediaSource, result); 549 } 550 } 551 sharedPrefsInitialized()552 private boolean sharedPrefsInitialized() { 553 if (mSharedPrefs != null) return true; 554 555 // It shouldn't reach this but let's be cautious. 556 Slogf.e(TAG, "SharedPreferences are not initialized!"); 557 String className = getClass().getName(); 558 for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { 559 // Let's print the useful logs only. 560 String log = ste.toString(); 561 if (log.contains(className)) { 562 Slogf.e(TAG, log); 563 } 564 } 565 return false; 566 } 567 isUserEphemeral(UserHandle userHandle)568 private boolean isUserEphemeral(UserHandle userHandle) { 569 return mUserHandleHelper.isEphemeralUser(userHandle); 570 } 571 setKeyEventListener()572 private void setKeyEventListener() { 573 int maxKeyCode = KeyEvent.getMaxKeyCode(); 574 ArrayList<Integer> mediaKeyCodes = new ArrayList<>(15); 575 for (int key = 1; key <= maxKeyCode; key++) { 576 if (KeyEvent.isMediaSessionKey(key)) { 577 mediaKeyCodes.add(key); 578 } 579 } 580 581 CarLocalServices.getService(CarInputService.class) 582 .registerKeyEventListener(new MediaKeyEventListener(), mediaKeyCodes); 583 } 584 585 // Sets a listener to be notified when the current power policy changes. 586 // Basically, the listener pauses the audio when a media component is disabled and resumes 587 // the audio when a media component is enabled. 588 // This is called only from init(). setPowerPolicyListener()589 private void setPowerPolicyListener() { 590 CarPowerPolicyFilter filter = new CarPowerPolicyFilter.Builder() 591 .setComponents(PowerComponent.MEDIA).build(); 592 CarLocalServices.getService(CarPowerManagementService.class) 593 .addPowerPolicyListener(filter, mPowerPolicyListener); 594 } 595 596 @Override release()597 public void release() { 598 synchronized (mLock) { 599 int userArraySize = mUserMediaPlayContexts.size(); 600 for (int i = 0; i < userArraySize; i++) { 601 clearUserDataLocked(mUserMediaPlayContexts.keyAt(i)); 602 } 603 } 604 mUserService.removeUserLifecycleListener(mUserLifecycleListener); 605 CarLocalServices.getService(CarPowerManagementService.class) 606 .removePowerPolicyListener(mPowerPolicyListener); 607 } 608 609 /** Clears the user data for {@code userId}. */ 610 @GuardedBy("mLock") clearUserDataLocked(@serIdInt int userId)611 private void clearUserDataLocked(@UserIdInt int userId) { 612 if (DEBUG) { 613 Slogf.d(TAG, "clearUserDataLocked() for user %d", userId); 614 } 615 UserMediaPlayContext userMediaContext = mUserMediaPlayContexts.get(userId); 616 if (userMediaContext == null) { 617 return; 618 } 619 620 if (userMediaContext.mContext != null) { 621 userMediaContext.mContext.unregisterReceiver(mPackageUpdateReceiver); 622 userMediaContext.mContext = null; 623 } 624 userMediaContext.mMediaSessionUpdater.unregisterCallbacks(); 625 mMediaSessionManager.removeOnActiveSessionsChangedListener( 626 userMediaContext.mSessionsListener); 627 } 628 629 @Override 630 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)631 public void dump(IndentingPrintWriter writer) { 632 writer.println("*CarMediaService*"); 633 writer.increaseIndent(); 634 writer.printf("DEBUG=%b\n", DEBUG); 635 writer.printf("mPlayOnBootConfig=%d\n", mPlayOnBootConfig); 636 writer.printf("mPlayOnMediaSourceChangedConfig=%d\n", mPlayOnMediaSourceChangedConfig); 637 writer.printf("mDefaultIndependentPlaybackConfig=%b\n", mDefaultIndependentPlaybackConfig); 638 writer.println(); 639 640 boolean hasSharedPrefs = mSharedPrefs != null; 641 synchronized (mLock) { 642 int userArraySize = mUserMediaPlayContexts.size(); 643 for (int i = 0; i < userArraySize; i++) { 644 int userId = mUserMediaPlayContexts.keyAt(i); 645 writer.printf("For user %d:\n", userId); 646 writer.increaseIndent(); 647 UserMediaPlayContext userMediaContext = mUserMediaPlayContexts.valueAt(i); 648 writer.printf("Pending init: %b\n", userMediaContext.mPendingInit); 649 dumpCurrentMediaComponentLocked(writer, "playback", MEDIA_SOURCE_MODE_PLAYBACK, 650 userId); 651 dumpCurrentMediaComponentLocked(writer, "browse", MEDIA_SOURCE_MODE_BROWSE, 652 userId); 653 MediaController mediaController = userMediaContext.mActiveMediaController; 654 if (mediaController != null) { 655 writer.printf("Current media controller: %s\n", 656 mediaController.getPackageName()); 657 writer.printf("Current browse service extra: %s\n", 658 getClassName(mediaController)); 659 } else { 660 writer.println("no active user media controller"); 661 } 662 writer.printf("Number of active media sessions (for user %d): %d\n", userId, 663 mMediaSessionManager.getActiveSessionsForUser( 664 /* notificationListener= */ null, UserHandle.of(userId)).size()); 665 666 writer.printf("Disabled by power policy: %b\n", 667 userMediaContext.mIsDisabledByPowerPolicy); 668 if (userMediaContext.mIsDisabledByPowerPolicy) { 669 writer.printf("Before being disabled by power policy, audio was %s\n", 670 userMediaContext.mWasPlayingBeforeDisabled ? "active" : "inactive"); 671 } 672 if (hasSharedPrefs) { 673 dumpLastUpdateTime(writer, userId); 674 dumpLastMediaSources(writer, "Playback", MEDIA_SOURCE_MODE_PLAYBACK, userId); 675 dumpLastMediaSources(writer, "Browse", MEDIA_SOURCE_MODE_BROWSE, userId); 676 dumpPlaybackState(writer, userId); 677 } 678 writer.decreaseIndent(); 679 } 680 } 681 682 if (hasSharedPrefs) { 683 dumpSharedPrefs(writer); 684 } else { 685 writer.println("No shared preferences"); 686 } 687 688 writer.decreaseIndent(); 689 } 690 691 @GuardedBy("mLock") 692 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpCurrentMediaComponentLocked(IndentingPrintWriter writer, String name, @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId)693 private void dumpCurrentMediaComponentLocked(IndentingPrintWriter writer, String name, 694 @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId) { 695 ComponentName componentName = getPrimaryMediaComponentsForUserLocked(userId)[mode]; 696 writer.printf("For user %d, current %s media component: %s\n", userId, name, 697 (componentName == null ? "-" : componentName.flattenToString())); 698 } 699 700 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpLastUpdateTime(IndentingPrintWriter writer, @UserIdInt int userId)701 private void dumpLastUpdateTime(IndentingPrintWriter writer, @UserIdInt int userId) { 702 long lastUpdate = mSharedPrefs.getLong(getLastUpdateKey(userId), -1); 703 writer.printf("For user %d, shared preference last updated on %d / ", userId, lastUpdate); 704 TimeUtils.dumpTime(writer, lastUpdate); 705 writer.println(); 706 } 707 708 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpLastMediaSources(IndentingPrintWriter writer, String name, @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId)709 private void dumpLastMediaSources(IndentingPrintWriter writer, String name, 710 @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId) { 711 writer.printf("%s media source history:\n", name); 712 writer.increaseIndent(); 713 List<ComponentName> lastMediaSources = getLastMediaSourcesInternal(mode, userId); 714 for (int i = 0; i < lastMediaSources.size(); i++) { 715 ComponentName componentName = lastMediaSources.get(i); 716 if (componentName == null) { 717 Slogf.e(TAG, "dump(): for user %d, empty last media source of %s" 718 + " at index %d: %s", 719 userId, mediaModeToString(mode), i, lastMediaSources); 720 continue; 721 } 722 writer.println(componentName.flattenToString()); 723 } 724 writer.decreaseIndent(); 725 } 726 727 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpPlaybackState(IndentingPrintWriter writer, @UserIdInt int userId)728 private void dumpPlaybackState(IndentingPrintWriter writer, @UserIdInt int userId) { 729 String key = getPlaybackStateKey(userId); 730 int playbackState = mSharedPrefs.getInt(key, PlaybackState.STATE_NONE); 731 writer.printf("media playback state: %d\n", playbackState); 732 } 733 734 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpSharedPrefs(IndentingPrintWriter writer)735 private void dumpSharedPrefs(IndentingPrintWriter writer) { 736 Map<String, ?> allPrefs = mSharedPrefs.getAll(); 737 writer.printf("%d shared preferences (saved on directory %s)", 738 allPrefs.size(), mContext.getDataDir()); 739 if (!Slogf.isLoggable(TAG, Log.VERBOSE) || allPrefs.isEmpty()) { 740 writer.println(); 741 return; 742 } 743 writer.println(':'); 744 } 745 746 /** 747 * @see {@link CarMediaManager#setMediaSource(ComponentName)} 748 */ 749 @Override setMediaSource(@onNull ComponentName componentName, @MediaSourceMode int mode)750 public void setMediaSource(@NonNull ComponentName componentName, 751 @MediaSourceMode int mode) { 752 assertPermission(mContext, 753 android.Manifest.permission.MEDIA_CONTENT_CONTROL); 754 UserHandle callingUser = getCallingUserHandle(); 755 if (DEBUG) { 756 Slogf.d(TAG, "Changing media source to: %s for user %s", 757 componentName.getPackageName(), callingUser); 758 } 759 setPrimaryMediaSource(componentName, mode, callingUser.getIdentifier()); 760 } 761 762 /** 763 * @see {@link CarMediaManager#getMediaSource()} 764 */ 765 @Override getMediaSource(@arMediaManager.MediaSourceMode int mode)766 public ComponentName getMediaSource(@CarMediaManager.MediaSourceMode int mode) { 767 assertPermission(mContext, 768 android.Manifest.permission.MEDIA_CONTENT_CONTROL); 769 int userId = getCallingUserHandle().getIdentifier(); 770 ComponentName componentName; 771 synchronized (mLock) { 772 componentName = getPrimaryMediaComponentsForUserLocked(userId)[mode]; 773 } 774 if (DEBUG) { 775 Slogf.d(TAG, "Getting media source mode %d for user %d: %s ", 776 mode, userId, componentName); 777 } 778 return componentName; 779 } 780 781 /** 782 * @see {@link CarMediaManager#registerMediaSourceListener(MediaSourceChangedListener)} 783 */ 784 @Override registerMediaSourceListener(ICarMediaSourceListener callback, @MediaSourceMode int mode)785 public void registerMediaSourceListener(ICarMediaSourceListener callback, 786 @MediaSourceMode int mode) { 787 int userId = getCallingUserHandle().getIdentifier(); 788 assertPermission(mContext, 789 android.Manifest.permission.MEDIA_CONTENT_CONTROL); 790 UserMediaPlayContext userMediaContext; 791 synchronized (mLock) { 792 userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 793 } 794 userMediaContext.mMediaSourceListeners[mode].register(callback); 795 } 796 797 /** 798 * @see {@link CarMediaManager#unregisterMediaSourceListener(ICarMediaSourceListener)} 799 */ 800 @Override unregisterMediaSourceListener(ICarMediaSourceListener callback, @MediaSourceMode int mode)801 public void unregisterMediaSourceListener(ICarMediaSourceListener callback, 802 @MediaSourceMode int mode) { 803 int userId = getCallingUserHandle().getIdentifier(); 804 assertPermission(mContext, 805 android.Manifest.permission.MEDIA_CONTENT_CONTROL); 806 UserMediaPlayContext userMediaContext; 807 synchronized (mLock) { 808 userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 809 } 810 userMediaContext.mMediaSourceListeners[mode].unregister(callback); 811 } 812 813 @Override getLastMediaSources(@arMediaManager.MediaSourceMode int mode)814 public List<ComponentName> getLastMediaSources(@CarMediaManager.MediaSourceMode int mode) { 815 assertPermission(mContext, 816 android.Manifest.permission.MEDIA_CONTENT_CONTROL); 817 int userId = getCallingUserHandle().getIdentifier(); 818 return getLastMediaSourcesInternal(mode, userId); 819 } 820 821 /** 822 * Returns the last media sources for the specified {@code mode} and {@code userId}. 823 * 824 * <p>Anywhere in this file, do not use the public {@link getLastMediaSources(int)} method. 825 * Use this method instead. 826 */ getLastMediaSourcesInternal( @arMediaManager.MediaSourceMode int mode, @UserIdInt int userId)827 private List<ComponentName> getLastMediaSourcesInternal( 828 @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId) { 829 String key = getMediaSourceKey(mode, userId); 830 String serialized = mSharedPrefs.getString(key, ""); 831 List<String> componentNames = getComponentNameList(serialized); 832 ArrayList<ComponentName> results = new ArrayList<>(componentNames.size()); 833 for (int i = 0; i < componentNames.size(); i++) { 834 results.add(ComponentName.unflattenFromString(componentNames.get(i))); 835 } 836 return results; 837 } 838 839 /** See {@link CarMediaManager#isIndependentPlaybackConfig}. */ 840 @Override 841 @TestApi isIndependentPlaybackConfig()842 public boolean isIndependentPlaybackConfig() { 843 assertPermission(mContext, 844 android.Manifest.permission.MEDIA_CONTENT_CONTROL); 845 int callingUser = getCallingUserHandle().getIdentifier(); 846 return isIndependentPlaybackConfigInternal(callingUser); 847 } 848 849 /** 850 * Returns independent playback config for the specified {@code userId}. 851 * 852 * <p>Anywhere in this file, do not use the public {@link isIndependentPlaybackConfig()} method. 853 */ isIndependentPlaybackConfigInternal(@serIdInt int userId)854 private boolean isIndependentPlaybackConfigInternal(@UserIdInt int userId) { 855 synchronized (mLock) { 856 return getOrCreateUserMediaPlayContextLocked(userId).mIndependentPlaybackConfig; 857 } 858 } 859 860 /** See {@link CarMediaManager#setIndependentPlaybackConfig}. */ 861 @Override 862 @TestApi setIndependentPlaybackConfig(boolean independent)863 public void setIndependentPlaybackConfig(boolean independent) { 864 assertPermission(mContext, 865 android.Manifest.permission.MEDIA_CONTENT_CONTROL); 866 int callingUser = getCallingUserHandle().getIdentifier(); 867 synchronized (mLock) { 868 getOrCreateUserMediaPlayContextLocked(callingUser).mIndependentPlaybackConfig = 869 independent; 870 } 871 } 872 873 /** Clears data for {@code fromUserId}, and initializes data for {@code toUserId}. */ onUserSwitch(@serIdInt int fromUserId, @UserIdInt int toUserId)874 private void onUserSwitch(@UserIdInt int fromUserId, @UserIdInt int toUserId) { 875 if (DEBUG) { 876 Slogf.d(TAG, "onUserSwitch() fromUserId=%d, toUserId=%d", fromUserId, toUserId); 877 } 878 // Clean up the data of the fromUser. 879 if (fromUserId != UserHandle.SYSTEM.getIdentifier()) { 880 onUserInvisible(fromUserId); 881 } 882 // Initialize the data of the toUser. 883 onUserVisible(toUserId); 884 } 885 onUserVisible(@serIdInt int userId)886 private void onUserVisible(@UserIdInt int userId) { 887 if (DEBUG) { 888 Slogf.d(TAG, "onUserVisible() for user=%d", userId); 889 } 890 maybeInitUser(userId); 891 } 892 893 /** Clears the user data when the user becomes invisible. */ onUserInvisible(@serIdInt int userId)894 private void onUserInvisible(@UserIdInt int userId) { 895 if (DEBUG) { 896 Slogf.d(TAG, "onUserInvisible(): userId=%d. Clearing data for the user.", userId); 897 } 898 synchronized (mLock) { 899 clearUserDataLocked(userId); 900 mUserMediaPlayContexts.delete(userId); 901 } 902 } 903 904 // TODO(b/153115826): this method was used to be called from the ICar binder thread, but it's 905 // now called by UserCarService. Currently UserCarService is calling every listener in one 906 // non-main thread, but it's not clear how the final behavior will be. So, for now it's ok 907 // to post it to mMainHandler, but once b/145689885 is fixed, we might not need it. onUserUnlocked(@serIdInt int userId)908 private void onUserUnlocked(@UserIdInt int userId) { 909 if (DEBUG) { 910 Slogf.d(TAG, "onUserUnlocked(): userId=%d", userId); 911 } 912 mCommonThreadHandler.post(() -> { 913 // No need to handle system user, but still need to handle background users. 914 if (userId == UserHandle.SYSTEM.getIdentifier()) { 915 return; 916 } 917 UserMediaPlayContext userMediaPlayContext; 918 boolean isPendingInit; 919 synchronized (mLock) { 920 userMediaPlayContext = getOrCreateUserMediaPlayContextLocked(userId); 921 isPendingInit = userMediaPlayContext.mPendingInit; 922 } 923 if (DEBUG) { 924 Slogf.d(TAG, "onUserUnlocked(): userId=%d pendingInit=%b", userId, 925 userMediaPlayContext.mPendingInit); 926 } 927 if (isPendingInit) { 928 initUser(userId); 929 synchronized (mLock) { 930 userMediaPlayContext.mPendingInit = false; 931 } 932 if (DEBUG) { 933 Slogf.d(TAG, "User %d is now unlocked", userId); 934 } 935 } 936 }); 937 } 938 939 @GuardedBy("mLock") updateMediaSessionCallbackForUserLocked(UserHandle userHandle)940 private void updateMediaSessionCallbackForUserLocked(UserHandle userHandle) { 941 int userId = userHandle.getIdentifier(); 942 UserMediaPlayContext userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 943 SessionChangedListener sessionsListener = userMediaContext.mSessionsListener; 944 if (sessionsListener != null) { 945 mMediaSessionManager.removeOnActiveSessionsChangedListener(sessionsListener); 946 } 947 sessionsListener = new SessionChangedListener(userId); 948 userMediaContext.mSessionsListener = sessionsListener; 949 mMediaSessionManager.addOnActiveSessionsChangedListener(null, userHandle, 950 new HandlerExecutor(mHandler), sessionsListener); 951 952 MediaSessionUpdater sessionUpdater = new MediaSessionUpdater(userId); 953 userMediaContext.mMediaSessionUpdater = sessionUpdater; 954 sessionUpdater.registerCallbacks(mMediaSessionManager.getActiveSessionsForUser(null, 955 userHandle)); 956 } 957 958 /** 959 * Attempts to stop the current source using MediaController.TransportControls.stop() 960 * This method also unregisters callbacks to the active media controller before calling stop(), 961 * to preserve the PlaybackState before stopping. 962 */ stopAndUnregisterCallback(@serIdInt int userId)963 private void stopAndUnregisterCallback(@UserIdInt int userId) { 964 UserMediaPlayContext userMediaContext; 965 MediaController mediaController; 966 synchronized (mLock) { 967 userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 968 mediaController = userMediaContext.mActiveMediaController; 969 } 970 if (mediaController == null) { 971 if (DEBUG) { 972 Slogf.d(TAG, "stopAndUnregisterCallback() for user %d." 973 + " Do nothing as there is no active media controller.", userId); 974 } 975 return; 976 } 977 978 mediaController.unregisterCallback(userMediaContext.mMediaControllerCallback); 979 if (DEBUG) { 980 Slogf.d(TAG, "stopping %s", mediaController.getPackageName()); 981 } 982 TransportControls controls = mediaController.getTransportControls(); 983 if (controls != null) { 984 // In order to prevent some apps from taking back the audio focus after being stopped, 985 // first call pause, if the app supports pause. This does not affect the saved source 986 // or the playback state, because the callback has already been unregistered. 987 PlaybackState playbackState = mediaController.getPlaybackState(); 988 if (playbackState != null 989 && (playbackState.getActions() & PlaybackState.ACTION_PAUSE) != 0) { 990 if (DEBUG) { 991 Slogf.d(TAG, "Call pause before stop"); 992 } 993 controls.pause(); 994 } 995 controls.stop(); 996 } else { 997 Slogf.e(TAG, "Can't stop playback, transport controls unavailable %s", 998 mediaController.getPackageName()); 999 } 1000 } 1001 1002 @GuardedBy("mLock") getOrCreateUserMediaPlayContextLocked(@serIdInt int userId)1003 private UserMediaPlayContext getOrCreateUserMediaPlayContextLocked(@UserIdInt int userId) { 1004 UserMediaPlayContext userMediaContext = mUserMediaPlayContexts.get(userId); 1005 if (userMediaContext == null) { 1006 userMediaContext = new UserMediaPlayContext(userId, mDefaultIndependentPlaybackConfig); 1007 mUserMediaPlayContexts.set(userId, userMediaContext); 1008 if (DEBUG) { 1009 Slogf.d(TAG, "Create a UserMediaPlayContext for user %d", userId); 1010 } 1011 } 1012 1013 return userMediaContext; 1014 } 1015 1016 /** A container to store per-user media play context data. */ 1017 private final class UserMediaPlayContext { 1018 1019 @Nullable 1020 private Context mContext; 1021 // MediaController for the user's active media session. This controller can be null 1022 // if playback has not been started yet. 1023 private MediaController mActiveMediaController; 1024 private int mCurrentPlaybackState; 1025 private boolean mIsDisabledByPowerPolicy; 1026 private boolean mWasPreviouslyDisabledByPowerPolicy; 1027 private boolean mWasPlayingBeforeDisabled; 1028 private boolean mIndependentPlaybackConfig; 1029 private final ComponentName[] mPrimaryMediaComponents; 1030 // The component name of the last media source that was removed while being primary. 1031 private final ComponentName[] mRemovedMediaSourceComponents; 1032 private boolean mPendingInit; 1033 1034 private final RemoteCallbackList<ICarMediaSourceListener>[] mMediaSourceListeners; 1035 private MediaSessionUpdater mMediaSessionUpdater; 1036 private SessionChangedListener mSessionsListener; 1037 private final MediaController.Callback mMediaControllerCallback; 1038 UserMediaPlayContext(@serIdInt int userId, boolean independentPlaybackConfig)1039 UserMediaPlayContext(@UserIdInt int userId, boolean independentPlaybackConfig) { 1040 mIndependentPlaybackConfig = independentPlaybackConfig; 1041 mPrimaryMediaComponents = new ComponentName[MEDIA_SOURCE_MODES]; 1042 mRemovedMediaSourceComponents = new ComponentName[MEDIA_SOURCE_MODES]; 1043 mMediaSourceListeners = new RemoteCallbackList[] {new RemoteCallbackList(), 1044 new RemoteCallbackList()}; 1045 mMediaSessionUpdater = new MediaSessionUpdater(userId); 1046 mSessionsListener = new SessionChangedListener(userId); 1047 mMediaControllerCallback = new ActiveMediaControllerCallback(userId); 1048 } 1049 } 1050 1051 private final class ActiveMediaControllerCallback extends MediaController.Callback { 1052 1053 private final @UserIdInt int mUserId; 1054 ActiveMediaControllerCallback(@serIdInt int userId)1055 ActiveMediaControllerCallback(@UserIdInt int userId) { 1056 mUserId = userId; 1057 } 1058 1059 @Override onPlaybackStateChanged(PlaybackState state)1060 public void onPlaybackStateChanged(PlaybackState state) { 1061 savePlaybackState(state, mUserId); 1062 } 1063 } 1064 1065 private class SessionChangedListener implements OnActiveSessionsChangedListener { 1066 private final @UserIdInt int mUserId; 1067 SessionChangedListener(int userId)1068 SessionChangedListener(int userId) { 1069 mUserId = userId; 1070 } 1071 1072 @Override onActiveSessionsChanged(List<MediaController> controllers)1073 public void onActiveSessionsChanged(List<MediaController> controllers) { 1074 if (DEBUG) { 1075 Slogf.d(TAG, "onActiveSessionsChanged() for user %d, controllers: %s", 1076 mUserId, controllers); 1077 } 1078 // Filter controllers based on their user ids. 1079 ArrayList<MediaController> userControllers = new ArrayList<>(controllers.size()); 1080 for (int i = 0; i < controllers.size(); i++) { 1081 MediaController controller = controllers.get(i); 1082 int userId = UserHandle.getUserHandleForUid( 1083 controller.getSessionToken().getUid()).getIdentifier(); 1084 if (userId == mUserId) { 1085 userControllers.add(controller); 1086 } else { 1087 Slogf.w(TAG, "onActiveSessionsChanged() received a change for " 1088 + "a different user: listener user %d, controller %s for user %d", 1089 mUserId, controller, userId); 1090 } 1091 } 1092 UserMediaPlayContext userMediaContext; 1093 synchronized (mLock) { 1094 userMediaContext = getOrCreateUserMediaPlayContextLocked(mUserId); 1095 } 1096 userMediaContext.mMediaSessionUpdater.registerCallbacks(userControllers); 1097 } 1098 } 1099 1100 private class MediaControllerCallback extends MediaController.Callback { 1101 1102 private final @UserIdInt int mUserId; 1103 private final MediaController mMediaController; 1104 private PlaybackState mPreviousPlaybackState; 1105 MediaControllerCallback(MediaController mediaController, @UserIdInt int userId)1106 private MediaControllerCallback(MediaController mediaController, @UserIdInt int userId) { 1107 mUserId = userId; 1108 mMediaController = mediaController; 1109 PlaybackState state = mediaController.getPlaybackState(); 1110 mPreviousPlaybackState = state; 1111 } 1112 register()1113 private void register() { 1114 mMediaController.registerCallback(this); 1115 } 1116 unregister()1117 private void unregister() { 1118 mMediaController.unregisterCallback(this); 1119 } 1120 1121 @Override onPlaybackStateChanged(@ullable PlaybackState state)1122 public void onPlaybackStateChanged(@Nullable PlaybackState state) { 1123 if (DEBUG) { 1124 Slogf.d(TAG, "onPlaybackStateChanged() for user %d; previous state: %s," 1125 + " new state: %s", mUserId, mPreviousPlaybackState, state.getState()); 1126 } 1127 if (state != null && state.isActive() 1128 && (mPreviousPlaybackState == null || !mPreviousPlaybackState.isActive())) { 1129 ComponentName mediaSource = getMediaSource(mMediaController.getPackageName(), 1130 getClassName(mMediaController), mUserId); 1131 if (mediaSource != null && Slogf.isLoggable(TAG, Log.INFO)) { 1132 synchronized (mLock) { 1133 if (!mediaSource.equals(getPrimaryMediaComponentsForUserLocked(mUserId) 1134 [MEDIA_SOURCE_MODE_PLAYBACK])) { 1135 Slogf.i(TAG, "Changing media source for user %d due to playback state " 1136 + "change: %s", mUserId, mediaSource.flattenToString()); 1137 } 1138 } 1139 } 1140 setPrimaryMediaSource(mediaSource, MEDIA_SOURCE_MODE_PLAYBACK, mUserId); 1141 } 1142 mPreviousPlaybackState = state; 1143 } 1144 } 1145 1146 private class MediaSessionUpdater { 1147 1148 private final @UserIdInt int mUserId; 1149 private Map<Token, MediaControllerCallback> mCallbacks = new HashMap<>(); 1150 MediaSessionUpdater(@serIdInt int userId)1151 MediaSessionUpdater(@UserIdInt int userId) { 1152 mUserId = userId; 1153 } 1154 1155 /** 1156 * Register a {@link MediaControllerCallback} for each given controller. Note that if a 1157 * controller was already watched, we don't register a callback again. This prevents an 1158 * undesired revert of the primary media source. Callbacks for previously watched 1159 * controllers that are not present in the given list are unregistered. 1160 */ registerCallbacks(List<MediaController> newControllers)1161 private void registerCallbacks(List<MediaController> newControllers) { 1162 1163 List<MediaController> additions = new ArrayList<>(newControllers.size()); 1164 Map<MediaSession.Token, MediaControllerCallback> updatedCallbacks = 1165 new HashMap<>(newControllers.size()); 1166 for (MediaController controller : newControllers) { 1167 MediaSession.Token token = controller.getSessionToken(); 1168 MediaControllerCallback callback = mCallbacks.get(token); 1169 if (callback == null) { 1170 callback = new MediaControllerCallback(controller, mUserId); 1171 callback.register(); 1172 additions.add(controller); 1173 } 1174 updatedCallbacks.put(token, callback); 1175 } 1176 1177 for (MediaSession.Token token : mCallbacks.keySet()) { 1178 if (!updatedCallbacks.containsKey(token)) { 1179 mCallbacks.get(token).unregister(); 1180 } 1181 } 1182 1183 mCallbacks = updatedCallbacks; 1184 updatePrimaryMediaSourceWithCurrentlyPlaying(additions, mUserId); 1185 // If there are no playing media sources, and we don't currently have the controller 1186 // for the active source, check the active sessions for a matching controller. If this 1187 // is called after a user switch, its possible for a matching controller to already be 1188 // active before the user is unlocked, so we check all of the current controllers 1189 synchronized (mLock) { 1190 UserMediaPlayContext userMediaContext = 1191 getOrCreateUserMediaPlayContextLocked(mUserId); 1192 if (userMediaContext.mActiveMediaController == null) { 1193 updateActiveMediaControllerLocked(newControllers, mUserId); 1194 } 1195 } 1196 } 1197 1198 /** 1199 * Unregister all MediaController callbacks 1200 */ unregisterCallbacks()1201 private void unregisterCallbacks() { 1202 for (Map.Entry<Token, MediaControllerCallback> entry : mCallbacks.entrySet()) { 1203 entry.getValue().unregister(); 1204 } 1205 } 1206 } 1207 1208 /** 1209 * Updates the primary media source, then notifies content observers of the change 1210 * Will update both the playback and browse sources if independent playback is not supported 1211 */ setPrimaryMediaSource(ComponentName componentName, @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId)1212 private void setPrimaryMediaSource(ComponentName componentName, 1213 @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId) { 1214 if (DEBUG) { 1215 Slogf.d(TAG, "setPrimaryMediaSource(component=%s, mode=%d, userId=%d)", 1216 componentName, mode, userId); 1217 } 1218 ComponentName mediaComponent; 1219 synchronized (mLock) { 1220 mediaComponent = getPrimaryMediaComponentsForUserLocked(userId)[mode]; 1221 } 1222 if (mediaComponent != null && mediaComponent.equals((componentName))) { 1223 return; 1224 } 1225 1226 if (!isIndependentPlaybackConfigInternal(userId)) { 1227 setPlaybackMediaSource(componentName, userId); 1228 setBrowseMediaSource(componentName, userId); 1229 } else if (mode == MEDIA_SOURCE_MODE_PLAYBACK) { 1230 setPlaybackMediaSource(componentName, userId); 1231 } else if (mode == MEDIA_SOURCE_MODE_BROWSE) { 1232 setBrowseMediaSource(componentName, userId); 1233 } 1234 // Android logs app usage into UsageStatsManager. ACTIVITY_RESUMED and ACTIVITY_STOPPED 1235 // events do not capture media app usage on AAOS because apps are hosted by a proxy such as 1236 // Media Center. Reporting a USER_INTERACTION event in setPrimaryMediaSource allows 1237 // attribution of non-foreground media app interactions to the app's package name 1238 if (isPlatformVersionAtLeastU() && componentName != null) { 1239 UsageStatsManagerHelper.reportUserInteraction(mUsageStatsManager, 1240 componentName.getPackageName(), userId); 1241 } 1242 } 1243 setPlaybackMediaSource(ComponentName playbackMediaSource, @UserIdInt int userId)1244 private void setPlaybackMediaSource(ComponentName playbackMediaSource, @UserIdInt int userId) { 1245 stopAndUnregisterCallback(userId); 1246 UserMediaPlayContext userMediaContext; 1247 1248 synchronized (mLock) { 1249 userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 1250 userMediaContext.mActiveMediaController = null; 1251 userMediaContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = 1252 playbackMediaSource; 1253 } 1254 1255 UserHandle userHandle = UserHandle.of(userId); 1256 if (playbackMediaSource != null 1257 && !TextUtils.isEmpty(playbackMediaSource.flattenToString())) { 1258 if (!isUserEphemeral(userHandle)) { 1259 saveLastMediaSource(playbackMediaSource, MEDIA_SOURCE_MODE_PLAYBACK, userId); 1260 } 1261 if (playbackMediaSource 1262 .equals(getRemovedMediaSourceComponentsForUser(userId) 1263 [MEDIA_SOURCE_MODE_PLAYBACK])) { 1264 getRemovedMediaSourceComponentsForUser(userId)[MEDIA_SOURCE_MODE_PLAYBACK] = null; 1265 } 1266 notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK, userId); 1267 startMediaConnectorService(playbackMediaSource, 1268 shouldStartPlayback(mPlayOnMediaSourceChangedConfig, userId), userId); 1269 } else { 1270 Slogf.i(TAG, "Media source is null for user %d, skip starting media " 1271 + "connector service", userId); 1272 // We will still notify the listeners that playback changed 1273 notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK, userId); 1274 } 1275 1276 // Reset current playback state for the new source, in the case that the app is in an error 1277 // state (e.g. not signed in). This state will be updated from the app callback registered 1278 // below, to make sure mCurrentPlaybackState reflects the current source only. 1279 synchronized (mLock) { 1280 userMediaContext.mCurrentPlaybackState = PlaybackState.STATE_NONE; 1281 updateActiveMediaControllerLocked(mMediaSessionManager 1282 .getActiveSessionsForUser(null, userHandle), userId); 1283 } 1284 } 1285 setBrowseMediaSource(ComponentName browseMediaSource, @UserIdInt int userId)1286 private void setBrowseMediaSource(ComponentName browseMediaSource, @UserIdInt int userId) { 1287 synchronized (mLock) { 1288 getPrimaryMediaComponentsForUserLocked(userId)[MEDIA_SOURCE_MODE_BROWSE] = 1289 browseMediaSource; 1290 } 1291 1292 if (browseMediaSource != null && !TextUtils.isEmpty(browseMediaSource.flattenToString())) { 1293 if (!isUserEphemeral(UserHandle.of(userId))) { 1294 saveLastMediaSource(browseMediaSource, MEDIA_SOURCE_MODE_BROWSE, userId); 1295 } 1296 if (browseMediaSource 1297 .equals(getRemovedMediaSourceComponentsForUser( 1298 userId)[MEDIA_SOURCE_MODE_BROWSE])) { 1299 getRemovedMediaSourceComponentsForUser(userId)[MEDIA_SOURCE_MODE_BROWSE] = null; 1300 } 1301 } 1302 1303 notifyListeners(MEDIA_SOURCE_MODE_BROWSE, userId); 1304 } 1305 notifyListeners(@arMediaManager.MediaSourceMode int mode, @UserIdInt int userId)1306 private void notifyListeners(@CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId) { 1307 synchronized (mLock) { 1308 UserMediaPlayContext userMediaContext = getOrCreateUserMediaPlayContextLocked(userId); 1309 RemoteCallbackList<ICarMediaSourceListener> callbackList = 1310 userMediaContext.mMediaSourceListeners[mode]; 1311 ComponentName primaryMediaComponent = userMediaContext.mPrimaryMediaComponents[mode]; 1312 int i = callbackList.beginBroadcast(); 1313 if (DEBUG) { 1314 Slogf.d(TAG, "Notify %d media source listeners for mode %d, user %d", 1315 i, mode, userId); 1316 } 1317 while (i-- > 0) { 1318 try { 1319 ICarMediaSourceListener callback = callbackList.getBroadcastItem(i); 1320 callback.onMediaSourceChanged(primaryMediaComponent); 1321 } catch (RemoteException e) { 1322 Slogf.e(TAG, e, "calling onMediaSourceChanged failed for user %d", userId); 1323 } 1324 } 1325 callbackList.finishBroadcast(); 1326 } 1327 } 1328 1329 /** 1330 * Finds the currently playing media source, then updates the active source if the component 1331 * name is different. 1332 */ updatePrimaryMediaSourceWithCurrentlyPlaying( List<MediaController> controllers, @UserIdInt int userId)1333 private void updatePrimaryMediaSourceWithCurrentlyPlaying( 1334 List<MediaController> controllers, @UserIdInt int userId) { 1335 for (MediaController controller : controllers) { 1336 if (controller.getPlaybackState() != null && controller.getPlaybackState().isActive()) { 1337 String newPackageName = controller.getPackageName(); 1338 String newClassName = getClassName(controller); 1339 if (!matchPrimaryMediaSource(newPackageName, newClassName, 1340 MEDIA_SOURCE_MODE_PLAYBACK, userId)) { 1341 ComponentName mediaSource = 1342 getMediaSource(newPackageName, newClassName, userId); 1343 if (Slogf.isLoggable(TAG, Log.INFO)) { 1344 if (mediaSource != null) { 1345 Slogf.i(TAG, 1346 "MediaController changed, updating media source for user %d " 1347 + "to: %s", userId, mediaSource.flattenToString()); 1348 } else { 1349 // Some apps, like Chrome, have a MediaSession but no 1350 // MediaBrowseService. Media Center doesn't consider such apps as 1351 // valid media sources. 1352 Slogf.i(TAG, 1353 "MediaController changed, but no media browse service for user" 1354 + " %d found in package: %s", userId, newPackageName); 1355 } 1356 } 1357 setPrimaryMediaSource(mediaSource, MEDIA_SOURCE_MODE_PLAYBACK, userId); 1358 } 1359 return; 1360 } 1361 } 1362 } 1363 matchPrimaryMediaSource(String newPackageName, String newClassName, @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId)1364 private boolean matchPrimaryMediaSource(String newPackageName, String newClassName, 1365 @CarMediaManager.MediaSourceMode int mode, @UserIdInt int userId) { 1366 synchronized (mLock) { 1367 ComponentName mediaComponent = getPrimaryMediaComponentsForUserLocked(userId)[mode]; 1368 if (mediaComponent != null 1369 && mediaComponent.getPackageName().equals(newPackageName)) { 1370 // If the class name of currently active source is not specified, only checks 1371 // package name; otherwise checks both package name and class name. 1372 if (TextUtils.isEmpty(newClassName)) { 1373 return true; 1374 } else { 1375 return newClassName.equals(mediaComponent.getClassName()); 1376 } 1377 } 1378 } 1379 return false; 1380 } 1381 1382 /** 1383 * Returns {@code true} if the provided component has a valid {@link MediaBrowserService}. 1384 */ 1385 @VisibleForTesting isMediaService(ComponentName componentName, @UserIdInt int userId)1386 public boolean isMediaService(ComponentName componentName, @UserIdInt int userId) { 1387 return getMediaService(componentName, userId) != null; 1388 } 1389 1390 /* 1391 * Gets the media service that matches the componentName for the specified user. 1392 */ getMediaService(ComponentName componentName, @UserIdInt int userId)1393 private ComponentName getMediaService(ComponentName componentName, @UserIdInt int userId) { 1394 String packageName = componentName.getPackageName(); 1395 String className = componentName.getClassName(); 1396 1397 PackageManager packageManager = mContext.getPackageManager(); 1398 Intent mediaIntent = new Intent(); 1399 mediaIntent.setPackage(packageName); 1400 mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE); 1401 List<ResolveInfo> mediaServices = packageManager.queryIntentServicesAsUser(mediaIntent, 1402 PackageManager.GET_RESOLVED_FILTER, UserHandle.of(userId)); 1403 1404 for (ResolveInfo service : mediaServices) { 1405 String serviceName = service.serviceInfo.name; 1406 if (!TextUtils.isEmpty(serviceName) 1407 // If className is not specified, returns the first service in the package; 1408 // otherwise returns the matched service. 1409 // TODO(b/136274456): find a proper way to handle the case where there are 1410 // multiple services and the className is not specified. 1411 && (TextUtils.isEmpty(className) || serviceName.equals(className))) { 1412 return new ComponentName(packageName, serviceName); 1413 } 1414 } 1415 1416 if (DEBUG) { 1417 Slogf.d(TAG, "No MediaBrowseService for user %d with ComponentName: %s", 1418 userId, componentName.flattenToString()); 1419 } 1420 return null; 1421 } 1422 1423 /* 1424 * Gets the component name of the media service. 1425 */ 1426 @Nullable getMediaSource(String packageName, String className, @UserIdInt int userId)1427 private ComponentName getMediaSource(String packageName, String className, 1428 @UserIdInt int userId) { 1429 return getMediaService(new ComponentName(packageName, className), userId); 1430 } 1431 saveLastMediaSource(ComponentName component, int mode, @UserIdInt int userId)1432 private void saveLastMediaSource(ComponentName component, int mode, @UserIdInt int userId) { 1433 if (!sharedPrefsInitialized()) { 1434 return; 1435 } 1436 String componentName = component.flattenToString(); 1437 String key = getMediaSourceKey(mode, userId); 1438 String serialized = mSharedPrefs.getString(key, null); 1439 String modeName = null; 1440 if (DEBUG) { 1441 modeName = mediaModeToString(mode); 1442 } 1443 1444 if (serialized == null) { 1445 if (DEBUG) { 1446 Slogf.d(TAG, "saveLastMediaSource(%s, %s, %d): no value for key %s", 1447 componentName, modeName, userId, key); 1448 } 1449 getSharedPrefsForWriting(userId).putString(key, componentName).apply(); 1450 } else { 1451 Deque<String> componentNames = new ArrayDeque<>(getComponentNameList(serialized)); 1452 componentNames.remove(componentName); 1453 componentNames.addFirst(componentName); 1454 String newSerialized = serializeComponentNameList(componentNames); 1455 if (DEBUG) { 1456 Slogf.d(TAG, "saveLastMediaSource(%s, %s, %d): updating %s from %s to %s", 1457 componentName, modeName, userId, key, serialized, newSerialized); 1458 } 1459 getSharedPrefsForWriting(userId).putString(key, newSerialized).apply(); 1460 } 1461 } 1462 getLastMediaSource(int mode, @UserIdInt int userId)1463 private @NonNull ComponentName getLastMediaSource(int mode, @UserIdInt int userId) { 1464 if (sharedPrefsInitialized()) { 1465 String key = getMediaSourceKey(mode, userId); 1466 String serialized = mSharedPrefs.getString(key, ""); 1467 if (!TextUtils.isEmpty(serialized)) { 1468 for (String name : getComponentNameList(serialized)) { 1469 ComponentName componentName = ComponentName.unflattenFromString(name); 1470 if (isMediaService(componentName, userId)) { 1471 return componentName; 1472 } 1473 } 1474 } 1475 } 1476 return getDefaultMediaSource(userId); 1477 } 1478 getDefaultMediaSource(@serIdInt int userId)1479 private ComponentName getDefaultMediaSource(@UserIdInt int userId) { 1480 String defaultMediaSource = mContext.getString(R.string.config_defaultMediaSource); 1481 ComponentName defaultComponent = ComponentName.unflattenFromString(defaultMediaSource); 1482 if (isMediaService(defaultComponent, userId)) { 1483 return defaultComponent; 1484 } 1485 Slogf.e(TAG, "No media service for user %d in the default component: %s", 1486 userId, defaultComponent); 1487 return null; 1488 } 1489 serializeComponentNameList(Deque<String> componentNames)1490 private String serializeComponentNameList(Deque<String> componentNames) { 1491 return String.join(COMPONENT_NAME_SEPARATOR, componentNames); 1492 } 1493 getComponentNameList(String serialized)1494 private List<String> getComponentNameList(String serialized) { 1495 String[] componentNames = serialized.split(COMPONENT_NAME_SEPARATOR); 1496 return (Arrays.asList(componentNames)); 1497 } 1498 savePlaybackState(PlaybackState playbackState, @UserIdInt int userId)1499 private void savePlaybackState(PlaybackState playbackState, @UserIdInt int userId) { 1500 if (!sharedPrefsInitialized()) { 1501 return; 1502 } 1503 if (isUserEphemeral(UserHandle.of(userId))) { 1504 return; 1505 } 1506 int state = playbackState != null ? playbackState.getState() : PlaybackState.STATE_NONE; 1507 synchronized (mLock) { 1508 getOrCreateUserMediaPlayContextLocked(userId).mCurrentPlaybackState = state; 1509 } 1510 1511 String key = getPlaybackStateKey(userId); 1512 if (DEBUG) { 1513 Slogf.d(TAG, "savePlaybackState() for user %d: %s = %d)", userId, key, state); 1514 } 1515 getSharedPrefsForWriting(userId).putInt(key, state).apply(); 1516 } 1517 1518 /** 1519 * Builds a string key for saving the playback state for a specific media source (and user) 1520 */ getPlaybackStateKey(@serIdInt int userId)1521 private String getPlaybackStateKey(@UserIdInt int userId) { 1522 ComponentName mediaComponent; 1523 synchronized (mLock) { 1524 mediaComponent = 1525 getPrimaryMediaComponentsForUserLocked(userId)[MEDIA_SOURCE_MODE_PLAYBACK]; 1526 } 1527 StringBuilder builder = new StringBuilder().append(PLAYBACK_STATE_KEY).append(userId); 1528 if (mediaComponent != null) { 1529 builder.append(mediaComponent.flattenToString()); 1530 } 1531 return builder.toString(); 1532 } 1533 getMediaSourceKey(int mode, @UserIdInt int userId)1534 private String getMediaSourceKey(int mode, @UserIdInt int userId) { 1535 return SOURCE_KEY + mode + SOURCE_KEY_SEPARATOR + userId; 1536 } 1537 getLastUpdateKey(@serIdInt int userId)1538 private String getLastUpdateKey(@UserIdInt int userId) { 1539 return LAST_UPDATE_KEY + userId; 1540 } 1541 1542 /** 1543 * Updates active media controller from the list that has the same component name as the primary 1544 * media component. Clears callback and resets media controller to null if not found. 1545 */ 1546 @GuardedBy("mLock") updateActiveMediaControllerLocked(List<MediaController> mediaControllers, @UserIdInt int userId)1547 private void updateActiveMediaControllerLocked(List<MediaController> mediaControllers, 1548 @UserIdInt int userId) { 1549 UserMediaPlayContext userMediaPlayContext = getOrCreateUserMediaPlayContextLocked(userId); 1550 if (userMediaPlayContext.mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] == null) { 1551 return; 1552 } 1553 if (userMediaPlayContext.mActiveMediaController != null) { 1554 userMediaPlayContext.mActiveMediaController.unregisterCallback( 1555 userMediaPlayContext.mMediaControllerCallback); 1556 userMediaPlayContext.mActiveMediaController = null; 1557 } 1558 for (MediaController controller : mediaControllers) { 1559 if (matchPrimaryMediaSource(controller.getPackageName(), getClassName(controller), 1560 MEDIA_SOURCE_MODE_PLAYBACK, userId)) { 1561 userMediaPlayContext.mActiveMediaController = controller; 1562 PlaybackState state = controller.getPlaybackState(); 1563 savePlaybackState(state, userId); 1564 // Specify Handler to receive callbacks on, to avoid defaulting to the calling 1565 // thread; this method can be called from the MediaSessionManager callback. 1566 // Using the version of this method without passing a handler causes a 1567 // RuntimeException for failing to create a Handler. 1568 controller.registerCallback(userMediaPlayContext.mMediaControllerCallback, 1569 mHandler); 1570 return; 1571 } 1572 } 1573 } 1574 1575 /** 1576 * Returns whether we should autoplay the current media source 1577 */ shouldStartPlayback(int config, @UserIdInt int userId)1578 private boolean shouldStartPlayback(int config, @UserIdInt int userId) { 1579 switch (config) { 1580 case AUTOPLAY_CONFIG_NEVER: 1581 return false; 1582 case AUTOPLAY_CONFIG_ALWAYS: 1583 return true; 1584 case AUTOPLAY_CONFIG_RETAIN_PER_SOURCE: 1585 if (!sharedPrefsInitialized()) { 1586 return false; 1587 } 1588 int savedState = 1589 mSharedPrefs.getInt(getPlaybackStateKey(userId), PlaybackState.STATE_NONE); 1590 if (DEBUG) { 1591 Slogf.d(TAG, "Getting saved playback state %d for user %d. Last saved on %d", 1592 savedState, userId, 1593 mSharedPrefs.getLong(getLastUpdateKey(userId), -1)); 1594 } 1595 return savedState == PlaybackState.STATE_PLAYING; 1596 case AUTOPLAY_CONFIG_RETAIN_PREVIOUS: 1597 int currentPlaybackState; 1598 synchronized (mLock) { 1599 currentPlaybackState = 1600 getOrCreateUserMediaPlayContextLocked(userId).mCurrentPlaybackState; 1601 } 1602 return currentPlaybackState == PlaybackState.STATE_PLAYING; 1603 default: 1604 Slogf.e(TAG, "Unsupported playback configuration: " + config); 1605 return false; 1606 } 1607 } 1608 1609 /** 1610 * Gets the editor used to update shared preferences. 1611 */ getSharedPrefsForWriting(@serIdInt int userId)1612 private SharedPreferences.Editor getSharedPrefsForWriting(@UserIdInt int userId) { 1613 long now = System.currentTimeMillis(); 1614 String lastUpdateKey = getLastUpdateKey(userId); 1615 Slogf.i(TAG, "Updating %s to %d", lastUpdateKey, now); 1616 return mSharedPrefs.edit().putLong(lastUpdateKey, now); 1617 } 1618 1619 @NonNull getClassName(MediaController controller)1620 private static String getClassName(MediaController controller) { 1621 Bundle sessionExtras = controller.getExtras(); 1622 String value = 1623 sessionExtras == null ? "" : sessionExtras.getString( 1624 Car.CAR_EXTRA_BROWSE_SERVICE_FOR_SESSION); 1625 return value != null ? value : ""; 1626 } 1627 mediaModeToString(@arMediaManager.MediaSourceMode int mode)1628 private static String mediaModeToString(@CarMediaManager.MediaSourceMode int mode) { 1629 return DebugUtils.constantToString(CarMediaManager.class, "MEDIA_SOURCE_", mode); 1630 } 1631 1632 private final class MediaKeyEventListener implements KeyEventListener { 1633 1634 /** 1635 * Handles a media key event from {@link CarInputService}. 1636 * 1637 * <p>When there are multiple active media sessions, stop after first successful delivery. 1638 */ 1639 @Override onKeyEvent(KeyEvent event, int displayType, int seat)1640 public void onKeyEvent(KeyEvent event, int displayType, int seat) { 1641 if (DEBUG) { 1642 Slogf.d(TAG, "onKeyEvent(%s, %d, %d)", event, displayType, seat); 1643 } 1644 int occupantZoneId = mOccupantZoneService.getOccupantZoneIdForSeat(seat); 1645 if (occupantZoneId == INVALID_ZONE_ID) { 1646 Slogf.w(TAG, "Failed to find a valid occupant zone for seat %d." 1647 + " Ignoring key event %s", seat, event); 1648 return; 1649 } 1650 int userId = mOccupantZoneService.getUserForOccupant(occupantZoneId); 1651 if (userId == INVALID_USER_ID) { 1652 Slogf.w(TAG, "Failed to find a valid user for occupant zone %d." 1653 + " Ignoring key event %s", occupantZoneId, event); 1654 return; 1655 } 1656 List<MediaController> mediaControllers = mMediaSessionManager.getActiveSessionsForUser( 1657 /* notificationListeners= */ null, UserHandle.of(userId)); 1658 // Send the key event until it is successfully sent to any of the active sessions. 1659 boolean sent = false; 1660 for (int i = 0; !sent && i < mediaControllers.size(); i++) { 1661 sent = mediaControllers.get(i).dispatchMediaButtonEvent(event); 1662 } 1663 1664 if (DEBUG) { 1665 if (sent) { 1666 Slogf.d(TAG, "Successfully sent the key event %s to user %d", event, userId); 1667 } else { 1668 Slogf.d(TAG, "No active media session can receive the key event %s for user %d", 1669 event, userId); 1670 } 1671 } 1672 } 1673 } 1674 } 1675