Android 12 CarMediaService源码解读

打印 上一主题 下一主题

主题 1033|帖子 1033|积分 3099

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
Android 12 CarMediaService源码解读

路径为
android12/packages/services/Car/service/src/com/android/car/CarMediaService
媒介

CarMediaService是管理汽车应用步伐的当前活动媒体源的服务,继承于☞CarServiceBase;在车里同时有且只有一个活跃源允许欣赏和播放,值得注意的是,正在播放或欣赏的媒体源可能不遵循MediaSession框架,但它依然被视为拥有活跃源。
初始化initUser()

起首一进来先看init()方法,注释写的很清晰了创建CarMediaService后,从ICarImpl调用了此方法。然后判定要不要initUser(int userId)我们来看下这个方法
  1.     private void initUser(int userId) {
  2.         // SharedPreferences are shared among different users thus only need initialized once. And
  3.         // they should be initialized after user 0 is unlocked because SharedPreferences in
  4.         // credential encrypted storage are not available until after user 0 is unlocked.
  5.         // initUser() is called when the current foreground user is unlocked, and by that time user
  6.         // 0 has been unlocked already, so initializing SharedPreferences in initUser() is fine.
  7.         synchronized (mLock) {
  8.             //sp为null初始化一下
  9.             if (mSharedPrefs == null) {
  10.                 mSharedPrefs = mContext.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
  11.             }
  12.             //之前已经注册广播接收器过的话先解绑再重新注册
  13.             if (mIsPackageUpdateReceiverRegistered) {
  14.                 mContext.unregisterReceiver(mPackageUpdateReceiver);
  15.             }
  16.             UserHandle currentUser = new UserHandle(userId);
  17.             mContext.registerReceiverAsUser(mPackageUpdateReceiver, currentUser,
  18.                     mPackageUpdateFilter, null, null);
  19.             //字面意思的标志位
  20.             mIsPackageUpdateReceiverRegistered = true;
  21.             //关键的媒体组件MEDIA_SOURCE_MODE_PLAYBACK表示上一次播放的;MEDIA_SOURCE_MODE_BROWSE表示上一次浏览的
  22.             mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = isCurrentUserEphemeral()
  23.                     ? getDefaultMediaSource() : getLastMediaSource(MEDIA_SOURCE_MODE_PLAYBACK);
  24.             mPrimaryMediaComponents[MEDIA_SOURCE_MODE_BROWSE] = isCurrentUserEphemeral()
  25.                     ? getDefaultMediaSource() : getLastMediaSource(MEDIA_SOURCE_MODE_BROWSE);
  26.             mActiveUserMediaController = null;
  27.             updateMediaSessionCallbackForCurrentUser();
  28.                         //通过CarMediaManager的回调通知播放源的变化
  29.             notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK);
  30.             notifyListeners(MEDIA_SOURCE_MODE_BROWSE);
  31.             //启动服务
  32.             startMediaConnectorService(shouldStartPlayback(mPlayOnBootConfig), currentUser);
  33.         }
  34.     }
复制代码
顺着来看下getDefaultMediaSource()和getLastMediaSource()
getDefaultMediaSource()和getLastMediaSource()

getDefaultMediaSource
  1.     private ComponentName getDefaultMediaSource() {
  2.         //默认的活动源,一般来说是蓝牙
  3.         String defaultMediaSource = mContext.getString(R.string.config_defaultMediaSource);
  4.         ComponentName defaultComponent = ComponentName.unflattenFromString(defaultMediaSource);
  5.         //如果包名没有一个MediaBrowseService服务,则返回null
  6.         if (isMediaService(defaultComponent)) {
  7.             return defaultComponent;
  8.         }
  9.         return null;
  10.     }
复制代码
getLastMediaSource
  1.     private @NonNull ComponentName getLastMediaSource(int mode) {
  2.         //判断sp初始化没有,如果初始化了去sp中取当前user的上一次的活跃源
  3.         if (sharedPrefsInitialized()) {
  4.             String key = getMediaSourceKey(mode);
  5.             String serialized = mSharedPrefs.getString(key, "");
  6.             //取出的值是一个string数组,如果是null则走getDefaultMediaSource()
  7.             if (!TextUtils.isEmpty(serialized)) {
  8.                 for (String name : getComponentNameList(serialized)) {
  9.                     //不为空,取包名返回。也必须带有一个MediaBrowseService服务
  10.                     ComponentName componentName = ComponentName.unflattenFromString(name);
  11.                     if (isMediaService(componentName)) {
  12.                         return componentName;
  13.                     }
  14.                 }
  15.             }
  16.         }
  17.         return getDefaultMediaSource();
  18.     }
复制代码
顺着再看一下updateMediaSessionCallbackForCurrentUser()
updateMediaSessionCallbackForCurrentUser()

  1.     private void updateMediaSessionCallbackForCurrentUser() {
  2.         if (mSessionsListener != null) {
  3.             mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsListener);
  4.         }
  5.         mSessionsListener = new SessionChangedListener(ActivityManager.getCurrentUser());
  6.         UserHandle currentUserHandle = new UserHandle(ActivityManager.getCurrentUser());
  7.         mMediaSessionManager.addOnActiveSessionsChangedListener(null, currentUserHandle,
  8.                 new HandlerExecutor(mHandler), mSessionsListener);
  9.         mMediaSessionUpdater.registerCallbacks(mMediaSessionManager.getActiveSessionsForUser(null,
  10.                 currentUserHandle));
  11.     }
复制代码
主要看addOnActiveSessionsChangedListener方法,更新了一下我们的mSessionsListener,关照我们活跃的MediaSession现在是哪个,然后就是关照回调更新,启动服务,至此CarMediaService的初始化就完成了。
设置媒体源 setMediaSource

  1.             @Override
  2.     public void setMediaSource(@NonNull ComponentName componentName,
  3.             @MediaSourceMode int mode) {
  4.                 //检查媒体源控制权限
  5.         ICarImpl.assertPermission(mContext, android.Manifest.permission.MEDIA_CONTENT_CONTROL);
  6.         if (Log.isLoggable(CarLog.TAG_MEDIA, Log.DEBUG)) {
  7.             Slog.d(CarLog.TAG_MEDIA, "Changing media source to: " + componentName.getPackageName());
  8.         }
  9.         setPrimaryMediaSource(componentName, mode);
  10.     }
  11.             }
复制代码
setPrimaryMediaSource

  1.             /**
  2.      * Updates the primary media source, then notifies content observers of the change
  3.      * Will update both the playback and browse sources if independent playback is not supported
  4.      */
  5.     private void setPrimaryMediaSource(@NonNull ComponentName componentName,
  6.             @CarMediaManager.MediaSourceMode int mode) {
  7.         synchronized (mLock) {
  8.                 //如果已经是此包名的mode则直接返回
  9.             if (mPrimaryMediaComponents[mode] != null
  10.                     && mPrimaryMediaComponents[mode].equals((componentName))) {
  11.                 return;
  12.             }
  13.         }
  14.                 如果不是独立播放的的配置,则将媒体播放源与媒体浏览源都更新,否则根据mode更新独立播放的的配置
  15.         if (!mIndependentPlaybackConfig) {
  16.             setPlaybackMediaSource(componentName);
  17.             setBrowseMediaSource(componentName);
  18.         } else if (mode == MEDIA_SOURCE_MODE_PLAYBACK) {
  19.             setPlaybackMediaSource(componentName);
  20.         } else if (mode == MEDIA_SOURCE_MODE_BROWSE) {
  21.             setBrowseMediaSource(componentName);
  22.         }
  23.     }
  24.                     }
复制代码
接着看下setPlaybackMediaSource,setBrowseMediaSource也是一样的逻辑,就不赘述了
setPlaybackMediaSource

  1.             private void setPlaybackMediaSource(ComponentName playbackMediaSource) {
  2.         //尝试注销mMediaControllerCallback回调,并通过TransportControls停止
  3.         stopAndUnregisterCallback();
  4.         //将此包名保存为上一个播放的音源包名
  5.         synchronized (mLock) {
  6.             mActiveUserMediaController = null;
  7.             mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = playbackMediaSource;
  8.         }
  9.         if (playbackMediaSource != null
  10.                 && !TextUtils.isEmpty(playbackMediaSource.flattenToString())) {
  11.             if (!isCurrentUserEphemeral()) {
  12.                 //保存上一个播放的音源
  13.                 saveLastMediaSource(playbackMediaSource, MEDIA_SOURCE_MODE_PLAYBACK);
  14.             }
  15.             if (playbackMediaSource
  16.                     .equals(mRemovedMediaSourceComponents[MEDIA_SOURCE_MODE_PLAYBACK])) {
  17.                 mRemovedMediaSourceComponents[MEDIA_SOURCE_MODE_PLAYBACK] = null;
  18.             }
  19.         }
  20.         notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK);
  21.         startMediaConnectorService(shouldStartPlayback(mPlayOnMediaSourceChangedConfig),
  22.                 new UserHandle(ActivityManager.getCurrentUser()));
  23.         // 当应用程序出错,重置新源的当前播放状态
  24.         //(例如未登录)。此状态将从注册的应用程序回调中更新
  25.         //以确保mCurrentPlaybackState仅反映当前源。
  26.         synchronized (mLock) {
  27.             mCurrentPlaybackState = PlaybackState.STATE_NONE;
  28.             updateActiveMediaControllerLocked(mMediaSessionManager
  29.                     .getActiveSessionsForUser(null,
  30.                             new UserHandle(ActivityManager.getCurrentUser())));
  31.         }
  32.     }
  33.        
复制代码
接着看下saveLastMediaSource
saveLastMediaSource

  1.             private void saveLastMediaSource(@NonNull ComponentName component, int mode) {
  2.         if (!sharedPrefsInitialized()) {
  3.             return;
  4.         }
  5.                 //获取包名和key
  6.         String componentName = component.flattenToString();
  7.         String key = getMediaSourceKey(mode);
  8.         String serialized = mSharedPrefs.getString(key, null);
  9.                 //如果没有就添加包名,如果有就将包名放到第一个
  10.         if (serialized == null) {
  11.             mSharedPrefs.edit().putString(key, componentName).apply();
  12.         } else {
  13.             Deque<String> componentNames = new ArrayDeque<>(getComponentNameList(serialized));
  14.             componentNames.remove(componentName);
  15.             componentNames.addFirst(componentName);
  16.             mSharedPrefs.edit().putString(key, serializeComponentNameList(componentNames)).apply();
  17.         }
  18.     }
复制代码
getMediaService

最后重点说下getMediaService方法
  1.             /*
  2.      * 获取与当前用户的包名匹配的MediaBrowseService
  3.      */
  4.     private ComponentName getMediaService(@NonNull ComponentName componentName) {
  5.         //获取包名和类名
  6.         String packageName = componentName.getPackageName();
  7.         String className = componentName.getClassName();
  8.         PackageManager packageManager = mContext.getPackageManager();
  9.         Intent mediaIntent = new Intent();
  10.         mediaIntent.setPackage(packageName);
  11.         mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
  12.         //通过packageManager查询此包名的所有服务
  13.         List<ResolveInfo> mediaServices = packageManager.queryIntentServicesAsUser(mediaIntent,
  14.                 PackageManager.GET_RESOLVED_FILTER, ActivityManager.getCurrentUser());
  15.         //找到则正常返回,找不到则返回null,不继续执行,输出No MediaBrowseService with ComponentName:日志
  16.         for (ResolveInfo service : mediaServices) {
  17.             String serviceName = service.serviceInfo.name;
  18.             if (!TextUtils.isEmpty(serviceName)
  19.                     // If className is not specified, returns the first service in the package;
  20.                     // otherwise returns the matched service.
  21.                     // TODO(b/136274456): find a proper way to handle the case where there are
  22.                     //  multiple services and the className is not specified.
  23.                     && (TextUtils.isEmpty(className) || serviceName.equals(className))) {
  24.                 return new ComponentName(packageName, serviceName);
  25.             }
  26.         }
  27.         if (Log.isLoggable(CarLog.TAG_MEDIA, Log.DEBUG)) {
  28.             Slog.d(CarLog.TAG_MEDIA, "No MediaBrowseService with ComponentName: "
  29.                     + componentName.flattenToString());
  30.         }
  31.         return null;
  32.     }
复制代码
总结

最后提一嘴CarMediaService是CarMediaManager的实现,我们APP正常通过CarMediaManager调用到CarMediaService的,关于CarMediaService源码解读的内容就那么多。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81429

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表