From d00b54e19aae6a80dc0abcedc29068fe40ce502d Mon Sep 17 00:00:00 2001 From: liu weikai Date: Sat, 27 Jun 2026 00:32:27 +0800 Subject: [PATCH] fix(ble): preserve meshtastic android message identity --- .../src/meshtastic/meshtastic_phone_core.cpp | 42 ++++++++++------ .../tests/test_phone_core_smoke.cpp | 49 +++++++++++++++++++ 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/modules/core_phone/src/meshtastic/meshtastic_phone_core.cpp b/modules/core_phone/src/meshtastic/meshtastic_phone_core.cpp index ccf461a1..057cff6d 100644 --- a/modules/core_phone/src/meshtastic/meshtastic_phone_core.cpp +++ b/modules/core_phone/src/meshtastic/meshtastic_phone_core.cpp @@ -55,7 +55,7 @@ void logDual(const char* format, ...) return; } - char buffer[192] = {}; + char buffer[160] = {}; va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); @@ -78,6 +78,17 @@ void copyBounded(char* dst, size_t dst_len, const char* src) dst[dst_len - 1] = '\0'; } +bool hasBoundedText(const char* text, size_t max_len) +{ + return text && max_len > 0 && text[0] != '\0'; +} + +bool hasAndroidVisibleNodeName(const PhoneNodeView& entry) +{ + return hasBoundedText(entry.short_name, sizeof(entry.short_name)) || + hasBoundedText(entry.long_name, sizeof(entry.long_name)); +} + void applyChannelPsk(uint8_t* dst, size_t dst_len, uint8_t* dst_key_len, @@ -1543,6 +1554,10 @@ bool MeshtasticPhoneCore::popConfigSnapshotFrame(MeshtasticBleFrame* out) { continue; } + if (!hasAndroidVisibleNodeName(entry)) + { + continue; + } from.which_payload_variant = meshtastic_FromRadio_node_info_tag; fillNodeInfoFromEntry(entry, &from.node_info); return encodeFromRadio(from, entry.node_id, out); @@ -1776,19 +1791,22 @@ void MeshtasticPhoneCore::fillNodeInfoFromEntry(const PhoneNodeView& entry, mesh meshtastic_NodeInfo& info = *out; std::memset(&info, 0, sizeof(info)); info.num = entry.node_id; - info.has_user = true; + info.has_user = hasAndroidVisibleNodeName(entry); char user_id[16] = {}; - std::snprintf(user_id, sizeof(user_id), "!%08lX", static_cast(entry.node_id)); - copyBounded(info.user.id, sizeof(info.user.id), user_id); - copyBounded(info.user.long_name, sizeof(info.user.long_name), entry.long_name); - copyBounded(info.user.short_name, sizeof(info.user.short_name), entry.short_name); - if (entry.has_macaddr) + if (info.has_user) { - memcpy(info.user.macaddr, entry.macaddr, sizeof(info.user.macaddr)); + std::snprintf(user_id, sizeof(user_id), "!%08lX", static_cast(entry.node_id)); + copyBounded(info.user.id, sizeof(info.user.id), user_id); + copyBounded(info.user.long_name, sizeof(info.user.long_name), entry.long_name); + copyBounded(info.user.short_name, sizeof(info.user.short_name), entry.short_name); + if (entry.has_macaddr) + { + memcpy(info.user.macaddr, entry.macaddr, sizeof(info.user.macaddr)); + } + info.user.hw_model = static_cast(entry.hw_model); + info.user.role = roleFromEntry(entry.role); } - info.user.hw_model = static_cast(entry.hw_model); - info.user.role = roleFromEntry(entry.role); info.channel = entry.channel; info.last_heard = entry.last_seen; info.snr = entry.snr; @@ -2271,11 +2289,7 @@ void MeshtasticPhoneCore::fillPacketFromText(const chat::MeshIncomingText& msg, packet.hop_limit = msg.hop_limit; packet.which_payload_variant = meshtastic_MeshPacket_decoded_tag; packet.decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; - packet.decoded.source = msg.from; - packet.decoded.dest = msg.to; packet.decoded.want_response = false; - packet.decoded.has_bitfield = true; - packet.decoded.bitfield = 0; packet.decoded.payload.size = static_cast( std::min(msg.text.size(), sizeof(packet.decoded.payload.bytes))); if (packet.decoded.payload.size > 0) diff --git a/modules/core_phone/tests/test_phone_core_smoke.cpp b/modules/core_phone/tests/test_phone_core_smoke.cpp index 56ba0aba..0b84c29b 100644 --- a/modules/core_phone/tests/test_phone_core_smoke.cpp +++ b/modules/core_phone/tests/test_phone_core_smoke.cpp @@ -280,6 +280,38 @@ int main() assert(!meshtastic_session.handleToRadio(invalid_meshtastic, sizeof(invalid_meshtastic))); meshtastic_session.close(); + phone::tests::FakePhoneRuntimeContext text_runtime; + FakeMeshtasticTransport text_transport; + phone::meshtastic::MeshtasticPhoneSession text_session( + text_runtime, text_transport, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + chat::MeshIncomingText incoming_text{}; + incoming_text.channel = chat::ChannelId::PRIMARY; + incoming_text.from = 0x4A59CD8C; + incoming_text.to = 0xFFFFFFFF; + incoming_text.msg_id = 0x9DD4E0E7; + incoming_text.timestamp = 123456789; + incoming_text.text = "Hi"; + incoming_text.hop_limit = 7; + text_session.onIncomingText(incoming_text); + + phone::meshtastic::MeshtasticBleFrame text_frame{}; + assert(text_session.popToPhone(&text_frame)); + meshtastic_FromRadio text_from = meshtastic_FromRadio_init_zero; + assert(decodeFromRadio(text_frame, text_from)); + assert(text_frame.from_num == incoming_text.msg_id); + assert(text_from.which_payload_variant == meshtastic_FromRadio_packet_tag); + assert(text_from.packet.from == incoming_text.from); + assert(text_from.packet.to == incoming_text.to); + assert(text_from.packet.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP); + assert(text_from.packet.decoded.source == 0); + assert(text_from.packet.decoded.dest == 0); + assert(!text_from.packet.decoded.has_bitfield); + assert(text_from.packet.decoded.payload.size == incoming_text.text.size()); + assert(std::memcmp(text_from.packet.decoded.payload.bytes, + incoming_text.text.data(), + incoming_text.text.size()) == 0); + assert(!text_session.popToPhone(&text_frame)); + phone::tests::FakePhoneRuntimeContext admin_runtime; FakeMeshtasticTransport admin_transport; phone::meshtastic::MeshtasticPhoneSession admin_session( @@ -626,6 +658,13 @@ int main() assert(custom_runtime.restart_device_count == 1); phone::tests::FakePhoneRuntimeContext config_runtime; + phone::PhoneNodeView observation_only_node{}; + observation_only_node.node_id = 0x4A59CD8C; + observation_only_node.last_seen = 123456789; + observation_only_node.snr = 6.0f; + observation_only_node.rssi = -69.0f; + config_runtime.nodes.push_back(observation_only_node); + phone::PhoneNodeView peer_node{}; peer_node.node_id = 0x12345679; copyBounded(peer_node.long_name, sizeof(peer_node.long_name), "Peer Node"); @@ -697,6 +736,7 @@ int main() assert(config_from.node_info.user.hw_model != meshtastic_HardwareModel_UNSET); bool saw_config_complete = false; + bool saw_observation_only_node = false; bool saw_peer_node = false; uint32_t last_config_from_radio_id = config_from.id; while (config_session.popToPhone(&config_frame)) @@ -711,10 +751,18 @@ int main() assert(config_from.which_payload_variant != meshtastic_FromRadio_moduleConfig_tag); assert(config_from.which_payload_variant != meshtastic_FromRadio_my_info_tag); assert(config_from.which_payload_variant != meshtastic_FromRadio_deviceuiConfig_tag); + if (config_from.which_payload_variant == meshtastic_FromRadio_node_info_tag && + config_from.node_info.num == observation_only_node.node_id) + { + saw_observation_only_node = true; + } if (config_from.which_payload_variant == meshtastic_FromRadio_node_info_tag && config_from.node_info.num == peer_node.node_id) { assert(config_frame.from_num == peer_node.node_id); + assert(config_from.node_info.has_user); + assert(std::strcmp(config_from.node_info.user.short_name, peer_node.short_name) == 0); + assert(std::strcmp(config_from.node_info.user.long_name, peer_node.long_name) == 0); saw_peer_node = true; } if (config_from.which_payload_variant == meshtastic_FromRadio_config_complete_id_tag) @@ -725,6 +773,7 @@ int main() break; } } + assert(!saw_observation_only_node); assert(saw_peer_node); assert(saw_config_complete);