本部分提供了特定于 Control Center 参考应用的技术细节。
控制中心是一款非捆绑式、具有特权且经过系统签名的应用,最低 SDK 版本为 35 (Android V [API 级别 35])。该应用安装在 system/priv-app 中,以使用 System APIs。如需读取媒体信息,应用必须经过平台签名。您可以通过无线下载 (OTA) 方式更新应用。
后台服务
“控制中心”应用依赖于后台服务来实现其功能。
Control Center Service 由 Vendor ServiceController 在用户生命周期的 user-post-unlocked 状态下启动。控制中心必须始终处于活动状态并在后台通信,应用不能依赖于用户打开应用。
Control Center Service 通过使用通信 API 连接到其他居住区域中的其他自身实例并与之通信。请务必阅读集成指南,了解每个用户的控制中心实例如何建立连接以及发送和接收数据。
通信
连接后,Control Center Service 会与传达信息的 protobuf 对象进行通信。如需使用 Communication APIs 将 protobuf 传递到另一个乘员区域,系统会将 protobuf 转换为 byte array,创建 payload object,并通过 CarOccupantConnectionManager#sendPayload 发送 Payload。
message ControlCenterPayload {
required Type messageType = 1;
// ...
enum Type {
MEDIA_STATUS = 0;
MEDIA_COMMAND = 1;
INPUT_LOCK_COMMAND = 2;
INPUT_LOCK_SUCCESSFUL = 3;
CANCEL_AUDIO_REQUEST = 4;
MIRRORING_REQUEST_RECEIVER_DECISION = 5;
MIRRORING_SESSION_ENDED = 6;
}
}
private fun parsePayload(
senderZone: OccupantZoneInfo,
payload: Payload
) {
val parsedPayload =
ControlCenterPayload.parseFrom(payload.bytes)
when (parsedPayload.messageType) {
ControlCenterPayload.Type.MEDIA_STATUS -> {
// logic here
}
}
//…
}
数据
有关乘员区域的信息以 OccupantZoneData 对象的形式存储在控制中心内。对本地 OccupantZoneData 的更改通过 Comms API 发送到其他控制中心实例。
当收到的 Payload 被解析时,解析后的数据会传递给本地 OccupantZoneStateRepository,后者会通知视图发生更改。大多数数据都通过 Kotlin flows on Android 在类之间传递。
处理座舱扬声器音频请求
为了让驾驶员始终能够接收乘客通过车厢扬声器播放音频的请求,驾驶员的 Control Center Service 在创建时会注册 Primary ZoneMedia Audio RequestCallback。
回调会收到对 CarAudioManager#requestMediaAudioOnPrimaryZone 的调用通知。驱动程序的 Control Center Service 通过创建浮动通知 (HUN) 来处理请求,该通知可通过 CarAudioManager#allowMediaAudioOnPrimaryZone(boolean) 接受或拒绝。
在其他屏幕上共同观看视频
共同观看功能之所以有效,是因为 CarActivityManager 中存在 Task Mirroring APIs。TaskMirroringManager 首先在 CarActivityManager#getVisibleTasks 中搜索正在播放的 MediaSession 应用的软件包,然后创建 VirtualDisplay 并通过 CarActivityManager#moveRootTaskToDisplay 将可见任务移至此显示屏。
这会返回一个 IBinder 令牌,MirroredSurfaceView 可在布局中使用该令牌通过 MirroredSurfaceView#mirrorSurface 显示任务。Communication API Payload 对象将令牌传递给其他居住者区域。
这些乘员区中的每个 Control Center 实例都会启动 Mirroring activity 并使用该令牌填充 MirroredSurfaceView。
任务镜像 API
控制中心使用以下任务镜像 API:
CarActivityManager#getVisibleTasks(int displayId)<ActivityManager.RunningTaskInfo> 调用,用于显示发件人。CarActivityManager#moveRootTaskToDisplay(int virtualDisplayId)CarActivityManager#createTaskMirroringToken(int taskId)IBinder 令牌,应在任务移至虚拟显示屏后调用。MirroredSurfaceView#mirrorSurface(IBinder token)控制中心内任务镜像的限制
控制中心仅支持 MediaSession 应用的任务镜像。
不过,该 API 可以镜像任何任务。虚拟显示屏的尺寸与发送方显示屏的尺寸相同。如果接收器的显示屏使用不同的分辨率和尺寸,虚拟显示屏会显示在屏幕中央。
显示可见任务
控制中心将机箱 Theme.CarUi.NoToolbar 扩展为半透明窗口。这意味着,当在任务上打开控制中心时,任务会在 CarActivityManager#getVisibleTasks 中返回,从而允许镜像任务。
接收镜像信息
控制中心会通知其他应用镜像会话。如需接收更新,应用必须绑定到 Control Center Service 并发送 Handler 类作为客户端,该客户端会从 Control
Center Service 接收并处理 Messages。
客户端应用可以接收镜像应用的软件包名称,并使用以下键为控制中心内托管镜像应用的 activity 启动 intent URI:
_config_msg_mirroring_pkg_name_key__config_msg_mirroring_redirect_uri_key_
这些配置必须存在于客户端应用资源和控制中心资源中。
调试控制中心
Logger 类用于处理控制中心日志,可以配置为强制记录日志。
class Logger(cls: Class<*>) {
companion object {
private const val FORCE_LOGS = false
}
private val tag: String
init {
tag = cls.simpleName
}
fun v(message: String) {
if (Log.isLoggable(tag, Log.VERBOSE) || FORCE_LOGS) {
Log.v(tag, message)
}
}
...
系统应用和可更新性
由于控制中心是系统应用,并且因使用仅限签名的权限而经过平台签名,因此控制中心必须预安装在设备上,并且只能通过 OTA 更新,与 Car Media App 类似。
从源代码构建控制中心
如需获取控制中心源代码,请参阅集成未捆绑应用。
多屏幕设备上的用户隐私
借助控制中心,所有乘客都可以在所有显示屏上查看媒体信息。 Google 建议您插入非阻塞式隐私权声明来通知用户。 Google 建议您在登录显示器时在系统级别执行此操作。