Skip to content

Device: Tune Bluetooth scanning power automatically#560

Merged
d4rken merged 1 commit into
mainfrom
fix/ble-scan-mode-auto-policy
Apr 30, 2026
Merged

Device: Tune Bluetooth scanning power automatically#560
d4rken merged 1 commit into
mainfrom
fix/ble-scan-mode-auto-policy

Conversation

@d4rken

@d4rken d4rken commented Apr 30, 2026

Copy link
Copy Markdown
Member

What changed

The app now picks the right Bluetooth scanning power automatically based on the situation:

  • Low Latency when your AirPods are connected — fast updates while you're using them
  • Balanced when the app is open and your headphones aren't connected — same as before
  • Low Power when the app is in the background and no headphones are connected — saves battery

The "Scanner mode" setting in General Settings is gone — the app decides based on context. The TroubleShooter still bumps to Low Latency while it's actively scanning.

Also fixes a case where scanning could fail to start for users who hadn't granted the Bluetooth Connect permission, or where Android's headset profile took a while to come up.

Refs #41, #124, #137

Technical Context

  • Critical regression fix: the new BleScanModeController.scannerMode combine includes bluetoothManager.connectedDevices, which is stateIn(initialValue = null).filterNotNull() — it never emits if the HEADSET profile proxy stalls or BLUETOOTH_CONNECT is missing. The combine then never produced a mode and BlePodMonitor.createBleScanner() blocked forever. Wrapped that source with .onStart { emit(emptyList()) }.catch { emit(emptyList()) } so the controller emits within one tick and upgrades when real data arrives.
  • Refcounted override: replaced setTemporaryOverride / clearTemporaryOverride with suspend fun <T> withTemporaryOverride(mode, block): T. Internal state is MutableStateFlow<Map<ScannerMode, Int>>; exposed mode picks the highest-priority active override (LOW_LATENCY > BALANCED > LOW_POWER). Eliminates the race where one caller's finally could clear another caller's override.
  • Reactive bonded set: new BluetoothManager2.bondedDeviceAddresses: Flow<Set<BluetoothAddress>> driven by ACTION_BOND_STATE_CHANGED, replacing a one-shot bondedDevices().first() query inside mapLatest. Receiver-path query is wrapped in try/catch so a thrown SecurityException (e.g. permission revoked at runtime) doesn't escape onReceive.
  • TroubleShooter uses withTemporaryOverride(LOW_LATENCY) override@{ ... }. return@override replaces the old return@launch to keep early-exit paths legal through the non-inline lambda.
  • Cleanup: ScannerMode.{identifier, labelRes} removed (dead after UI deletion). Five settings_scanner_mode_* keys removed across all 76 locale files. The DataStore key core.scanner.mode retains stale values silently — no migration needed.
  • Out of scope: TroubleShooter still creates addressless AppleDeviceProfiles, which the new policy can't match for LOW_LATENCY until the user links a paired address. Pre-existing behavior, deferred.

Review checklist

  • BLE scanning starts within ~1s of app launch even before BLUETOOTH_CONNECT is granted (regression guard)
  • Bluetooth/ScannerMode log shows BALANCED in foreground without connected profile, LOW_POWER in background, LOW_LATENCY once profiled+bonded headphones connect
  • TroubleShooter raises override on entry and clears it on completion / cancellation / exception
  • No leftover references to R.string.settings_scanner_mode_* or generalSettings.scannerMode

…ections

Replaces the user-facing scanner mode setting with an automatic policy that picks LOW_LATENCY when a profile-paired device is connected, BALANCED in the foreground, and LOW_POWER in the background. The TroubleShooter scopes a temporary LOW_LATENCY override via a refcounted withTemporaryOverride block so overlapping callers stay correct.

Fixes a regression where the controller could block BLE scanning entirely if BLUETOOTH_CONNECT was missing or the HEADSET profile proxy stalled, and adds a reactive bondedDeviceAddresses flow so bond changes propagate without waiting for an unrelated input. Cleans up the now-dead scanner mode strings across all locales and unused ScannerMode fields.
@d4rken d4rken added bug Something isn't working coms/BLE Uses Apple's Proximity Payloads, based on BLE broadcasted advertisements. labels Apr 30, 2026
@d4rken d4rken merged commit c8f5ced into main Apr 30, 2026
10 checks passed
@d4rken d4rken deleted the fix/ble-scan-mode-auto-policy branch April 30, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working coms/BLE Uses Apple's Proximity Payloads, based on BLE broadcasted advertisements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant