Commit graph

274 commits

Author SHA1 Message Date
torlando-tech
51a9c0db50 feat(telephony): improve call setup reliability and init speed
- Announce lxst.telephony destination alongside LXMF announces so
  remote peers discover both messaging and call paths together
- Add periodic re-announcing in CallManager (3-hour interval,
  matching reference LXST Telephony implementation)
- Auto-request identity path from network when unknown, with 5s
  retry loop (mirrors existing LXMF pattern in reticulum_wrapper)
- Move setupLxstCallManager() to first position in setupBridges()
  so Telephone is available immediately after Python init (was last,
  causing up to ~47s delay before calls could be placed)
- Remove stale "call.audio" announce handler that could never match
  the actual "lxst.telephony" aspect

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:43:23 -05:00
torlando-tech
13bedfe429 fix(python): guard call() with _busy flag to prevent race with incoming calls
The call() method checked and set self.active_call without holding
_call_handler_lock. During the up-to-70s path discovery window, an
incoming call could set active_call via __caller_identified, which
call() would then overwrite — orphaning the incoming link.

Fix: set _busy=True under lock before path discovery so incoming
calls see the reservation and get rejected. Clear _busy in finally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 22:44:21 -05:00
torlando-tech
9e51d70652 fix(python): reject outgoing call if active_call already set
Prevents orphaned Reticulum links when call() is invoked while an
existing call is active or connecting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 21:10:47 -05:00
torlando-tech
d139938531 fix(python): eliminate TOCTOU race in audio/signal hot path
Use local reference capture instead of locks for receive_audio_packet()
and receive_signal() to prevent race with hangup()/__link_closed()
nulling self.active_call. Lock-free approach avoids audio jitter from
contention on the 25-50Hz packet path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 19:52:48 -05:00
torlando-tech
c9054ece03 fix: address Sentry/Greptile review comments on PR #416
- Add _call_handler_lock to answer(), hangup(), and __link_closed()
  to prevent race conditions on self.active_call (Sentry HIGH)
- Remove invalid `=*` gitignore pattern (Greptile)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 19:18:25 -05:00
torlando-tech
990cddde1d test(python): improve call_manager.py coverage from 48% to 94%
Add 40 new tests across 12 test classes covering previously untested
code paths: incoming/outgoing link handling, packet reception, signal
routing, audio forwarding, and Kotlin callback notification. Add
pragma: no cover to RNS fallback stub (dead code in test context).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 19:05:02 -05:00
torlando-tech
77115a0a07 fix(test): rewrite test_call_manager.py for transport-layer API
call_manager.py was rewritten during LXST-kt extraction as a thin
Reticulum transport layer — profile constants, telephone attribute,
audio bridge, and callback handlers all moved to Kotlin. Rewrite all
50 tests to match the new API surface: active_call (RNS.Link),
packet forwarding, signal routing, and bridge wiring.

Key fix: use RNS.Link.ACTIVE symbolically (not hardcoded int) since
conftest.py globally replaces sys.modules['RNS'] with MagicMock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 17:27:43 -05:00
torlando-tech
1c497946b3 chore: remove dead Python LXST dependencies and audio backend
Remove vestigial Python LXST artifacts that are no longer needed
now that audio, codecs, and filters are handled by Kotlin LXST-kt:

- Remove numpy pip dependency (was for Python audio float32 conversion)
- Remove pycodec2 pip install and pre-built wheels (Kotlin JNI handles codecs)
- Delete chaquopy_audio_backend.py (617 lines, replaced by AudioDevice.kt)
- Delete lxst and lxst-filterlib wheels (never installed)
- Remove dead set_audio_bridge() plumbing from wrapper and manager
- Update call_manager.py to use renamed onInboundPacket/onInboundSignal
- Update LXST-kt submodule with transport-agnostic core/ renames

Remaining Python: call_manager.py (Reticulum bridge), rns, lxmf, u-msgpack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:34 -05:00
torlando-tech
96a93038e2 fix(lxst): fix incoming call answer button not picking up calls
Python's _notify_kotlin callback (via Chaquopy function reference) silently
fails to reach Telephone.onIncomingCall(), leaving isIncomingCall=false so
answer() always rejected. Fix with three changes:

- Add Telephone.prepareForAnswer() for lightweight JIT state setup
- Binder answerCall() falls back to CallBridge identity when answer() fails
- VoiceCallScreen auto-answer no longer gates on callState, preventing
  notification answer button from starting a new outgoing call

Also: answer() now returns Boolean, onIncomingCall() no longer sends
duplicate STATUS_RINGING (Python already sent it to remote).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:33 -05:00
torlando-tech
80cfe18ffa feat(lxst): extract lxst module with audio pipeline, codecs, and telephony
Extract the LXST (voice call) stack into a standalone Gradle module
(lxst/) decoupled from the reticulum module. Includes the full audio
pipeline (LineSink, LineSource, Mixer, Packetizer, Pipeline), codec
wrappers (Opus with 16kHz decode-rate cap for wuqi-opus 1024-sample
limit, Codec2), bridge layer (KotlinAudioBridge with 500ms AudioTrack
buffer floor, NetworkPacketBridge, CallBridge), and telephony state
machine (Telephone, Profile, NetworkTransport interface).

LineSink improvements:
- Monotonic clock pacer eliminates cumulative drift from relative delays
- Post-underrun re-buffering (AUTOSTART_MIN frames) prevents rapid
  micro-underrun cycles that caused audible pops
- Underrun duration threshold: brief glitches (<2 frame periods) resume
  immediately, sustained gaps trigger full re-buffer
- MAX_FRAMES increased to 15 (900ms jitter absorption at 60ms/frame)

Test coverage:
- LineSinkPacingTest: 9 unit tests for monotonic clock, burst pacing,
  drift, underrun recovery, re-buffer behavior
- LineSinkRebufferInstrumentedTest: 5 hardware tests validating
  re-buffer strategy against real AudioTrack underrun counts
- OpusDecodeRateInstrumentedTest: 8 tests documenting wuqi-opus
  1024-sample buffer constraint and 16kHz decode cap
- LineSinkPacingInstrumentedTest, FullDuplexInstrumentedTest,
  LineSourceCaptureInstrumentedTest: hardware validation suite

Known limitations:
- RX delayed ~2-3s (16kHz decode → 60ms frames → large pre-buffer)
- Occasional TX pops (separate from RX re-buffer work)
- Fix requires replacing wuqi-opus with caller-allocated-buffer library

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:33 -05:00
torlando-tech
1d0cf79f0a feat(11.6): enable bidirectional voice calls with crash fixes
Wire Kotlin→Python audio transmit path by registering call_manager as
NetworkPacketBridge's pythonNetworkHandler. Fix wuqi-opus SIGSEGV by
capping decode sample rate to fit 1024-sample buffer. Fix LineSink
sample rate mismatch and dual-sink race condition. Re-enable full
transmit pipeline (Packetizer, Mixer, LineSource). Add jarray→bytes
conversion for Chaquopy type compatibility.

Key changes:
- PythonWrapperManager: register setPythonNetworkHandler after CallManager init
- Opus: compute safe decode sample rate for wuqi-opus buffer limit
- LineSink: add releasedFlag to prevent stale frame auto-restart
- Telephone: re-enable transmit pipeline, fix pipeline lifecycle
- NetworkPacketBridge: add diagnostic logging for consumer coroutine
- call_manager.py: add jarray→bytes conversion, diagnostic logging
- LinkSource: add packet decoding with codec header parsing
- Remove obsolete lxst_debug_instrumentation.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:02:33 -05:00
torlando-tech
44077010d4 feat(11.6-01): add Kotlin audio active check to LineSource instrumentation
- Check is_kotlin_audio_active() in instrumented_ingest_job()
- Skip frame processing when Kotlin audio active
- Log skip every 100 frames for verification
- Maintain timing with sleep(frame_time)
- Provides additional safety layer beyond recorder silence
2026-02-07 16:02:33 -05:00
torlando-tech
b405dfa462 feat(11.6-01): add set_kotlin_audio_active() to CallManager
- Add set_kotlin_audio_active() method to CallManager
- Delegates to chaquopy_audio_backend.set_kotlin_audio_active()
- Provides Kotlin-accessible method to control Python audio disable
- Logs state change for debugging
2026-02-07 16:02:33 -05:00
torlando-tech
a28e7db15f feat(11.6-01): add _kotlin_audio_active flag to disable Python LXST audio
- Add global _kotlin_audio_active flag (default False)
- Add set_kotlin_audio_active() and is_kotlin_audio_active() functions
- ChaquopyRecorder._record_chunk() returns silence when flag True
- ChaquopyPlayer.play() drops frames when flag True
- Prevents dual audio pipeline conflict during Kotlin calls
2026-02-07 16:02:33 -05:00
torlando-tech
f2fb803e5b feat(11.5-01): wire INTEGER signals from Python to Kotlin
- Add STATUS_* signalling constants (0x00-0x06) to CallManager
- Send INTEGER signals via send_signal() in all LXST callbacks:
  - _handle_ringing() -> STATUS_RINGING (0x04)
  - _handle_established() -> STATUS_CONNECTING (0x05) + STATUS_ESTABLISHED (0x06)
  - _handle_ended() -> STATUS_AVAILABLE (0x03)
  - _handle_busy() -> STATUS_BUSY (0x00)
  - _handle_rejected() -> STATUS_REJECTED (0x01)
- Preserve existing _notify_kotlin() string events for UI

This fixes the signal type mismatch (Gap 1 from 11-VERIFICATION.md)
where Kotlin Telephone expected INTEGER signals but Python only sent
STRING events. Kotlin Telephone can now transition to ESTABLISHED state
and open audio pipelines.
2026-02-07 16:02:33 -05:00
torlando-tech
d5615bae86 feat(11-04): add Kotlin Telephone integration to call_manager.py
- Add on_state_changed() for Kotlin state notifications
- Add on_profile_changed() for profile sync
- Add set_kotlin_telephone_callback() for callback registration
- Add _notify_kotlin() for Python->Kotlin event notifications
- Update receive_audio_packet() to forward to LXST Packetizer
- Update receive_signal() to forward to LXST signalling
- Wire existing handlers to notify Kotlin Telephone
2026-02-07 16:02:33 -05:00
torlando-tech
b53df88c45 feat(10-05): initialize NetworkPacketBridge in PythonWrapperManager
- Add NetworkPacketBridge import
- Add networkPacketBridge instance variable
- Initialize NetworkPacketBridge in setupCallManager()
- Pass bridge to Python via set_network_bridge()
- Add shutdown cleanup for networkPacketBridge
- Add set_network_bridge() method to reticulum_wrapper.py
- Pass kotlin_network_bridge to initialize_call_manager()
2026-02-07 16:02:33 -05:00
torlando-tech
cae71db622 feat(10-05): add network bridge methods to call_manager.py
- Add _kotlin_network_bridge instance variable
- Update initialize() to accept kotlin_network_bridge parameter
- Update initialize_call_manager() to pass network bridge
- Add send_audio_packet() for Python->Kotlin packets
- Add send_signal() for Python->Kotlin signalling
- Add receive_audio_packet() stub for Kotlin->Python (Phase 11)
- Add receive_signal() stub for Kotlin->Python (Phase 11)
2026-02-07 16:02:33 -05:00
torlando-tech
a745afd43a chore(07-02): add opus-jni dependency to build.gradle.kts
- Added cn.entertech.android:wuqi-opus:1.0.3 from Maven Central
- Library wraps theeasiestway/android-opus-codec (libopus 1.3.1)
- Provides OpusEncoder/OpusDecoder with JNI bindings
- Native .so libraries for arm64-v8a and x86_64 ABIs
2026-02-07 16:02:33 -05:00
torlando-tech
86252a908e fix(python): make display_name optional in create_identity()
The second create_identity() method at line 7376 shadows the first one at
line 2338. Making display_name optional allows callers that don't pass a
name to work correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 10:31:56 -05:00
torlando-tech
153112dbc1 docs(deps): document RPC exception handling fix in requirements.txt
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:49:23 -05:00
torlando-tech
f4a8fe8658 fix(deps): update RNS to fix socket leak in TCPInterface
Update Reticulum fork from rebase-1.1.3 to fix/socket-leak-1.1.3 branch.

The fix closes sockets on connection failure in TCPInterface.connect() and
BackboneInterface.connect() to prevent resource leak during reconnection
attempts (~780 leaked sockets/hour, ~4.3 MB/hour native memory growth).

Addresses memory growth identified during Phase 9 profiling investigation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:29:03 -05:00
torlando-tech
dd416490a2 docs(05-02): audit PyObject lifecycle for memory leaks
- Comprehensive review of all PyObject usage in Kotlin code
- Confirmed no PyObject storage beyond method scope
- All PyObjects immediately converted to Kotlin types
- Chaquopy automatic reference counting handles cleanup
- Documented findings in memory_profiler.py comments

Files audited:
- PythonWrapperManager.kt ✓
- EventHandler.kt ✓
- IdentityManager.kt ✓
- RoutingManager.kt ✓
- PythonResultConverter.kt ✓

Conclusion: No PyObject reference leaks in Kotlin code.
2026-01-29 18:41:10 -05:00
torlando-tech
42667c8308 test(05-02): add unit tests for memory profiler
Cover start/stop, snapshots, periodic scheduling, and thread safety.
13 test cases for ~90% coverage of memory_profiler.py.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:33:04 -05:00
torlando-tech
9899f10ad0 fix(05-02): prevent race condition in memory profiler timer
Add threading.Lock to synchronize access to _profiling_active and
_snapshot_timer between stop_profiling() and timer callback.

Prevents orphaned timer continuing after stop_profiling() returns.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:29:26 -05:00
torlando-tech
9355889a31 feat(05-01): add Python memory profiler module
- Create memory_profiler.py with tracemalloc snapshot comparison
- Add enable_memory_profiling() method to ReticulumWrapper
- Schedule periodic snapshots using threading.Timer (Chaquopy-compatible)
- Filter frozen importlib and unknown traces to reduce noise
- Log top 10 growing allocations to Android logcat via logging_utils
- Provide get_memory_profile() for current stats query
- Zero overhead when disabled (lazy import pattern)
2026-01-29 17:08:16 -05:00
Torlando
c0529dc307
Merge pull request #284 from torlando-tech/feature/issue-216-rns-discovery
feat: add RNS 1.1.x bootstrap and interface discovery support
2026-01-22 13:48:06 -05:00
torlando-tech
bb601d071b test: add comprehensive tests for get_discovered_interfaces()
Add 27 tests covering:
- get_discovered_interfaces(): TCP interfaces, radio interfaces with LoRa
  params, interfaces with location data, multiple interfaces sorted by
  status and stamp value, bytes/string transport_id handling, None value
  filtering, exception handling
- _get_discovery_status_name(): status code to name conversion
- _create_config_file() discovery options: discover_interfaces,
  autoconnect_discovered_interfaces, interface_discovery_sources,
  required_discovery_value, bootstrap_only for TCP clients

This improves patch coverage from ~56% to target higher coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 23:19:20 -05:00
torlando-tech
02b53b64a8 test: add comprehensive unit tests for RNS discovery feature
Add unit tests for the RNS 1.1.x discovery functionality:

Python tests:
- test_autoconnected_endpoints.py: 14 tests for get_autoconnected_interface_endpoints()
- test_discovery_enabled.py: 12 tests for is_discovery_enabled()

Kotlin tests:
- DiscoveredInterfacesViewModelTest.kt: 22 tests covering autoconnected endpoints
  state, isAutoconnected() helper, and discovery settings loading
- DiscoveredInterfacesScreenTest.kt: 29 tests for UI helpers including
  isYggdrasilAddress(), formatInterfaceType(), and icon/badge visibility

Also fixes:
- Fix detekt ComplexCondition issues in RNodeWizardScreen, MapScreen, MainActivity
- Fix detekt LongMethod in ColumbaApplication by extracting restorePeerIdentities()
- Fix detekt SwallowedException in InterfaceRepository with verbose logging
- Fix detekt MatchingDeclarationName by moving FocusInterfaceDetails to own file
- Fix detekt ReturnCount in isYggdrasilAddress and ViewModel helper functions
- Update StartupConfigLoaderTest for new discovery settings
- Update TcpClientWizardViewModelTest for allInterfaceEntities changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 22:58:08 -05:00
torlando-tech
53fa9f24e0 feat: add connected indicator and improved icons for discovered interfaces
- Add "Connected" badge showing which discovered interfaces are currently
  auto-connected by RNS discovery
- Add getAutoconnectedEndpoints() API to fetch active auto-connected interfaces
- Use type-specific icons: globe for TCP, antenna for radio, incognito for I2P,
  tree-pine for Yggdrasil (detected via 0200::/7 IPv6 range)
- Show I2P b32 addresses with .b32.i2p suffix
- Display human-readable interface type labels (e.g., "Backbone (TCP)")

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 21:12:05 -05:00
torlando-tech
8bc1d054d4 feat: add discovery toggle and fix discovered interfaces display
- Add toggle in Discovered Interfaces screen to enable/disable discovery
- Service auto-restarts when toggle is changed (no app restart needed)
- Show bootstrap-enabled interface names in settings card
- Add discovery settings to SettingsRepository and StartupConfigLoader
- Include discover_interfaces and autoconnect_discovered_interfaces in config
- Include bootstrap_only for TCPClient interfaces in buildConfigJson
- Fix Python wrapper bug: discovered_interfaces() returns list, not dict
- Add bottom padding to list to avoid nav bar overlap

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 19:49:16 -05:00
torlando-tech
c40f7425ca test: Add comprehensive tests for interface name extraction
Add unit tests for ReceivingInterfaceInfo:
- 25 Kotlin tests covering TCP, Auto, BLE, RNode, Serial, and unknown interfaces
- Tests for friendly name extraction, interface type parsing, and edge cases

Refactor ReceivingInterfaceInfo to reduce cyclomatic complexity:
- Extract InterfaceCategory enum for display properties
- Extract categorizeInterface() and looksLikeAddress() helper functions
- Main function now delegates to smaller, focused functions

Add 2 additional Python tests for interface name formatting:
- Test interface.name same as class name returns class only
- Test interface.name being None returns class only

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 19:07:22 -05:00
torlando-tech
66982b18de feat: Show user-configured interface name in Node Details
Update the "Received Via" card in Node Details to show the user-configured
interface name (e.g., "Sideband Server") instead of generic type names
(e.g., "TCP/IP").

Python changes:
- Build formatted interface string "ClassName[UserConfiguredName]" from
  the interface object's type and .name attribute
- This allows Kotlin to extract both the friendly name and interface type

Kotlin changes:
- Add extractFriendlyName() to parse user-configured name from brackets
- Add extractInterfaceType() to parse class name before brackets
- Display friendly name as main text, interface type as subtitle

The card now shows:
- Title: "Received Via"
- Content: "Sideband Server" (user-configured name)
- Subtitle: "TCPClientInterface" (interface type)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 19:02:43 -05:00
torlando-tech
93d794c1fd feat: extend DiscoveredInterface data model with full RNS 1.1.x fields
Add comprehensive discovery data to DiscoveredInterface including:
- Transport/network IDs and status info (statusCode, lastHeard, hops)
- TCP-specific fields (reachableOn, port)
- Radio-specific fields (frequency, bandwidth, SF, CR, modulation)
- Location fields (latitude, longitude, height)
- Helper properties for interface type detection

Update Python bridge to return full discovery data from RNS 1.1.x API.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:11:18 -05:00
torlando-tech
e0dae212dc feat: add RNS 1.1.x bootstrap and interface discovery support (#216)
Add support for RNS 1.1.0+ interface discovery and bootstrap features,
enabling new users to connect without relying on deprecated public
infrastructure.

Changes:
- Add discovery settings to ReticulumConfig (discoverInterfaces,
  autoconnectDiscoveredInterfaces, interfaceDiscoverySources,
  requiredDiscoveryValue)
- Add bootstrapOnly option to TCPClient interface config
- Update Python config generation for discovery and bootstrap options
- Add get_discovered_interfaces() and is_discovery_enabled() API methods
- Extend AIDL interface with discovery methods
- Mark 3 reliable community servers as bootstrap candidates (Beleth,
  Quad4, FireZen)
- Add bootstrap toggle to TCP Client wizard UI

Note: The RNS fork needs to be rebased on 1.1.2 to enable the actual
discovery features. The code is ready but awaiting the dependency update.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:45:38 -05:00
Torlando
3ca089d02c
Merge pull request #97 from torlando-tech/dependabot/gradle/cameraX-1.5.2
deps(deps): bump cameraX from 1.4.1 to 1.5.2
2026-01-21 14:11:47 -05:00
torlando-tech
975c8c003a Fix flaky tests and improve CI failure visibility
- Harden 4 Python tests that were flaky due to mock object identity
  comparisons by using explicit sentinel values for link status constants
- Add failure annotations to CI workflow so root cause job is clearly
  marked when fail-fast cancels sibling jobs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 13:52:47 -05:00
Torlando
4af51e7c8a
Merge pull request #311 from torlando-tech/feature/issue-215-telemetry-collector-api
Add telemetry collector API with group tracking features
2026-01-21 13:49:52 -05:00
torlando-tech
7e95f278b3 Add tests for set_telemetry_allowed_requesters method
Cover all branches: success with items, empty list, lowercase
normalization, empty string filtering, set storage, deduplication,
and list replacement.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 13:32:29 -05:00
torlando-tech
9bf59cbec6 Fix telemetry request tests and add blocked requester tests
- Add requester to allowed list in tests that expect requests to succeed
- Add test_blocks_request_from_non_allowed_requester to verify blocking
- Add test_blocks_request_when_allowed_list_empty to verify empty = block all

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 12:44:42 -05:00
torlando-tech
2a79ed2613 Fix empty telemetry stream responses appearing in chat
Empty telemetry stream responses (0 entries from collector) were
appearing as blank messages in chat because is_location_only was
only set when the stream had entries. Now telemetry stream messages
are always marked as location-only regardless of whether they
contain data.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:28:37 -05:00
torlando-tech
b060512aee Add allowed telemetry requesters feature for host mode
Implements the ability to control which contacts can request telemetry
when acting as a collector (host mode). Users can select specific
contacts who are allowed to request their group's location data.
Requests from non-allowed contacts are silently blocked.

Changes:
- Add telemetry_allowed_requesters state and filtering in Python layer
- Add AIDL interface and bridge for setTelemetryAllowedRequesters
- Add SettingsRepository storage with DataStore persistence
- Add TelemetryCollectorManager state flow and Python sync
- Add SettingsViewModel state management with ContactRepository
- Add UI components: AllowedRequestersSection and AllowedRequestersDialog
- Fix ArrayList to Python list conversion in ReticulumServiceBinder
- Fix state preservation in SettingsViewModel.loadSettings()
- Add CLAUDE.md with Chaquopy ArrayList pitfall documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 10:39:49 -05:00
Torlando
3fe17c2ff9
Merge pull request #243 from torlando-tech/feature/issue-215-telemetry-collector-api
Add Telemetry Collector API support
2026-01-21 00:28:51 -05:00
Torlando
3657c288fe
Merge pull request #280 from torlando-tech/feature/usb-rnode-support
feat: USB RNode support (Issue #173)
2026-01-21 00:24:41 -05:00
torlando-tech
a05e26194c Add comprehensive tests for unpack_telemetry_stream
Add 16 new tests for unpack_telemetry_stream covering:
- Empty stream handling
- Bytes and string source hash conversion
- Entries with too few elements (validation)
- Invalid telemetry data handling
- Valid timestamp application (converted to milliseconds)
- Future timestamp rejection (>1 hour ahead)
- Zero/None timestamp handling
- Valid appearance parsing (icon, fg/bg colors)
- Invalid icon name formats (special chars, too long)
- Invalid color bytes (too short, non-bytes)
- Multiple entry processing
- Exception handling during entry processing

Total Python tests: 61

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 00:15:16 -05:00
torlando-tech
4282e99f7c Add comprehensive tests for send_telemetry_request and FIELD_COMMANDS handling
Expand Python test coverage to include:

TestSendTelemetryRequestSuccess (6 tests):
- Successful send with immediate identity recall
- Successful send with cached identity
- Error when local_lxmf_destination is missing
- Error when identity cannot be resolved after path request
- jarray conversion handling
- Correct FIELD_COMMANDS structure building

TestOnLxmfDeliveryFieldCommands (4 tests):
- FIELD_COMMANDS handling when collector enabled
- Skipping FIELD_COMMANDS when collector disabled
- Identity retry mechanism in delivery handler
- Ignoring non-collector requests

Total Python tests: 45 (up from 35)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 00:12:56 -05:00
torlando-tech
9b6452660c Expand Python test coverage for telemetry host mode
Add 14 new tests covering:
- _send_telemetry_stream_response: filtering by timebase, LXMF message
  creation, handling empty telemetry, cleanup of expired entries
- send_telemetry_request: error handling for uninitialized states
- FIELD_COMMANDS constants: verify all protocol constants exist
- Timebase filtering: verify received_at vs timestamp filtering

These tests improve coverage for the Python telemetry collector
implementation, specifically the FIELD_TELEMETRY_STREAM response
generation and FIELD_COMMANDS request handling paths.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 23:06:28 -05:00
torlando-tech
5b724e34fd Add comprehensive unit tests for telemetry collector host mode
- Add 4 tests to SettingsRepositoryTest for host mode persistence
- Add 7 tests to TelemetryCollectorManagerTest for host mode management
- Add 6 tests to SettingsViewModelTest for host mode UI state
- Create test_telemetry_host_mode.py with 21 Python tests for:
  - pack_telemetry_stream function
  - set_telemetry_collector_enabled method
  - _store_telemetry_for_collector method
  - _cleanup_expired_telemetry method
  - Integration tests for full workflow
- Fix detekt issues with appropriate suppressions
- Fix SwallowedException by including exception in log

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:30:04 -05:00
torlando-tech
ba2cf44dea Add telemetry collector (host mode) for Group Tracker
Enable Columba to act as a telemetry collector compatible with Sideband's
protocol. When Host Group mode is enabled, Columba will:

- Store incoming FIELD_TELEMETRY (0x02) location data from peers
- Handle FIELD_COMMANDS (0x09) telemetry requests
- Respond with FIELD_TELEMETRY_STREAM (0x03) containing all stored entries

Implementation details:
- In-memory storage with 24-hour TTL, keeping latest per source
- Uses received_at timestamp for timebase filtering (handles clock skew)
- Follows Sideband's identity recall + path request pattern
- UI toggle in Location Sharing settings card

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:04:13 -05:00
torlando-tech
2a19d58f1d Add telemetry request functionality to Group Tracker
Enable requesting location data from group host (telemetry collector):

- Add send_telemetry_request() to Python wrapper using FIELD_COMMANDS
- Add path request retry logic when identity not immediately known
- Add request toggle and interval settings to UI
- TelemetryCollectorManager handles periodic location requests
- New AIDL/protocol methods for sendTelemetryRequest

UI renamed from "Telemetry Collector" to "Group Tracker" with
user-friendly labels (Group Host, Share with group, etc.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 20:22:54 -05:00