Adapta aplicaciones existentes de Android para video, juegos y navegadores web para que se ejecuten como aplicaciones estacionadas en Android Auto y Android Automotive OS. Sigue estos pasos para cumplir con las pautas de calidad para aplicaciones de automóviles para su distribución en Google Play.
Requisitos previos
- Comienza con una aplicación Android adaptativa existente en una de las categorías de aplicaciones estacionadas admitidas: aplicaciones de transmisión de video, juegos o navegadores web.
Glosario
- Aplicaciones estacionadas: Las aplicaciones estacionadas se ejecutan en Android Auto y Android Automotive OS solo mientras el vehículo está estacionado. Las tres categorías estacionadas son video, juegos y navegadores; otras categorías no se pueden publicar como aplicaciones estacionadas.
- Android Auto: Android Auto es un sistema de proyección basado en teléfono. Las aplicaciones se ejecutan en el teléfono del usuario y se proyectan en la pantalla del automóvil a través de una conexión por cable o inalámbrica. Android Auto es una plataforma separada de Android Automotive OS.
- Android Automotive OS: Android Automotive OS, o AAOS, es una variante de Android que se ejecuta directamente en el vehículo. Android Automotive OS es una plataforma separada de Android Auto.
- Categoría de video: Las aplicaciones de video muestran transmisiones de video mientras están estacionadas. Generalmente disponible en Android Automotive OS; no compatible con Android Auto.
- Categoría de juegos: Las aplicaciones de juegos proporcionan juego mientras están estacionadas. Generalmente disponible en Android Automotive OS y Android Auto.
- Categoría de navegadores: Los navegadores web permiten a los usuarios navegar por la web mientras están estacionados. Disponible en beta en Android Automotive OS; no compatible con Android Auto.
- Categoría de medios: No es una categoría estacionada. Las aplicaciones de medios reproducen contenido de audio (música, radio, audiolibros, excluyendo video) mientras conducen o están estacionados.
- Plataforma Android for Cars: Android for Cars es un término general que incluye Android Auto y Android Automotive OS.
- Categorías generalmente disponibles: Permitidas en las pistas de prueba interna, prueba cerrada, prueba abierta y producción de Google Play para una plataforma compatible.
- Categorías beta: Permitidas en las pistas de prueba interna o cerrada; las pistas de prueba abierta y producción requieren aceptación en un programa de acceso anticipado.
- Audio mientras se conduce: Una función beta en Android Automotive OS que permite a las aplicaciones de video reproducir audio mientras el vehículo está en movimiento.
- Pautas de calidad para aplicaciones de automóviles: Al auditar una aplicación según los criterios de Nivel 3 (Car ready), Nivel 2 (Car optimized) o Nivel 1 (Car differentiated), DEBE consultar pautas de calidad para aplicaciones de automóviles.
- Modo de compatibilidad de Android Automotive OS: El modo de compatibilidad (
android.software.car.display_compatibility) es una función del sistema en vehículos seleccionados que proporciona una acción de retroceso del sistema, renderizado de área segura, escalado de densidad y bloqueo opaco del modo de conducción (moviendo actividades al estado Stopped). El modo de compatibilidad solo es relevante para aplicaciones que admiten Android Automotive OS; no es relevante para aplicaciones que solo admiten Android Auto.
Exploración del código base
Busca en el código base los archivos AndroidManifest.xml (o crea
Assets/Plugins/Android/AndroidManifest.xml en proyectos Unity),
res/xml/automotive_app_desc.xml, build.gradle, build.gradle.kts,
o gradle/libs.versions.toml. Localiza reproductores de video existentes,
clases MediaSession, llamadas requestedOrientation y manejadores WindowInsets.
A menos que el usuario indique lo contrario, pide permiso antes de agregar dependencias.
Paso 1: Configurar el manifiesto de la aplicación
Declara la categoría de la aplicación, las plataformas de destino, los requisitos de funciones de hardware y la configuración de la actividad en AndroidManifest.xml.
1.1: Identificar la categoría de la aplicación
- Aplicaciones de video: Las aplicaciones de video DEBEN establecer
android:appCategory="video"en<application>y NO DEBEN incluirapp/src/main/res/xml/automotive_app_desc.xmlconuses name="video". - Juegos: Los juegos DEBEN establecer
android:appCategory="game"en<application>. Declaraandroid.hardware.gamepadconandroid:required="false"si la aplicación admite entrada de controlador. - Navegadores: Los navegadores DEBEN incluir
<category android:name="android.intent.category.APP_BROWSER"/>en el<intent-filter>MAINde un<activity>exportado (android:exported="true").
1.2: Identificar plataformas de destino y funciones de hardware
- Android Auto: Las aplicaciones que apuntan a Android Auto (Android 15+) DEBEN agregar
<category android:name="android.intent.category.CAR_LAUNCHER"/>a un<intent-filter>en el archivoAndroidManifest.xml. Agrégalo al<intent-filter>MAINa menos que el usuario especifique un filtro de intención diferente. NUNCA agregues esta categoría si la aplicación solo apunta a Android Automotive OS. - Android Automotive OS: Las aplicaciones que apuntan a Android Automotive OS DEBEN declarar
<uses-feature android:name="android.hardware.type.automotive" android:required="false"/>("false"para pistas móviles o de múltiples factores de forma;"true"solo si la aplicación se publicará en la pista dedicada de Automotive OS) en el archivoAndroidManifest.xml. NUNCA agregues esta etiqueta de función si la aplicación solo apunta a Android Auto. - Funciones de hardware no requeridas (
DO-1): Para cumplir con los requisitos de funciones de Google Play yDO-1, declara funciones no admitidas (android.hardware.wifi,android.hardware.screen.portrait,android.hardware.screen.landscape,android.hardware.camera) conandroid:required="false", y eliminaandroid:required="true"en funciones ausentes en los automóviles. - Continuidad de visualización (
LS-C1): Configuraandroid:configChangesparascreenSize,smallestScreenSize,orientation,screenLayoutydensityen<activity>para que el movimiento hacia o desde una pantalla distante no recrea la actividad o interrumpe la reproducción. - Modo de compatibilidad y distracción: Declarar el modo de compatibilidad es opcional y solo es relevante para aplicaciones que admiten Android Automotive OS. NUNCA agregues el modo de compatibilidad para aplicaciones que solo admiten Android Auto. Agregar
android.hardware.type.automotivedeshabilita el modo de compatibilidad a menos que se agregue<meta-data android:name="android.software.car.display_compatibility" android:value="true"/>a<application>. Cuando una aplicación declaraandroid.software.car.display_compatibilityenAndroidManifest.xml, la aplicación DEBE verificarPackageManager.hasSystemFeature("android.software.car.display_compatibility")en tiempo de ejecución y ocultar las acciones de retroceso o cierre duplicadas dentro de la aplicación cuando devuelvetrue. Las aplicaciones estacionadas NO DEBEN incluir<meta-data android:name="distractionOptimized" android:value="true"/>en ningún<activity>(DD-2,DD-3).
Paso 2: Proporcionar navegación hacia atrás y soporte de orientación
2.1: Agregar acciones de retroceso y cierre dentro de la aplicación
Los vehículos con Android Automotive OS no están obligados a tener un botón de retroceso de hardware o software ni navegación por gestos (AN-1). Proporciona controles explícitos de retroceso y cierre en la interfaz de usuario en cada pantalla que no sea raíz, incluyendo pantallas de detalle y reproductores de pantalla completa:
- Si
AndroidManifest.xmlno declaraandroid.software.car.display_compatibility: Muestra controles explícitos de retroceso y cierre dentro de la aplicación de forma incondicional. - Si
AndroidManifest.xmldeclaraandroid.software.car.display_compatibilityconandroid:value="true": Muestra controles de retroceso y cierre dentro de la aplicación solo cuandoPackageManager.hasSystemFeature("android.software.car.display_compatibility")devuelvefalse.
2.2: Proteger las solicitudes de orientación en tiempo de ejecución
Las pantallas de los vehículos tienen orientaciones fijas. Solicitar orientación horizontal en una pantalla de automóvil fija vertical causa bandas negras o bucles de parpadeo (DO-1). Elimina los atributos de manifiesto android:screenOrientation codificados y verifica PackageManager.FEATURE_SCREEN_LANDSCAPE y PackageManager.FEATURE_SCREEN_PORTRAIT antes de establecer requestedOrientation.
Paso 3: Adaptarse a las barras del sistema y los recortes de pantalla irregulares
3.1: Rastrear las inserciones de barras del sistema controlables
En Android Automotive OS, los OEM pueden bloquear las barras del sistema visibles para que los controles de clima y vehículo sigan siendo accesibles (AR-1). Rastrea las inserciones controlables con OnControllableInsetsChangedListener, oculta solo las barras controlables y aplica windowInsetsPadding para las inserciones no controlables de statusBars, navigationBars y displayCutout.
3.2: Renderizar de forma segura en recortes de pantalla irregulares
Para cumplir con el criterio de Nivel 1 AR-2 en pantallas de automóvil curvas o no rectangulares, NUNCA uses LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT (0) o LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES (1). Configura LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS (3) o LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER (2) usando el calificador de recurso car en res/values-car-v30/integers.xml:
<integer name="windowLayoutInDisplayCutoutMode">3</integer>
Protege la interfaz de usuario interactiva, las barras de la aplicación y las listas desplazables con WindowInsets.safeDrawing.
Paso 4: Cumplir con los requisitos de distracción del conductor y reproducción de medios
4.1: Pausar la reproducción en transiciones del ciclo de vida y bloquear la reanudación al conducir
Cuando un vehículo comienza a moverse, el sistema operativo cubre las actividades estacionadas con una superposición de bloqueo y activa onPause (y onStop en vehículos con modo de compatibilidad). Para cumplir con DD-2, DD-3 y MC-1:
- Pausa la reproducción en
ON_PAUSEoON_STOPusandoLifecycleEventEffectdeandroidx.lifecycle:lifecycle-runtime-compose. - Expón un
MediaSession(androidx.media3:media3-session) con comandos de reproducción, pausa o detención y metadatos de elementos (MC-1), y libéralo enonCleared. - Agrega
useLibrary("android.car")enbuild.gradle.kts, envuelveExoPlayerenForwardingSimpleBasePlayer, escuchaCarUxRestrictionsManager, pausa la reproducción cuandoisRequiresDistractionOptimizationse vuelvetrue, y eliminaCOMMAND_PLAY_PAUSEengetStatepara que los controles externos deMediaSessionno puedan reanudar la reproducción mientras se conduce:
@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: Compatibilidad con audio en segundo plano mientras se conduce (función beta opcional)
Como esta es una función beta opcional, NUNCA implementes esta función a menos que el usuario lo solicite. Para implementar la función:
- Declara
<uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false" />enAndroidManifest.xml, junto con los permisosFOREGROUND_SERVICEyFOREGROUND_SERVICE_MEDIA_PLAYBACK. - Verifica la compatibilidad del vehículo en tiempo de ejecución con
CarFeatures.isFeatureEnabled(context, CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING)deandroidx.car.app:app. Solo estableceshouldPreventPlaycuando!isBackgroundAudioWhileDrivingSupported && restrictions.isRequiresDistractionOptimization. - Aloja el
MediaSessiony el reproductor dentro de unMediaSessionServiceexportado conandroid:foregroundServiceType="mediaPlayback", y conéctate desde la interfaz de usuario usandoMediaController.BuilderybuildAsync.
Paso 5: Proteger datos confidenciales en aplicaciones de navegador
Debido a que los vehículos son dispositivos compartidos, los navegadores DEBEN bloquear el acceso a contraseñas guardadas y métodos de pago a menos que estén protegidos por un bloqueo de perfil (SD-1). Antes de sincronizar contraseñas o datos de pago con el vehículo, los navegadores DEBEN solicitar al usuario que se autentique y mostrar un aviso en la pantalla del automóvil indicando que sus datos se sincronizarán con el automóvil (SD-2).
Verifica si se ha establecido un PIN, patrón o contraseña usando KeyguardManager, abre Settings.ACTION_SECURITY_SETTINGS si no está configurado, y autentica con BiometricPrompt usando solo BiometricManager.Authenticators.DEVICE_CREDENTIAL (los vehículos no tienen hardware biométrico):
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)
}
Paso 6: Verificar la implementación
Compila la aplicación usando ./gradlew assembleDebug. Encuentra y corrige cualquier error de compilación.