• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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