马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Android 12 CarMediaService源码解读
路径为
android12/packages/services/Car/service/src/com/android/car/CarMediaService
媒介
CarMediaService是管理汽车应用步伐的当前活动媒体源的服务,继承于☞CarServiceBase;在车里同时有且只有一个活跃源允许欣赏和播放,值得注意的是,正在播放或欣赏的媒体源可能不遵循MediaSession框架,但它依然被视为拥有活跃源。
初始化initUser()
起首一进来先看init()方法,注释写的很清晰了创建CarMediaService后,从ICarImpl调用了此方法。然后判定要不要initUser(int userId)我们来看下这个方法
- private void initUser(int userId) {
- // SharedPreferences are shared among different users thus only need initialized once. And
- // they should be initialized after user 0 is unlocked because SharedPreferences in
- // credential encrypted storage are not available until after user 0 is unlocked.
- // initUser() is called when the current foreground user is unlocked, and by that time user
- // 0 has been unlocked already, so initializing SharedPreferences in initUser() is fine.
- synchronized (mLock) {
- //sp为null初始化一下
- if (mSharedPrefs == null) {
- mSharedPrefs = mContext.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
- }
- //之前已经注册广播接收器过的话先解绑再重新注册
- if (mIsPackageUpdateReceiverRegistered) {
- mContext.unregisterReceiver(mPackageUpdateReceiver);
- }
- UserHandle currentUser = new UserHandle(userId);
- mContext.registerReceiverAsUser(mPackageUpdateReceiver, currentUser,
- mPackageUpdateFilter, null, null);
- //字面意思的标志位
- mIsPackageUpdateReceiverRegistered = true;
- //关键的媒体组件MEDIA_SOURCE_MODE_PLAYBACK表示上一次播放的;MEDIA_SOURCE_MODE_BROWSE表示上一次浏览的
- mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = isCurrentUserEphemeral()
- ? getDefaultMediaSource() : getLastMediaSource(MEDIA_SOURCE_MODE_PLAYBACK);
- mPrimaryMediaComponents[MEDIA_SOURCE_MODE_BROWSE] = isCurrentUserEphemeral()
- ? getDefaultMediaSource() : getLastMediaSource(MEDIA_SOURCE_MODE_BROWSE);
- mActiveUserMediaController = null;
- updateMediaSessionCallbackForCurrentUser();
- //通过CarMediaManager的回调通知播放源的变化
- notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK);
- notifyListeners(MEDIA_SOURCE_MODE_BROWSE);
- //启动服务
- startMediaConnectorService(shouldStartPlayback(mPlayOnBootConfig), currentUser);
- }
- }
复制代码 顺着来看下getDefaultMediaSource()和getLastMediaSource()
getDefaultMediaSource()和getLastMediaSource()
getDefaultMediaSource
- private ComponentName getDefaultMediaSource() {
- //默认的活动源,一般来说是蓝牙
- String defaultMediaSource = mContext.getString(R.string.config_defaultMediaSource);
- ComponentName defaultComponent = ComponentName.unflattenFromString(defaultMediaSource);
- //如果包名没有一个MediaBrowseService服务,则返回null
- if (isMediaService(defaultComponent)) {
- return defaultComponent;
- }
- return null;
- }
复制代码 getLastMediaSource
- private @NonNull ComponentName getLastMediaSource(int mode) {
- //判断sp初始化没有,如果初始化了去sp中取当前user的上一次的活跃源
- if (sharedPrefsInitialized()) {
- String key = getMediaSourceKey(mode);
- String serialized = mSharedPrefs.getString(key, "");
- //取出的值是一个string数组,如果是null则走getDefaultMediaSource()
- if (!TextUtils.isEmpty(serialized)) {
- for (String name : getComponentNameList(serialized)) {
- //不为空,取包名返回。也必须带有一个MediaBrowseService服务
- ComponentName componentName = ComponentName.unflattenFromString(name);
- if (isMediaService(componentName)) {
- return componentName;
- }
- }
- }
- }
- return getDefaultMediaSource();
- }
复制代码 顺着再看一下updateMediaSessionCallbackForCurrentUser()
updateMediaSessionCallbackForCurrentUser()
- private void updateMediaSessionCallbackForCurrentUser() {
- if (mSessionsListener != null) {
- mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsListener);
- }
- mSessionsListener = new SessionChangedListener(ActivityManager.getCurrentUser());
- UserHandle currentUserHandle = new UserHandle(ActivityManager.getCurrentUser());
- mMediaSessionManager.addOnActiveSessionsChangedListener(null, currentUserHandle,
- new HandlerExecutor(mHandler), mSessionsListener);
- mMediaSessionUpdater.registerCallbacks(mMediaSessionManager.getActiveSessionsForUser(null,
- currentUserHandle));
- }
复制代码 主要看addOnActiveSessionsChangedListener方法,更新了一下我们的mSessionsListener,关照我们活跃的MediaSession现在是哪个,然后就是关照回调更新,启动服务,至此CarMediaService的初始化就完成了。
设置媒体源 setMediaSource
- @Override
- public void setMediaSource(@NonNull ComponentName componentName,
- @MediaSourceMode int mode) {
- //检查媒体源控制权限
- ICarImpl.assertPermission(mContext, android.Manifest.permission.MEDIA_CONTENT_CONTROL);
- if (Log.isLoggable(CarLog.TAG_MEDIA, Log.DEBUG)) {
- Slog.d(CarLog.TAG_MEDIA, "Changing media source to: " + componentName.getPackageName());
- }
- setPrimaryMediaSource(componentName, mode);
- }
- }
复制代码 setPrimaryMediaSource
- /**
- * Updates the primary media source, then notifies content observers of the change
- * Will update both the playback and browse sources if independent playback is not supported
- */
- private void setPrimaryMediaSource(@NonNull ComponentName componentName,
- @CarMediaManager.MediaSourceMode int mode) {
- synchronized (mLock) {
- //如果已经是此包名的mode则直接返回
- if (mPrimaryMediaComponents[mode] != null
- && mPrimaryMediaComponents[mode].equals((componentName))) {
- return;
- }
- }
- 如果不是独立播放的的配置,则将媒体播放源与媒体浏览源都更新,否则根据mode更新独立播放的的配置
- if (!mIndependentPlaybackConfig) {
- setPlaybackMediaSource(componentName);
- setBrowseMediaSource(componentName);
- } else if (mode == MEDIA_SOURCE_MODE_PLAYBACK) {
- setPlaybackMediaSource(componentName);
- } else if (mode == MEDIA_SOURCE_MODE_BROWSE) {
- setBrowseMediaSource(componentName);
- }
- }
- }
复制代码 接着看下setPlaybackMediaSource,setBrowseMediaSource也是一样的逻辑,就不赘述了
setPlaybackMediaSource
- private void setPlaybackMediaSource(ComponentName playbackMediaSource) {
- //尝试注销mMediaControllerCallback回调,并通过TransportControls停止
- stopAndUnregisterCallback();
- //将此包名保存为上一个播放的音源包名
- synchronized (mLock) {
- mActiveUserMediaController = null;
- mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = playbackMediaSource;
- }
- if (playbackMediaSource != null
- && !TextUtils.isEmpty(playbackMediaSource.flattenToString())) {
- if (!isCurrentUserEphemeral()) {
- //保存上一个播放的音源
- saveLastMediaSource(playbackMediaSource, MEDIA_SOURCE_MODE_PLAYBACK);
- }
- if (playbackMediaSource
- .equals(mRemovedMediaSourceComponents[MEDIA_SOURCE_MODE_PLAYBACK])) {
- mRemovedMediaSourceComponents[MEDIA_SOURCE_MODE_PLAYBACK] = null;
- }
- }
- notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK);
- startMediaConnectorService(shouldStartPlayback(mPlayOnMediaSourceChangedConfig),
- new UserHandle(ActivityManager.getCurrentUser()));
- // 当应用程序出错,重置新源的当前播放状态
- //(例如未登录)。此状态将从注册的应用程序回调中更新
- //以确保mCurrentPlaybackState仅反映当前源。
- synchronized (mLock) {
- mCurrentPlaybackState = PlaybackState.STATE_NONE;
- updateActiveMediaControllerLocked(mMediaSessionManager
- .getActiveSessionsForUser(null,
- new UserHandle(ActivityManager.getCurrentUser())));
- }
- }
-
复制代码 接着看下saveLastMediaSource
saveLastMediaSource
- private void saveLastMediaSource(@NonNull ComponentName component, int mode) {
- if (!sharedPrefsInitialized()) {
- return;
- }
- //获取包名和key
- String componentName = component.flattenToString();
- String key = getMediaSourceKey(mode);
- String serialized = mSharedPrefs.getString(key, null);
- //如果没有就添加包名,如果有就将包名放到第一个
- if (serialized == null) {
- mSharedPrefs.edit().putString(key, componentName).apply();
- } else {
- Deque<String> componentNames = new ArrayDeque<>(getComponentNameList(serialized));
- componentNames.remove(componentName);
- componentNames.addFirst(componentName);
- mSharedPrefs.edit().putString(key, serializeComponentNameList(componentNames)).apply();
- }
- }
复制代码 getMediaService
最后重点说下getMediaService方法
- /*
- * 获取与当前用户的包名匹配的MediaBrowseService
- */
- private ComponentName getMediaService(@NonNull ComponentName componentName) {
- //获取包名和类名
- String packageName = componentName.getPackageName();
- String className = componentName.getClassName();
- PackageManager packageManager = mContext.getPackageManager();
- Intent mediaIntent = new Intent();
- mediaIntent.setPackage(packageName);
- mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
- //通过packageManager查询此包名的所有服务
- List<ResolveInfo> mediaServices = packageManager.queryIntentServicesAsUser(mediaIntent,
- PackageManager.GET_RESOLVED_FILTER, ActivityManager.getCurrentUser());
- //找到则正常返回,找不到则返回null,不继续执行,输出No MediaBrowseService with ComponentName:日志
- for (ResolveInfo service : mediaServices) {
- String serviceName = service.serviceInfo.name;
- if (!TextUtils.isEmpty(serviceName)
- // If className is not specified, returns the first service in the package;
- // otherwise returns the matched service.
- // TODO(b/136274456): find a proper way to handle the case where there are
- // multiple services and the className is not specified.
- && (TextUtils.isEmpty(className) || serviceName.equals(className))) {
- return new ComponentName(packageName, serviceName);
- }
- }
- if (Log.isLoggable(CarLog.TAG_MEDIA, Log.DEBUG)) {
- Slog.d(CarLog.TAG_MEDIA, "No MediaBrowseService with ComponentName: "
- + componentName.flattenToString());
- }
- return null;
- }
复制代码 总结
最后提一嘴CarMediaService是CarMediaManager的实现,我们APP正常通过CarMediaManager调用到CarMediaService的,关于CarMediaService源码解读的内容就那么多。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |