From f966ab13075733e866bbd7fa63c37b91a0c2b07f Mon Sep 17 00:00:00 2001 From: Mac DeCourcy <49794076+mdecourcy@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:20:23 -0800 Subject: [PATCH] fix: skip default nodeinfo upsert in handleReceivedUser for live packets (#3819) --- .../geeksville/mesh/service/MeshService.kt | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 25fc91a82..395b420d7 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -992,6 +992,17 @@ class MeshService : Service() { sessionPasskey = a.sessionPasskey } + /** + * Check if a User is a default/placeholder from firmware (node was evicted and re-created) and whether we should + * preserve existing user data instead of overwriting it. + */ + private fun shouldPreserveExistingUser(existing: MeshProtos.User, incoming: MeshProtos.User): Boolean { + val isDefaultName = incoming.longName.matches(Regex("^Meshtastic [0-9a-fA-F]{4}$")) + val isDefaultHwModel = incoming.hwModel == MeshProtos.HardwareModel.UNSET + val hasExistingUser = existing.id.isNotEmpty() && existing.hwModel != MeshProtos.HardwareModel.UNSET + return hasExistingUser && isDefaultName && isDefaultHwModel + } + private fun handleSharedContactImport(contact: AdminProtos.SharedContact) { handleReceivedUser(contact.nodeNum, contact.user, manuallyVerified = true) } @@ -1006,22 +1017,37 @@ class MeshService : Service() { updateNodeInfo(fromNum) { val newNode = (it.isUnknownUser && p.hwModel != MeshProtos.HardwareModel.UNSET) - val keyMatch = !it.hasPKC || it.user.publicKey == p.publicKey - it.user = - if (keyMatch) { - p - } else { - p.copy { - Timber.w("Public key mismatch from $longName ($shortName)") - publicKey = NodeEntity.ERROR_BYTE_STRING + // Check if this is a default/unknown user from firmware (node was evicted and re-created) + val shouldPreserve = shouldPreserveExistingUser(it.user, p) + + if (shouldPreserve) { + // Firmware sent us a placeholder - keep all our existing user data + Timber.d( + "Preserving existing user data for node $fromNum: " + + "kept='${it.user.longName}' (hwModel=${it.user.hwModel}), " + + "skipped default='${p.longName}' (hwModel=UNSET)", + ) + // Still update channel and verification status + it.channel = channel + it.manuallyVerified = manuallyVerified + } else { + val keyMatch = !it.hasPKC || it.user.publicKey == p.publicKey + it.user = + if (keyMatch) { + p + } else { + p.copy { + Timber.w("Public key mismatch from $longName ($shortName)") + publicKey = NodeEntity.ERROR_BYTE_STRING + } } + it.longName = p.longName + it.shortName = p.shortName + it.channel = channel + it.manuallyVerified = manuallyVerified + if (newNode) { + serviceNotifications.showNewNodeSeenNotification(it) } - it.longName = p.longName - it.shortName = p.shortName - it.channel = channel - it.manuallyVerified = manuallyVerified - if (newNode) { - serviceNotifications.showNewNodeSeenNotification(it) } } } @@ -1723,14 +1749,9 @@ class MeshService : Service() { updateNodeInfo(info.num) { if (info.hasUser()) { // Check if this is a default/unknown user from firmware (node was evicted and re-created) - val isDefaultName = info.user.longName.matches(Regex("^Meshtastic [0-9a-fA-F]{4}$")) - val isDefaultHwModel = info.user.hwModel == MeshProtos.HardwareModel.UNSET - val hasExistingUser = it.user.id.isNotEmpty() && it.user.hwModel != MeshProtos.HardwareModel.UNSET + val shouldPreserve = shouldPreserveExistingUser(it.user, info.user) - // If firmware sends a default user (evicted node), preserve our existing user data - val shouldPreserveExisting = hasExistingUser && isDefaultName && isDefaultHwModel - - if (shouldPreserveExisting) { + if (shouldPreserve) { // Firmware sent us a placeholder - keep all our existing user data Timber.d( "Preserving existing user data for node ${info.num}: " +