关于 android-for-cars-parked-apps
指导 Android 编码 Agent 将现有视频、游戏或浏览器应用适配为 Android Auto 或 Android Automotive OS 的驻车应用。内容涵盖平台清单、车载界面、安全要求与发布检查。
主要功能
- 01识别驻车应用类别与目标平台
- 02配置清单和车载兼容性
- 03处理返回导航与屏幕方向
- 04适配系统栏和不规则显示区域
- 05暂停行驶期间的媒体播放
适用场景
- 01将视频应用适配至车载系统
- 02调整游戏以支持驻车场景
- 03为车载浏览器落实数据保护要求
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ android/ skills --skill android-for-cars-parked-apps安装后在 Claude Code 中输入/android-for-cars-parked-apps确认已加载。
附带文件references/android/docs/quality-guidelines/car-app-quality/index.md
详细介绍
将现有 Android 视频应用、游戏和网页浏览器改造为可在 Android Auto 和 Android Automotive OS 上运行的驻车应用。按照以下步骤操作,以满足 Google Play 分发的汽车应用质量准则。
前提条件
- 从现有的自适应 Android 应用开始,且该应用属于受支持的驻车应用类别之一:视频流应用、游戏或网页浏览器。
术语表
- 驻车应用:驻车应用仅在车辆停放时于 Android Auto 和 Android Automotive OS 上运行。驻车应用分为视频、游戏和浏览器三类;其他类别的应用不能作为驻车应用发布。
- Android Auto:Android Auto 是由手机驱动的投射系统。应用在用户的手机上运行,并通过有线或无线连接投射到车载显示屏上。Android Auto 是与 Android Automotive OS 不同的平台。
- Android Automotive OS:Android Automotive OS(简称 AAOS)是直接运行在车辆上的 Android 变体。Android Automotive OS 是与 Android Auto 不同的平台。
- 视频类别:视频应用可在车辆停放时播放流媒体视频。通常可在 Android Automotive OS 上使用;Android Auto 不支持。
- 游戏类别:游戏应用可在车辆停放时提供游戏体验。通常可在 Android Automotive OS 和 Android Auto 上使用。
- 浏览器类别:网页浏览器可让用户在车辆停放时浏览网页。目前在 Android Automotive OS 上以 Beta 版提供;Android Auto 不支持。
- 媒体类别:非驻车类别。媒体应用可在驾驶或停车时播放音频内容(音乐、广播、有声读物,不包括视频)。
- Android for Cars 平台:Android for Cars 是一个统称,包含 Android Auto 和 Android Automotive OS。
- 通常可用的类别:对于受支持的平台,可在 Google Play 的内部测试、封闭式测试、公开测试和正式版轨道中发布。
- Beta 类别:可在内部测试或封闭式测试轨道中发布;公开测试和正式版轨道则必须获准加入抢先体验计划。
- 驾驶时播放音频:Android Automotive OS 上的一项 Beta 功能,可让视频应用在车辆行驶时播放音频。
- 汽车应用质量准则:根据第 3 级(适用于汽车)、第 2 级(针对汽车优化)或第 1 级(汽车特色应用)标准审核应用时,必须检查汽车应用质量准则。
- Android Automotive OS 兼容模式:兼容模式(
android.software.car.display_compatibility)是部分车型上的一项系统功能,可提供系统返回操作提示、安全区域渲染、密度缩放,以及行驶模式下的遮挡(将活动移至 Stopped 状态)。兼容模式仅适用于支持 Android Automotive OS 的应用;仅支持 Android Auto 的应用无需考虑此模式。
探索代码库
在代码库中搜索 AndroidManifest.xml(Unity 项目中也可创建
Assets/Plugins/Android/AndroidManifest.xml)、res/xml/automotive_app_desc.xml、build.gradle、build.gradle.kts
或 gradle/libs.versions.toml 文件。查找现有的视频播放器、MediaSession 类、requestedOrientation 调用,以及 WindowInsets 处理程序。除非用户另有指示,否则添加依赖项前请先征求许可。
第 1 步:配置应用清单
在 AndroidManifest.xml 中声明应用类别、目标平台、硬件功能要求和 Activity 配置。
1.1:识别应用类别
- 视频应用:视频应用必须在
<application>上设置android:appCategory="video",并且不得包含带有uses name="video"的app/src/main/res/xml/automotive_app_desc.xml。 - 游戏:游戏必须在
<application>上设置android:appCategory="game"。如果应用支持控制器输入,请声明android.hardware.gamepad,并设置android:required="false"。 - 浏览器:浏览器必须在已导出的
<activity>(android:exported="true")的MAIN<intent-filter>中包含<category android:name="android.intent.category.APP_BROWSER"/>。
1.2:识别目标平台和硬件功能
- Android Auto:面向 Android Auto(Android 15+)的应用必须在
AndroidManifest.xml文件的某个<intent-filter>中添加<category android:name="android.intent.category.CAR_LAUNCHER"/>。除非用户指定其他 intent filter,否则将其添加到MAIN<intent-filter>中。如果应用仅面向 Android Automotive OS,绝不要添加此类别。 - Android Automotive OS:面向 Android Automotive OS 的应用必须在
AndroidManifest.xml文件中声明<uses-feature android:name="android.hardware.type.automotive" android:required="false"/>(适用于移动设备或多形态设备渠道时设为"false";仅当应用将在专用 Automotive OS 渠道发布时才设为"true")。如果应用仅面向 Android Auto,绝不要添加此功能标签。 - 非必需硬件功能(
DO-1):为满足 Google Play 功能要求和DO-1,请将不支持的功能(android.hardware.wifi、android.hardware.screen.portrait、android.hardware.screen.landscape、android.hardware.camera)声明为android:required="false",并移除车载设备不具备的功能上的android:required="true"。 - 显示连续性(
LS-C1):在<activity>上为screenSize、smallestScreenSize、orientation、screenLayout和density配置android:configChanges,以避免切换到远端显示屏或从远端显示屏切换回来时重建 activity 或导致播放卡顿。 - 兼容模式和驾驶分心:声明兼容模式是可选的,且仅适用于支持 Android Automotive OS 的应用。对于仅支持 Android Auto 的应用,绝不要添加兼容模式。添加
android.hardware.type.automotive会禁用兼容模式,除非在<application>中添加<meta-data android:name="android.software.car.display_compatibility" android:value="true"/>。当应用在AndroidManifest.xml中声明android.software.car.display_compatibility时,应用必须在运行时检查PackageManager.hasSystemFeature("android.software.car.display_compatibility"),并在结果为true时隐藏应用内重复的返回或关闭控件。驻车应用不得在任何<activity>上包含<meta-data android:name="distractionOptimized" android:value="true"/>(DD-2、DD-3)。
第 2 步:提供返回导航和方向支持
2.1:添加应用内返回和关闭控件
Android Automotive OS 车辆不一定配备硬件或软件返回按钮,也不一定支持手势导航(AN-1)。请在每个非根屏幕(包括详情屏幕和全屏播放器)上提供明确的 UI 返回和关闭控件:
- 如果
AndroidManifest.xml未声明android.software.car.display_compatibility:始终显示明确的应用内返回和关闭控件。 - 如果
AndroidManifest.xml声明了android.software.car.display_compatibility,且其android:value="true":仅当PackageManager.hasSystemFeature("android.software.car.display_compatibility")返回false时,才显示应用内返回和关闭控件。
2.2:防护运行时方向请求
车载显示屏的方向是固定的。在固定为纵向的车载显示屏上请求横向显示,会导致黑边或闪烁循环(DO-1)。删除硬编码的 android:screenOrientation 清单属性,并在设置 requestedOrientation 前检查 PackageManager.FEATURE_SCREEN_LANDSCAPE 和 PackageManager.FEATURE_SCREEN_PORTRAIT。
步骤 3:适配系统栏和不规则显示屏挖孔
3.1:跟踪可控制的系统栏内边距
在 Android Automotive OS 上,OEM 可以锁定系统栏使其保持可见,以便用户随时使用空调和车辆控制功能(AR-1)。使用 OnControllableInsetsChangedListener 跟踪可控制的内边距,仅隐藏可控制的系统栏,并对不可控制的 statusBars、navigationBars 和 displayCutout 内边距应用 windowInsetsPadding。
3.2:安全地在不规则显示屏挖孔区域渲染内容
为满足 Tier 1 标准 AR-2,在曲面或非矩形车载显示屏上,绝不要使用 LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT(0)或 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES(1)。使用 car 资源限定符,在 res/values-car-v30/integers.xml 中配置 LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS(3)或 LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER(2):
<integer name="windowLayoutInDisplayCutoutMode">3</integer>
使用 WindowInsets.safeDrawing 保护交互式 UI、应用栏和可滚动列表。
步骤 4:满足驾驶员分心和媒体播放要求
4.1:在生命周期转换时暂停播放,并禁止车辆行驶时恢复播放
车辆开始移动时,操作系统会使用阻挡层覆盖已停放的 Activity,并触发 onPause(在兼容模式车辆上还会触发 onStop)。为满足 DD-2、DD-3 和 MC-1:
- 使用来自
androidx.lifecycle:lifecycle-runtime-compose的LifecycleEventEffect,在ON_PAUSE或ON_STOP时暂停播放。 - 暴露带有播放、暂停或停止命令及媒体项元数据(
MC-1)的MediaSession(androidx.media3:media3-session),并在onCleared中释放它。 - 在
build.gradle.kts中添加useLibrary("android.car"),将ExoPlayer包装在ForwardingSimpleBasePlayer中,监听CarUxRestrictionsManager,在isRequiresDistractionOptimization变为true时暂停播放,并在getState中移除COMMAND_PLAY_PAUSE,以防止外部MediaSession控件在车辆行驶时恢复播放:
@UnstableApi
class CarAwarePlayer(context: Context) :
ForwardingSimpleBasePlayer(ExoPlayer.Builder(context).build()) {
private var shouldPreventPlay = false
private var pausedByUxRestrictions = false
private lateinit var carUxRestrictionsManager: CarUxRestrictionsManager
init {
if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
val car = Car.createCar(context)
carUxRestrictionsManager =
car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
shouldPreventPlay =
carUxRestrictionsManager.currentCarUxRestrictions.isRequiresDistractionOptimization
invalidateState()
carUxRestrictionsManager.registerListener { restrictions: CarUxRestrictions ->
shouldPreventPlay = restrictions.isRequiresDistractionOptimization
if (!shouldPreventPlay && pausedByUxRestrictions) {
handleSetPlayWhenReady(true)
invalidateState()
} else if (shouldPreventPlay && isPlaying) {
pausedByUxRestrictions = true
handleSetPlayWhenReady(false)
invalidateState()
}
}
}
}
override fun getState(): State {
val state = super.getState()
return state.buildUpon()
.setAvailableCommands(
state.availableCommands.buildUpon()
.removeIf(COMMAND_PLAY_PAUSE, shouldPreventPlay)
.build()
)
.build()
}
override fun handleRelease(): ListenableFuture<*> {
if (::carUxRestrictionsManager.isInitialized) {
carUxRestrictionsManager.unregisterListener()
}
return super.handleRelease()
}
}
4.2:支持车辆行驶时的后台音频(可选 Beta 功能)
由于这是可选的 Beta 功能,除非用户提出请求,否则绝不要实现此功能。要实现此功能:
- 在
AndroidManifest.xml中声明<uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false" />,并同时声明FOREGROUND_SERVICE和FOREGROUND_SERVICE_MEDIA_PLAYBACK权限。 - 使用来自
androidx.car.app:app的CarFeatures.isFeatureEnabled(context, CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING)检查车辆的运行时支持情况。仅在!isBackgroundAudioWhileDrivingSupported && restrictions.isRequiresDistractionOptimization时设置shouldPreventPlay。 - 在导出的
MediaSessionService中托管MediaSession和播放器,并设置android:foregroundServiceType="mediaPlayback";在 UI 中使用MediaController.Builder和buildAsync连接。
第 5 步:保护浏览器应用中的敏感数据
由于车辆属于共享设备,浏览器必须在未通过个人资料锁保护的情况下,禁止访问已保存的密码和支付方式(SD-1)。在将密码或支付数据同步到车辆之前,浏览器必须提示用户进行身份验证,并在车载屏幕上显示通知,说明其数据将同步到车辆(SD-2)。
使用 KeyguardManager 检查是否设置了 PIN、图案或密码;如果未设置,则打开 Settings.ACTION_SECURITY_SETTINGS;并且仅使用 BiometricManager.Authenticators.DEVICE_CREDENTIAL 通过 BiometricPrompt 进行身份验证(车辆没有生物识别硬件):
val keyguardManager = context.getSystemService<KeyguardManager>()
val isDeviceSecure = keyguardManager?.isDeviceSecure == true
lateinit var biometricPrompt: BiometricPrompt
if (!isDeviceSecure) {
context.startActivity(
Intent(Settings.ACTION_SECURITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} else {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(context.getString(R.string.auth_title))
.setSubtitle(context.getString(R.string.sync_data_to_car_notice))
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.build()
biometricPrompt.authenticate(promptInfo)
}
第 6 步:验证实现
使用 ./gradlew assembleDebug 构建应用。查找并修复所有构建错误。
实现后的指导
实现后向用户提供以下指导:
- 对于 Android Auto 应用,请将应用安装到手机上,并使用 Desktop Head Unit(DHU)进行测试。
- 对于 Android Automotive OS,请将应用安装到 Android Studio 中的 Android Automotive OS 模拟器上。
- 如果应用属于目标平台的 Beta 类别,则必须先获准加入早期访问计划,才能将应用提交到开放测试或正式发布轨道。
- 如果应用实现了 Beta 驾驶时音频功能,则必须先获准加入早期访问计划,才能将应用提交到开放测试或正式发布轨道。
- 提交应用前,请根据相关的车载应用质量指南检查应用。
反模式
- 绝不在驻车应用的任何
<activity>中包含<meta-data android:name="distractionOptimized" android:value="true"/>,也绝不在视频应用中包含带有uses name="video"的res/xml/automotive_app_desc.xml。 - 绝不假设 Android Automotive OS 上存在硬件或软件返回按钮。
- 绝不假设系统栏只位于顶部和底部,或始终可以隐藏。
- 绝不在 Android Automotive OS 上使用
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT或LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES。 - 绝不根据车速或挡位状态推断 UX 限制,而不读取
CarUxRestrictionsManager中的isRequiresDistractionOptimization。 - 绝不在 Android Automotive OS 上的浏览器应用中将生物识别器(
BIOMETRIC_STRONG、BIOMETRIC_WEAK)与BiometricPrompt搭配使用;只能使用DEVICE_CREDENTIAL。 - 绝不在用户只要求支持 Android Automotive OS 时添加
CAR_LAUNCHERintent 类别。 - 绝不在用户只要求支持 Android Auto 时添加
android.hardware.type.automotive功能标签。
最佳实践
- 必须对不支持的硬件功能(
wifi、screen.portrait、screen.landscape、camera)声明android:required="false",并在调用requestedOrientation前检查FEATURE_SCREEN_LANDSCAPE和FEATURE_SCREEN_PORTRAIT(DO-1)。 - 必须在 Android Automotive OS 上(兼容模式除外)提供应用内返回和关闭操作入口(
AN-1),并使用WindowInsets.safeDrawing和OnControllableInsetsChangedListener保护所有交互式 UI 和文本,避免被系统栏和显示挖孔遮挡(AR-1、AR-2)。 - 必须在 UX 限制生效时暂停播放;除非支持并启用了驾驶时音频,否则在行驶期间从
MediaSession中移除COMMAND_PLAY_PAUSE(DD-2、DD-4、MC-1)。 - 必须要求通过个人资料锁进行身份验证,并在保存、访问或同步浏览器密码或支付信息前显示屏幕同步披露说明(
SD-1、SD-2)。