fix(car): notification-only car messaging for production; park templated behind flag (#6015)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
James Rich
2026-06-29 12:30:29 -05:00
committed by GitHub
parent d25444e406
commit f47c31da16
6 changed files with 62 additions and 2 deletions

View File

@@ -43,9 +43,24 @@ if (keystorePropertiesFile.exists()) {
keystorePropertiesFile.inputStream().use { keystoreProperties.load(it) }
}
// The templated Android Auto experience (CarAppService + HomeScreen) is a Google "templated
// messaging" beta feature that is publishable only to Closed/Internal tracks — Open/Production
// submissions are auto-rejected (https://developer.android.com/training/cars/communication/templated-messaging).
// Default builds therefore ship *notification-only* car messaging, which is GA and production-safe.
// Build a Closed-track templated AAB with: -PenableCarTemplates=true
// ponytail: gated by a gradle property + res override, not a full build flavor — templated is
// parked until it leaves Google's beta. Promote to a flavor dimension only if CI must ship both.
val enableCarTemplates = (findProperty("enableCarTemplates") as String?)?.toBoolean() ?: false
configure<ApplicationExtension> {
namespace = "org.meshtastic.app"
// When templates are enabled, this res dir overrides feature:car's notification-only
// automotive_app_desc.xml with one that also declares <uses name="template" />.
if (enableCarTemplates) {
sourceSets.getByName("google").res.srcDir("src/googleCarTemplates/res")
}
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String?
@@ -115,6 +130,10 @@ configure<ApplicationExtension> {
}
ndk { abiFilters += listOf("armeabi-v7a", "arm64-v8a") }
// Activates the (google-only) CarAppService. Off by default so production builds ship
// notification-only car messaging; flipped on by -PenableCarTemplates=true for Closed tracks.
manifestPlaceholders["carTemplatesEnabled"] = enableCarTemplates.toString()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Templated Android Auto declaration, included ONLY when building with -PenableCarTemplates=true
(wired in androidApp/build.gradle.kts). Being an app-module resource, it overrides feature:car's
notification-only automotive_app_desc.xml in the merge, adding <uses name="template" /> so Android
Auto recognizes and launches the CarAppService/HomeScreen. Closed/Internal tracks only — Google
auto-rejects templated-messaging builds submitted to Open/Production.
-->
<automotiveApp>
<uses name="notification" />
<uses name="template" />
</automotiveApp>

View File

@@ -30,6 +30,11 @@ android {
defaultConfig {
minSdk = 23
consumerProguardFiles("proguard-rules.pro")
// The CarAppService is disabled unless -PenableCarTemplates=true. feature:car resolves the
// placeholder in its OWN manifest, so it must read the property directly (the app's value does
// not override a library's own placeholder). Default false → production ships it disabled.
manifestPlaceholders["carTemplatesEnabled"] =
((findProperty("enableCarTemplates") as String?)?.toBoolean() ?: false).toString()
}
// Robolectric provides the Android context that androidx.car.app TestCarContext/ScreenController need.

View File

@@ -2,8 +2,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- enabled is gated by the app's `carTemplatesEnabled` manifest placeholder: off by default
(production ships notification-only car messaging), on for -PenableCarTemplates=true builds.
The decisive gate is automotive_app_desc's <uses name="template" /> (see res/xml); this is
defense-in-depth so the templated service can't be bound in production. -->
<service
android:name="org.meshtastic.feature.car.service.MeshtasticCarAppService"
android:enabled="${carTemplatesEnabled}"
android:exported="true"
android:permission="androidx.car.app.CarAppService">
<intent-filter>

View File

@@ -144,8 +144,22 @@ class HomeScreen(
carContext.getCarService(AppManager::class.java).showAlert(alert)
}
// Any exception thrown out of onGetTemplate() crashes the Android Auto host ("content not able to
// load") and fails Play's Auto review. Degrade to a safe template instead and log for diagnosis.
// NOTE: this guards exceptions in OUR build path; a host-side template-contract rejection (e.g. the
// messaging surface must be a ListTemplate/SectionedItemTemplate of ConversationItems, not this
// TabTemplate dashboard) would not throw here — confirm against the Play pre-launch stack before
// promoting templated builds. See -PenableCarTemplates in androidApp/build.gradle.kts.
@Suppress("TooGenericExceptionCaught")
override fun onGetTemplate(): Template = try {
buildHomeTemplate()
} catch (e: Exception) {
Logger.e(tag = "HomeScreen", throwable = e) { "onGetTemplate failed; showing fallback template" }
buildDisconnectedTemplate()
}
@Suppress("ReturnCount")
override fun onGetTemplate(): Template {
private fun buildHomeTemplate(): Template {
val connectionStatus = stateCoordinator.sessionState.value.connectionStatus
if (connectionStatus == ConnectionState.Disconnected || connectionStatus == ConnectionState.DeviceSleep) {
return buildDisconnectedTemplate()

View File

@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Default (production) declaration: notification-only car messaging. This is GA and ships to any
Play track. The templated experience (<uses name="template" />) is a Google beta restricted to
Closed/Internal tracks, so it is added ONLY by androidApp/src/googleCarTemplates/res when built
with -PenableCarTemplates=true (that override replaces this file in the merge).
-->
<automotiveApp>
<uses name="template" />
<uses name="notification" />
</automotiveApp>