Skip to content

fix: improve BLE BATTERY_SAVER discovery reliability#622

Merged
torlando-tech merged 2 commits intomainfrom
fix/ble-battery-saver-discovery
Mar 8, 2026
Merged

fix: improve BLE BATTERY_SAVER discovery reliability#622
torlando-tech merged 2 commits intomainfrom
fix/ble-battery-saver-discovery

Conversation

@torlando-tech
Copy link
Copy Markdown
Owner

Summary

  • AGGRESSIVE match mode instead of STICKY — ensures devices are detected even with few radio windows during short/infrequent scans
  • BALANCED idle scan mode instead of LOW_POWER — LOW_POWER's ~10% duty cycle leaves too few radio windows for reliable discovery; battery savings are already handled by the longer idle interval
  • BATTERY_SAVER tuning: scanDuration 5s→8s (~2 radio windows per scan), adRefresh 180s→90s (better advertising visibility between scans)
  • Explanatory comment on BlePowerSettings.getSettings() documenting why scanDurationMs is intentionally non-monotonic across profiles (5s, 10s, 8s)

Context

After the original BLE shutdown fixes in #609, an audit of all three power profiles found that BATTERY_SAVER had insufficient radio windows per scan for reliable discovery. These changes address that while keeping all profiles well under Android's 5-scans-per-30s throttle limit.

Test plan

  • Verify BATTERY_SAVER profile discovers nearby BLE devices within ~23s (one active scan cycle)
  • Verify BALANCED and PERFORMANCE profiles are unaffected
  • Verify advertising is refreshed every ~90s in BATTERY_SAVER mode
  • Confirm no Android scan throttle warnings in logcat across all profiles

🤖 Generated with Claude Code

- Use MATCH_MODE_AGGRESSIVE instead of MATCH_MODE_STICKY so even
  short scan windows detect nearby devices
- Keep SCAN_MODE_BALANCED for idle scans instead of LOW_POWER
  (LOW_POWER's ~10% duty cycle leaves too few radio windows)
- Tune BATTERY_SAVER: scanDuration 5s→8s (~2 radio windows),
  adRefresh 180s→90s (better visibility between scans)
- Add explanatory comment for non-monotonic scanDurationMs values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 8, 2026

Greptile Summary

This PR tunes the BATTERY_SAVER BLE power profile to improve device discovery reliability: it increases scan duration (5s → 8s) to guarantee more radio windows per scan, halves the advertising refresh interval (180s → 90s) for better peer visibility, switches the idle scan mode from LOW_POWER to BALANCED (avoiding LOW_POWER's ~10% duty cycle that starved discovery), and upgrades the global match mode from MATCH_MODE_STICKY to MATCH_MODE_AGGRESSIVE. A clarifying comment on the non-monotonic scanDurationMs values across profiles is also added.

Key observations from the review:

  • The MATCH_MODE_AGGRESSIVE change in performScan() is applied globally to all power profiles (PERFORMANCE, BALANCED, BATTERY_SAVER), not just BATTERY_SAVER as the PR description implies. This is likely intentional given that aggressive matching improves discovery reliability in general, but it is an undocumented behavioural change for the other profiles and should be called out.
  • After replacing SCAN_MODE_LOW_POWER with SCAN_MODE_BALANCED in the else branch, determineScanMode() now has two branches (scansWithoutNewDevices < IDLE_SCANS_THRESHOLD and else) that return the same value, leaving the second condition as effective dead code. The function should be simplified to a two-branch when.
  • All profiles remain safely under Android's 5-scans-per-30s throttle limit with these new durations.

Confidence Score: 4/5

  • Safe to merge with minor cleanup; the redundant when branch and unscoped MATCH_MODE_AGGRESSIVE change should be addressed.
  • The core BLE tuning is sound — scan durations, idle intervals, and scan mode changes are all correct and keep every profile well under Android's throttle limit. Two issues reduce confidence: (1) MATCH_MODE_AGGRESSIVE silently affects all power profiles beyond the PR's stated BATTERY_SAVER scope, and (2) determineScanMode() has a redundant unreachable branch that is dead code. Neither is a runtime crash risk, but the global match-mode change is an undocumented behavioural regression path for BALANCED/PERFORMANCE.
  • BleScanner.kt — both issues (global MATCH_MODE_AGGRESSIVE and redundant when-branch) are in this file.

Important Files Changed

Filename Overview
reticulum/src/main/java/com/lxmf/messenger/reticulum/ble/client/BleScanner.kt Changes match mode to MATCH_MODE_AGGRESSIVE (globally, not scoped to BATTERY_SAVER) and replaces LOW_POWER with BALANCED in the idle scan mode path; the resulting determineScanMode() has two now-identical branches that should be collapsed.
reticulum/src/main/java/com/lxmf/messenger/reticulum/ble/model/BlePowerSettings.kt BATTERY_SAVER scan duration bumped from 5s to 8s and advertising refresh halved from 180s to 90s; adds helpful explanatory comment on non-monotonic scanDurationMs values. Changes are straightforward and well-documented.

Sequence Diagram

sequenceDiagram
    participant ScanLoop as Scan Loop
    participant PS as performScan()
    participant DSM as determineScanMode()
    participant BLE as BT LE Scanner (HW)
    participant HSR as handleScanResult()

    ScanLoop->>PS: performScan(minRssi)
    PS->>DSM: determineScanMode()
    alt newDevicesInLastScan > 3
        DSM-->>PS: SCAN_MODE_LOW_LATENCY
    else all other cases (was LOW_POWER for idle)
        DSM-->>PS: SCAN_MODE_BALANCED
    end
    PS->>BLE: startScan(filter, MATCH_MODE_AGGRESSIVE)
    Note over BLE: Previously MATCH_MODE_STICKY for all profiles
    BLE-->>HSR: onScanResult() callbacks
    HSR->>HSR: filter by MIN_RSSI, update devices map
    Note over PS: delay(scanDurationMs) — BATTERY_SAVER 8s (was 5s)
    PS->>BLE: stopScan()
    PS-->>ScanLoop: return
    ScanLoop->>ScanLoop: delay(currentScanInterval)
    Note over ScanLoop: BATTERY_SAVER active=15s, idle=120s
    ScanLoop->>ScanLoop: adjustScanInterval()
Loading

Last reviewed commit: 19f4219

Both branches returned SCAN_MODE_BALANCED after the LOW_POWER removal,
making the IDLE_SCANS_THRESHOLD check redundant dead code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Mar 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@torlando-tech torlando-tech merged commit 6e2dc03 into main Mar 8, 2026
13 checks passed
@torlando-tech torlando-tech deleted the fix/ble-battery-saver-discovery branch March 8, 2026 00:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant