diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt index f572cc4..206d42b 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt @@ -107,6 +107,8 @@ class GearslipRunner( @Volatile private var audioLink: AudioLink? = null @Volatile private var guidanceLink: AudioLink? = null @Volatile private var vendorChannelId = -1 + /** The car's Wi-Fi projection channel, opened in GM cars only (see [startVendorChannel]); -1 when closed. */ + @Volatile private var wifiChannelId = -1 private var audioMessagesSeen = 0 private var touchWidth = 0 private var touchHeight = 0 @@ -260,6 +262,8 @@ class GearslipRunner( CarBluetooth.onMessage(messageId, body) } else if (frame.channel == vendorChannelId) { onVendorMessage(messageId, body) + } else if (frame.channel == wifiChannelId) { + onWifiProjectionMessage(messageId, body) } else if (frame.channel == CarMic.current?.channelId) { CarMic.current?.onMessage(messageId, body) } else if (frame.channel == audioLink?.channelId) { @@ -726,6 +730,23 @@ class GearslipRunner( vendorChannelId = gm.serviceId log.i("-> ChannelOpenRequest on GM's channel ${gm.serviceId}") openChannel(gm.serviceId) + // Opening GM's channel and pinging the car didn't stop the Bolt hanging up, so a GM car + // also gets the two channels Gearslip had never opened: Bluetooth and Wi-Fi projection. + // Only what arrives on them is logged, by message id and size, until one turns out to matter. + CarBluetooth.announce("a GM car, issue #18") + ServiceDiscovery.findWifiProjectionService(serviceDiscoveryResponse)?.let { channel -> + wifiChannelId = channel + log.i("-> ChannelOpenRequest on the Wi-Fi projection channel $channel (a GM car, issue #18)") + openChannel(channel) + } + } + + private fun onWifiProjectionMessage(messageId: Int, body: ByteArray) { + if (messageId == MSG_CHANNEL_OPEN_RESPONSE) { + log.i("<- Wi-Fi projection channel open: status=${Protobuf.readInt32Field(body, 1) ?: 0}") + } else { + log.i("<- Wi-Fi projection message id=$messageId (${body.size} bytes)") + } } private fun onVendorMessage(messageId: Int, body: ByteArray) { diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/ServiceDiscovery.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/ServiceDiscovery.kt index 690db76..fc6892b 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/ServiceDiscovery.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/ServiceDiscovery.kt @@ -200,6 +200,13 @@ object ServiceDiscovery { VendorService(id, name) } + /** The channel of the car's Wi-Fi projection service (Service field 14), or null. */ + fun findWifiProjectionService(response: ByteArray): Int? = + Wire.allBytes(Wire.fields(response), 1).firstNotNullOfOrNull { channel -> + val service = Wire.fields(channel) + if (Wire.bytes(service, 14) == null) null else Wire.varint(service, 1)?.toInt() + } + fun findBluetoothService(response: ByteArray): BluetoothService? { for (channel in Wire.allBytes(Wire.fields(response), 1)) { val service = Wire.fields(channel) diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarBluetooth.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarBluetooth.kt index 5d78d5a..5748435 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarBluetooth.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarBluetooth.kt @@ -105,6 +105,20 @@ object CarBluetooth { } } + /** + * Opens the car's Bluetooth channel though there's no pairing to do, as a real phone does on + * every connection. Issue #18: a 2021 Bolt hangs up about 20 seconds in, and this is one of + * the two channels it offers that Gearslip never opened. Without an address the driver typed, + * it only opens the channel: a made-up address could set the car pairing with the wrong phone. + */ + fun announce(why: String) { + val c = car ?: return + if (pairing || announcing) return + announcing = true + log.i("-> ChannelOpenRequest on channel ${c.service.serviceId} ($why)") + c.open() + } + fun onCarDisconnected() { car = null pairing = false @@ -157,6 +171,10 @@ object CarBluetooth { else main.post { startBond("the car wouldn't open its Bluetooth channel") } return } + if (announcing && typedAddress() == null) { + log.i("Bluetooth channel open; no address typed in Settings, so no PairingRequest") + return + } val method = c.service.pairingMethods.firstOrNull { it == METHOD_NUMERIC_COMPARISON } ?: c.service.pairingMethods.firstOrNull() ?: METHOD_NUMERIC_COMPARISON c.send(