CarMediaService 是AAOS中统一管理媒体播放控制、信息显示和用户交互等功能的服务。这一服务依赖于android MediaSession框架。 所以首先需要简单的了解一下MediaSession。
MediaSession 框架规范了音视频应用中界面与播放器之间的通信接口,实现界面与播放器之间的完全解耦。MediaSession 框架定义了媒体会话和媒体控制器两个重要的类,它们为构建多媒体播放器应用提供了一个完善的技术架构。
AAOS上面的MediaSession的控制框图如上主要是MediaControl和MediaSession两个类直接的交互。
MediaControl是UI端控制Service端的类,在AAOS中所有的app播放控制客户端的实现都是carMediaApp中MediaControl的实现的(包括蓝牙audio localplayer界面中暂停播放,下一首 上一首等等)。
MediaSession是服务端, 这个服务端包括(蓝牙的src\com\android\bluetooth,和/apps/Car/LocalMediaPlayer)。这这里面实现了Mediassion 的callback 用来响应client 端UI的控制。 而响应之后的状态改变可以通过继承MediaControl的callback 在客户端实现。
用途
CarMediaService通过CarMediaManager给外部应用提供使用的API。
这些api允许开发者控制车辆中的主要媒体源,以及获取与这一媒体源相关的更新信息。通过这个 API 来实现媒体播放控制、信息显示和用户交互等功能。
接口
CarMediaManger是客户端 通过AIDL的接口 调用到CarMediaService中。 提供的AIDIL的接口如下:
综上可以看到carMediaService 是实现所有媒体相关的ui 浏览和控制的统一管理。 监听媒体源的变化,控制活动和非活动媒体源的播放、退出、暂停等等。
interface ICarMedia {
/** Gets the currently active media source for the provided mode */
ComponentName getMediaSource(int mode);
/** Sets the currently active media source for the provided mode */
void setMediaSource(in ComponentName mediaSource, int mode);
/** Register a callback that receives updates to the active media source */
void registerMediaSourceListener(in ICarMediaSourceListener callback, int mode);
/** Unregister a callback that receives updates to the active media source */
void unregisterMediaSourceListener(in ICarMediaSourceListener callback, int mode);
/** Retrieve a list of media sources, ordered by most recently used */
List getLastMediaSources(int mode);
/** Returns whether the browse and playback sources can be changed independently. */
boolean isIndependentPlaybackConfig();
/** Sets whether the browse and playback sources can be changed independently. */
void setIndependentPlaybackConfig(boolean independent);
}
首先获取CarMediaManager,然后注册MediaSource变化的监听,在mediasource有变化的时候更新ui。
mHandler = new Handler(application.getMainLooper());
mMediaSourceListener = componentName -> mHandler.post(
() -> updateModelState(mInputFactory.getMediaSource(componentName)));
try {
mCarMediaManager = mInputFactory.getCarMediaManager(mCar);
mCarMediaManager.addMediaSourceListener(mMediaSourceListener, mode);
MediaSource src = mInputFactory.getMediaSource(mCarMediaManager.getMediaSource(mode));
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "Initializing with " + src);
}
updateModelState(src);
} catch (CarNotConnectedException e) {
Log.e(TAG, "Car not connected", e);
}
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);
List mediaServices = packageManager.queryIntentServicesAsUser(mediaIntent,
PackageManager.GET_RESOLVED_FILTER, ActivityManager.getCurrentUser());
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);
}
}
}
注册MediaSessionActive的回调
当服务端的MediaSession 设置为active的时候,回调到这个SessionChangedListener中
启动MediaConnectService
MediaConnectorService会调用MediaConnectorService的onStartCommand。
onStartCommand中会获取当前应用的MediaControl设置到PlaybackViewModel。
PlaybackViewModel利用这个control对MediaSession的service端进行控制。
控制是在service端实现mediaControl的onPrepare、onPlay等实现的。
如果MediaSession当前是播放的状态那么会stop掉。如果不是的话 会先进行prepare操作。
private void initUser(@UserIdInt int userId) {
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();
notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK);
notifyListeners(MEDIA_SOURCE_MODE_BROWSE);
startMediaConnectorService(shouldStartPlayback(mPlayOnBootConfig), currentUser);
}
}
private class SessionChangedListener implements OnActiveSessionsChangedListener {
@Override
public void onActiveSessionsChanged(List<MediaController> controllers) {
if (ActivityManager.getCurrentUser() != mCurrentUser) {
Slog.e(CarLog.TAG_MEDIA, "Active session callback for old user: " + mCurrentUser);
return;
}
mMediaSessionUpdater.registerCallbacks(controllers);
}
}
这个会回调到onPlaybackStateChanged,回调的状态是playing而且跟回调之前的状态不一样的时候 也会调用setPrimaryMediaSource进行primary mediasource的更新。
public void onPlaybackStateChanged(@Nullable PlaybackState state) {
if (state.getState() == PlaybackState.STATE_PLAYING
&& state.getState() != mPreviousPlaybackState) {
ComponentName mediaSource = getMediaSource(mMediaController.getPackageName(),
getClassName(mMediaController));
if (mediaSource != null) {
setPrimaryMediaSource(mediaSource, MEDIA_SOURCE_MODE_PLAYBACK);
}
}
mPreviousPlaybackState = state.getState();
}
}
CarMediaService 的实现是通过注册MediaSessionActive 和 MediaController的callback 来监听当前用户所有应用 有关媒体播放浏览等事件。当事件发生时
更新AAOS界面的信息和控制的控件。