mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-09-18 17:19:42 -04:00
Root-caused via #6682/#6693: Node.hopsAway defaults to the sentinel -1
instead of null, and nothing distinguished a locally-retained node from
one the connected radio just reported this session. Together that made
a legitimately cached row (multi-radio use, or a radio with a smaller
NodeDB than the phone) render as if it were freshly heard with 0 hops,
0 SNR, 0 RSSI.
- Add Node.hopsAwayOrNull, extending the existing snrOrNull/rssiOrNull
pattern, and fix both copies of getRelayNode (the live one in
Packet.kt and the unused one in Node.kt) whose plain
minByOrNull { it.hopsAway } let the unresolved -1 sentinel look
closer than a real hop count.
- Track the current connection session's exact NodeDB membership via
NodeManager.currentSessionNodeNums, published right after each Stage
2 handshake and reconciled against the same generation-race window
already used for connectionIdentity.
- Add a "Saved on phone" badge (node list rows + detail screen) for any
node retained locally but absent from that snapshot, and stop those
rows from claiming "online" from a cached, no-longer-current
lastHeard (list rows, a11y description).
New/updated tests across core:model, core:data, core:ui, core:database
and feature:node; screenshot goldens regenerated for the new "Saved on
phone" previews. Full baseline
(spotlessApply/spotlessCheck/detekt/assembleDebug/test/allTests) and
:screenshot-tests:validateDebugScreenshotTest are green.
Fixes #6263
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
:core:database
This module provides the local Room database persistence layer for the application using Room Kotlin Multiplatform (KMP).
Key Components
MeshtasticDatabase: The main Room database class, defined incommonMain.- DAOs (Data Access Objects):
NodeInfoDao: Manages storage and retrieval of node information (NodeEntity). Contains critical logic for handling Public Key Conflict (PKC) resolution and preventing identity wiping attacks.PacketDao: Handles storage of mesh packets, including text messages, waypoints, and reactions.
- Entities:
NodeEntity: Represents a node on the mesh.Packet: Represents a stored packet.ReactionEntity: Represents emoji reactions to packets.
Security Considerations
Public Key Conflict (PKC) Handling
The NodeInfoDao implements specific logic to protect against impersonation and "wipe" attacks:
- Wipe Protection: Receiving an
is_licensed=truepacket (which normally clears the public key for compliance) will not clear an existing valid public key if one is already known. This prevents attackers from sending fake licensed packets to wipe keys from the DB. - Conflict Detection: If a new key arrives for an existing node ID that conflicts with a known valid key, the key is set to
ERROR_BYTE_STRINGto flag the potential impersonation.
Dependency Graph
graph TB
:core:database[database]:::kmp-library
:core:database --> :core:common
:core:database --> :core:model
:core:database -.-> :core:di
:core:database -.-> :core:resources
:core:database -.-> :core:testing
classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef compose-desktop-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef android-library fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-library-compose fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-test fill:#A0C4FF,stroke:#000,stroke-width:2px,color:#000;
classDef jvm-library fill:#BDB2FF,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-library-compose fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-library fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;