mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
fix(ble): Drop proximity messages with unsupported payload prefix
AirPods Gen4 emit a one-off type 0x07 frame from their identity address on connect whose payload prefix is 0x07 instead of 0x01. Its bytes are not the plaintext status format, but the model bytes happen to match, so it minted a fresh device tracker and showed as a duplicate device card until the stale timeout. Every known plaintext status broadcast (including pairing mode) uses prefix 0x01; pairing state lives in the suffix byte. Reject anything else at the decoder, logging the dropped frame's hex. Fixes #603
This commit is contained in:
@@ -2,7 +2,9 @@ package eu.darken.capod.pods.core.apple.ble.protocol
|
||||
|
||||
import android.bluetooth.le.ScanFilter
|
||||
import dagger.Reusable
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.DEBUG
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@@ -16,6 +18,17 @@ object ProximityPairing {
|
||||
return null
|
||||
}
|
||||
|
||||
// Pods also emit type 0x07 frames whose payload does not use the plaintext status
|
||||
// format, e.g. prefix 0x07 from the identity address on connect (#603). Their bytes
|
||||
// are garbage at the standard offsets and would create a ghost device.
|
||||
if (message.data.firstOrNull() != PAIRING_MESSAGE_PREFIX) {
|
||||
log(TAG, DEBUG) {
|
||||
val hex = message.data.joinToString(" ") { "%02X".format(it.toInt()) }
|
||||
"Dropping proximity pairing message with unsupported prefix: [$hex]"
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return ProximityMessage(
|
||||
type = message.type,
|
||||
length = message.length,
|
||||
@@ -49,4 +62,9 @@ object ProximityPairing {
|
||||
|
||||
// This is the default message length among official Apple devices, clones may have different length
|
||||
internal const val PAIRING_MESSAGE_LENGTH = 25
|
||||
|
||||
// First payload byte of every known plaintext status broadcast, including pairing mode
|
||||
internal val PAIRING_MESSAGE_PREFIX = 0x01.toUByte()
|
||||
|
||||
private val TAG = logTag("Pod", "Apple", "ProximityPairing")
|
||||
}
|
||||
|
||||
+12
-1
@@ -1,6 +1,7 @@
|
||||
package eu.darken.capod.pods.core.apple.ble.devices.airpods
|
||||
|
||||
import eu.darken.capod.pods.core.apple.PodModel
|
||||
import eu.darken.capod.pods.core.apple.ble.BlePodSnapshot
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.BaseBlePodsTest
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.DualApplePods
|
||||
import eu.darken.capod.pods.core.apple.ble.devices.HasAppleColor
|
||||
@@ -43,4 +44,14 @@ class AirPodsGen4Test : BaseBlePodsTest() {
|
||||
model shouldBe PodModel.AIRPODS_GEN4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frame with unsupported payload prefix is rejected - #603`() = runTest {
|
||||
// Captured from AirPods Gen4 on connect: sent from the identity address, payload prefix
|
||||
// 0x07 instead of 0x01, model bytes match but battery/color bytes are not plaintext.
|
||||
// Decoding it as a status frame created a duplicate device card.
|
||||
create<BlePodSnapshot?>("07 19 07 19 20 35 DF DE 46 76 CE 66 5C F4 6E BA B6 AC 1B 69 4C A7 D8 09 BF 9A 04") {
|
||||
this shouldBe null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user