fix: skip default nodeinfo upsert in handleReceivedUser for live packets (#3819)

This commit is contained in:
Mac DeCourcy 2025-11-25 23:20:23 -08:00 committed by GitHub
parent 07ade75f72
commit f966ab1307
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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}: " +