Use some bits of the proximity message to recognize the same device again

This commit is contained in:
darken
2022-01-05 00:37:29 +01:00
parent 6e0a000eb3
commit 490aebf5e0
2 changed files with 59 additions and 8 deletions
@@ -27,7 +27,8 @@ class AppleFactory @Inject constructor(
data class KnownDevice(
val identifier: UUID,
val scanResult: ScanResult
val scanResult: ScanResult,
val message: ProximityPairing.Message,
) {
val address: String
get() = scanResult.device.address
@@ -72,25 +73,47 @@ class AppleFactory @Inject constructor(
return proximityMessage
}
private suspend fun recognizeDevice(scanResult: ScanResult): UUID {
private suspend fun recognizeDevice(scanResult: ScanResult, message: ProximityPairing.Message): UUID {
val address = scanResult.device.address
var identifier: UUID? = null
knownDevs.values.firstOrNull { it.address == address }?.let {
log(TAG, VERBOSE) { "recognizeDevice: Recovered previous ID via address: $it" }
knownDevs[it.identifier] = it.copy(scanResult = scanResult)
knownDevs[it.identifier] = it.copy(
scanResult = scanResult,
message = message,
)
identifier = it.identifier
}
if (identifier == null) {
val currentMarkers = message.getRecogMarkers()
knownDevs.values
.firstOrNull { it.message.getRecogMarkers() == currentMarkers }
?.let {
log(TAG, DEBUG) { "recognizeDevice: Close match based similarity markers." }
log(TAG, DEBUG) { "recognizeDevice: Old marker: ${it.message.getRecogMarkers()}" }
log(TAG, DEBUG) { "recognizeDevice: New marker: $currentMarkers" }
knownDevs[it.identifier] = it.copy(
scanResult = scanResult,
message = message,
)
identifier = it.identifier
}
}
if (identifier == null) {
knownDevs.values
.firstOrNull {
it.rssi > -60 && !it.isOlderThan(Duration.ofSeconds(10))
}
?.let {
log(TAG, VERBOSE) { "recognizeDevice: Close match based on RSSI and timestamp." }
knownDevs[it.identifier] = it.copy(scanResult = scanResult)
log(TAG, DEBUG) { "recognizeDevice: Close match based on RSSI and timestamp." }
knownDevs[it.identifier] = it.copy(
scanResult = scanResult,
message = message,
)
identifier = it.identifier
}
}
@@ -103,7 +126,8 @@ class AppleFactory @Inject constructor(
knownDevs[identifier!!] = KnownDevice(
identifier = identifier!!,
scanResult = scanResult
scanResult = scanResult,
message = message
)
knownDevs.values.toList().forEach { knownDevice ->
@@ -119,7 +143,7 @@ class AppleFactory @Inject constructor(
suspend fun create(scanResult: ScanResult): PodDevice? = mutex.withLock {
val pm = getMessage(scanResult) ?: return@withLock null
val identifier = recognizeDevice(scanResult)
val identifier = recognizeDevice(scanResult, pm)
log(TAG, INFO) {
val data = scanResult.scanRecord!!.getManufacturerSpecificData(
@@ -131,6 +155,14 @@ class AppleFactory @Inject constructor(
"Decoding (MAC=${scanResult.device.address}, nanos=${scanResult.timestampNanos}, rssi=${scanResult.rssi}): $dataHex"
}
return createPodDevice(scanResult, pm, identifier)
}
private fun createPodDevice(
scanResult: ScanResult,
pm: ProximityPairing.Message,
identifier: UUID
): ApplePods {
val dm = (
((pm.data[1].toInt() and 255) shl 8) or (pm.data[2].toInt() and 255)
).toUShort()
@@ -3,6 +3,8 @@ package eu.darken.capod.pods.core.apple.protocol
import android.bluetooth.le.ScanFilter
import dagger.Reusable
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.lowerNibble
import eu.darken.capod.common.upperNibble
import eu.darken.capod.pods.core.apple.ApplePods
import javax.inject.Inject
@@ -13,7 +15,24 @@ object ProximityPairing {
val type: UByte,
val length: Int,
val data: UByteArray
)
) {
data class Markers(
val vendor: UByte,
val length: UByte,
val device: UShort,
val batteryData: Set<UByte>,
val deviceColor: UByte,
)
fun getRecogMarkers(): Markers = Markers(
vendor = CONTINUITY_PROTOCOL_MESSAGE_TYPE_PROXIMITY_PAIRING,
length = PROXIMITY_PAIRING_MESSAGE_LENGTH.toUByte(),
device = (((data[1].toInt() and 255) shl 8) or (data[2].toInt() and 255)).toUShort(),
// Make comparison order independent
batteryData = setOf(data[4].upperNibble, data[4].lowerNibble),
deviceColor = data[7]
)
}
@Reusable
class Decoder @Inject constructor() {