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