diff --git a/core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt b/core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt index 5085a8f5b5..5deccbe785 100644 --- a/core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt +++ b/core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt @@ -21,9 +21,16 @@ import com.juul.kable.AndroidPeripheral import com.juul.kable.Peripheral import com.juul.kable.PeripheralBuilder import com.juul.kable.PooledThreadingStrategy +import com.juul.kable.ScannerBuilder import com.juul.kable.toIdentifier import org.meshtastic.core.model.util.anonymize +// Kable's default trySendBlocking can park the scan-callback (sometimes main) thread in dense BLE +// environments, causing ANRs; preConflate drops excess advertisements instead (kable#654). +internal actual fun ScannerBuilder.platformScanConfig() { + preConflate = true +} + /** Android's scanner filters on address in hardware, so Kable's `Filter.Address` works natively here. */ internal actual val supportsNativeAddressScanFilter: Boolean = true diff --git a/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt b/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt index 56191b671f..a53680775a 100644 --- a/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt +++ b/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt @@ -75,6 +75,7 @@ open class KableBleScanner(private val loggingConfig: BleLoggingConfig) : BleSca internal open fun advertisements(filter: KableScanFilter): Flow { val scanner = Scanner { + platformScanConfig() logging { applyConfig(loggingConfig) } when (filter) { KableScanFilter.None -> Unit diff --git a/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt b/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt index 171f6286bf..8a1e3bd1a2 100644 --- a/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt +++ b/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt @@ -18,6 +18,10 @@ package org.meshtastic.core.ble import com.juul.kable.Peripheral import com.juul.kable.PeripheralBuilder +import com.juul.kable.ScannerBuilder + +/** Platform-specific configuration for the Scanner builder (e.g. Android's `preConflate`). */ +internal expect fun ScannerBuilder.platformScanConfig() /** * Whether Kable honours a scan filter on device address here. Android only: `Filter.Address` throws on Apple/JS, and diff --git a/core/ble/src/iosMain/kotlin/org/meshtastic/core/ble/NoopStubs.kt b/core/ble/src/iosMain/kotlin/org/meshtastic/core/ble/NoopStubs.kt index 6e9baf4d11..d6bc236b02 100644 --- a/core/ble/src/iosMain/kotlin/org/meshtastic/core/ble/NoopStubs.kt +++ b/core/ble/src/iosMain/kotlin/org/meshtastic/core/ble/NoopStubs.kt @@ -18,10 +18,15 @@ package org.meshtastic.core.ble import com.juul.kable.Peripheral import com.juul.kable.PeripheralBuilder +import com.juul.kable.ScannerBuilder // Kable's `Filter.Address` throws UnsupportedOperationException on Apple. internal actual val supportsNativeAddressScanFilter: Boolean = false +internal actual fun ScannerBuilder.platformScanConfig() { + // No-op: preConflate is Android-only. +} + /** No-op stubs for iOS target in core:ble. */ internal actual fun PeripheralBuilder.platformConfig(device: BleDevice, autoConnect: () -> Boolean) { // No-op for stubs diff --git a/core/ble/src/jvmMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt b/core/ble/src/jvmMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt index 0b4b4ae85f..188a1ad33b 100644 --- a/core/ble/src/jvmMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt +++ b/core/ble/src/jvmMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt @@ -18,8 +18,13 @@ package org.meshtastic.core.ble import com.juul.kable.Peripheral import com.juul.kable.PeripheralBuilder +import com.juul.kable.ScannerBuilder import com.juul.kable.toIdentifier +internal actual fun ScannerBuilder.platformScanConfig() { + // No-op: preConflate is Android-only. +} + // Kable's btleplug backend evaluates scan filters with a hardcoded `address = null`, so an address filter matches // nothing and the scan yields no advertisements at all. internal actual val supportsNativeAddressScanFilter: Boolean = false