diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt index dbca04d..d506129 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt @@ -226,6 +226,7 @@ class CarAppConnection(private val context: Context) { fun disconnect() = disconnect(keepLocation = false) private fun disconnect(keepLocation: Boolean) { + main.removeCallbacks(handshakeTimeout) clearToasts() lent?.let { sendSurfaceDestroyed(it) } lent = null @@ -257,11 +258,24 @@ class CarAppConnection(private val context: Context) { // --- handshake --------------------------------------------------------------------- + /** + * An app that won't have Gearslip as its host never answers the first question. Car App + * Library 1.9 and later throw inside the app when a host that isn't Google's asks, and since + * the call is one-way the error never comes back, so without this the car would show + * "Starting..." for ever. + */ + private val handshakeTimeout = Runnable { + if (_status.value.phase == Phase.HANDSHAKE) { + fail(Phase.REJECTED, "It didn't answer Gearslip. Apps built for newer versions of Android Auto only run on Google's.") + } + } + private fun onBound(service: IBinder) { val app = ICarApp.Stub.asInterface(service) carApp = app update { it.copy(phase = Phase.HANDSHAKE, detail = "bound, asking for app info") } GearslipLog.i("host: bound; requesting app info") + main.postDelayed(handshakeTimeout, HANDSHAKE_TIMEOUT_MS) call("getAppInfo", onValue = { value -> val info = value as? AppInfo ?: return@call fail(Phase.FAILED, "app info was ${value?.javaClass?.simpleName}") @@ -274,6 +288,7 @@ class CarAppConnection(private val context: Context) { * offers cannot be rendered correctly, so it is refused here rather than half-drawn. */ private fun onAppInfo(info: AppInfo) { + main.removeCallbacks(handshakeTimeout) val negotiated = minOf(HOST_API_LEVEL, info.latestCarAppApiLevel) GearslipLog.i( "host: app api ${info.minCarAppApiLevel}..${info.latestCarAppApiLevel} " + @@ -839,6 +854,9 @@ class CarAppConnection(private val context: Context) { /** How long a new destination waits for the old route to end; see [navigateTo]. */ const val REROUTE_WAIT_MS = 1_500L + /** How long an app gets to answer the first question before it counts as refusing. */ + const val HANDSHAKE_TIMEOUT_MS = 10_000L + /** Context.BIND_FOREGROUND_SERVICE; the constant itself is hidden from the SDK. */ const val BIND_FOREGROUND_SERVICE = 0x04000000 }