From 2741e58cecbebbc3cb8d2efd438b6fc5eabbcf84 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Thu, 20 Jun 2024 18:33:14 +0100 Subject: [PATCH 01/14] [WIP] Workaround for Flipper connection mac change --- .../bridge/api/scanner/FlipperScanner.kt | 7 ++- .../impl/manager/FlipperBleManagerImpl.kt | 2 + .../bridge/impl/scanner/FlipperScannerImpl.kt | 33 ++++++++++- .../bridge/impl/utils/BridgeImplConfig.kt | 2 +- .../service/impl/FlipperServiceApiImpl.kt | 37 ++++++++---- .../delegate/FlipperSafeConnectWrapper.kt | 13 +++-- .../delegate/FlipperServiceConnectDelegate.kt | 57 ++++++++++++++++--- .../impl/delegate/RemoveBondHelper.java | 13 +++++ .../impl/model/SavedFlipperConnectionInfo.kt | 24 ++++++++ 9 files changed, 159 insertions(+), 29 deletions(-) create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt diff --git a/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt b/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt index fb93cb7de6..c8d36104a1 100644 --- a/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt +++ b/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt @@ -14,5 +14,10 @@ interface FlipperScanner { /** * @return flipper by id */ - fun findFlipperById(deviceId: String): Flow + suspend fun findFlipperById(deviceId: String): Flow + + /** + * @return flipper by id + */ + fun findFlipperByName(deviceName: String): Flow } diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt index 807ac10384..42019926bb 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.sync.Mutex import no.nordicsemi.android.ble.ConnectRequest import no.nordicsemi.android.ble.ConnectionPriorityRequest +import no.nordicsemi.android.ble.Request import javax.inject.Inject import javax.inject.Provider @@ -98,6 +99,7 @@ class FlipperBleManagerImpl @Inject constructor( } override suspend fun connectToDevice(device: BluetoothDevice) { + info { "Schedule to connect: $device" } withLock(bleMutex, "connect") { val connectRequestLocal = connect(device).retry( Constants.BLE.RECONNECT_COUNT, diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt index 45329ec573..3371599650 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt @@ -1,6 +1,7 @@ package com.flipperdevices.bridge.impl.scanner import android.Manifest +import android.annotation.SuppressLint import android.bluetooth.BluetoothAdapter import android.content.Context import android.content.pm.PackageManager @@ -14,11 +15,14 @@ import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.info import com.squareup.anvil.annotations.ContributesBinding import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat @@ -55,7 +59,7 @@ class FlipperScannerImpl @Inject constructor( } ).filter { it.address.startsWith(Constants.MAC_PREFIX) || - it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true + it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true }.map { discoveredBluetoothDevice -> var mutableDevicesList: List = emptyList() mutex.withLock { @@ -77,7 +81,7 @@ class FlipperScannerImpl @Inject constructor( } } - override fun findFlipperById(deviceId: String): Flow { + override suspend fun findFlipperById(deviceId: String): Flow { val bondedDevice = getAlreadyBondedDevices().firstOrNull { it.address == deviceId } @@ -88,6 +92,25 @@ class FlipperScannerImpl @Inject constructor( .map { DiscoveredBluetoothDevice(it) } } + @SuppressLint("MissingPermission") + override fun findFlipperByName( + deviceName: String + ): Flow = flow { + /*getAlreadyBondedDevices().filter { + it.name == deviceName + }.forEach { + emit(it) + }*/ + + scanner.scanFlow(provideSettings(), provideFilterForDefaultScan()) + .onEach { info { "Found: ${it.device.name} (name to found is $deviceName), isTrue: ${it.device.name == deviceName}" } } + .filter { it.device.name == deviceName } + .map { DiscoveredBluetoothDevice(it) } + .collect { + emit(it) + } + } + private fun getAlreadyBondedDevices(): List { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission( @@ -100,7 +123,7 @@ class FlipperScannerImpl @Inject constructor( return bluetoothAdapter.bondedDevices.filter { it.address?.startsWith(Constants.MAC_PREFIX) == true || - it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true + it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true }.map { DiscoveredBluetoothDevice( device = it, @@ -126,4 +149,8 @@ class FlipperScannerImpl @Inject constructor( private fun provideFilterForFindById(deviceId: String): List { return listOf(ScanFilter.Builder().setDeviceAddress(deviceId).build()) } + + private fun provideFilterForFindByName(deviceName: String): List { + return listOf(ScanFilter.Builder().setDeviceAddress(deviceName).build()) + } } diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt index 2a98563ad7..dfc80b902b 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt @@ -1,5 +1,5 @@ package com.flipperdevices.bridge.impl.utils object BridgeImplConfig { - const val BLE_VLOG = false + const val BLE_VLOG = true } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt index b70c3b6500..90922fd091 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt @@ -5,6 +5,7 @@ import com.flipperdevices.bridge.api.di.FlipperBleServiceGraph import com.flipperdevices.bridge.api.manager.FlipperBleManager import com.flipperdevices.bridge.service.api.FlipperServiceApi import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper +import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo import com.flipperdevices.core.di.SingleIn import com.flipperdevices.core.di.provideDelegate import com.flipperdevices.core.ktx.jre.FlipperDispatchers @@ -61,16 +62,23 @@ class FlipperServiceApiImpl @Inject constructor( var previousDeviceId: String? = null scope.launch(FlipperDispatchers.workStealingDispatcher) { - pairSettingsStore.data.map { it.deviceId }.collectLatest { deviceId -> - withLock(mutex, "connect") { - if (!unhandledExceptionApi.isBleConnectionForbiddenFlow().first() && - deviceId != previousDeviceId - ) { - previousDeviceId = deviceId - flipperSafeConnectWrapper.onActiveDeviceUpdate(deviceId) + pairSettingsStore.data + .collectLatest { pairSettings -> + withLock(mutex, "connect") { + if (!unhandledExceptionApi.isBleConnectionForbiddenFlow().first() && + pairSettings.deviceId != previousDeviceId + ) { + previousDeviceId = pairSettings.deviceId + flipperSafeConnectWrapper.onActiveDeviceUpdate( + if (pairSettings.deviceId == null) { + null + } else { + SavedFlipperConnectionInfo.build(pairSettings) + } + ) + } } } - } } } @@ -85,8 +93,10 @@ class FlipperServiceApiImpl @Inject constructor( if (bleManager.isConnected() || flipperSafeConnectWrapper.isTryingConnected()) { return@launchWithLock } - val deviceId = pairSettingsStore.data.first().deviceId - flipperSafeConnectWrapper.onActiveDeviceUpdate(deviceId) + val pairSetting = pairSettingsStore.data.first() + flipperSafeConnectWrapper.onActiveDeviceUpdate( + SavedFlipperConnectionInfo.build(pairSetting) + ) } override suspend fun disconnect(isForce: Boolean) = withLock(mutex, "disconnect") { @@ -97,8 +107,11 @@ class FlipperServiceApiImpl @Inject constructor( } override suspend fun reconnect() = withLock(mutex, "reconnect") { - val deviceId = pairSettingsStore.data.first().deviceId - flipperSafeConnectWrapper.onActiveDeviceUpdate(deviceId) + val pairSetting = pairSettingsStore.data.first() + + flipperSafeConnectWrapper.onActiveDeviceUpdate( + SavedFlipperConnectionInfo.build(pairSetting) + ) } suspend fun close() = withLock(mutex, "close") { diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt index 8a96ece93a..f441586a78 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt @@ -2,6 +2,7 @@ package com.flipperdevices.bridge.service.impl.delegate import com.flipperdevices.bridge.api.error.FlipperBleServiceError import com.flipperdevices.bridge.api.error.FlipperServiceErrorListener +import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo import com.flipperdevices.core.di.provideDelegate import com.flipperdevices.core.ktx.jre.FlipperDispatchers import com.flipperdevices.core.ktx.jre.launchWithLock @@ -34,7 +35,7 @@ class FlipperSafeConnectWrapper @Inject constructor( private val connectDelegate by connectDelegateProvider suspend fun onActiveDeviceUpdate( - deviceId: String? + connectionInfo: SavedFlipperConnectionInfo? ) = launchWithLock(mutex, scope, "onActiveDeviceUpdate") { info { "Call cancel and join to current job" } currentConnectingJob?.cancelAndJoin() @@ -43,7 +44,7 @@ class FlipperSafeConnectWrapper @Inject constructor( var errorOnDeviceUpdate: Throwable? do { errorOnDeviceUpdate = runCatching { - onActiveDeviceUpdateInternal(deviceId) + onActiveDeviceUpdateInternal(connectionInfo) }.exceptionOrNull() if (errorOnDeviceUpdate != null) { error(errorOnDeviceUpdate) { "Unexpected error on activeDeviceUpdate" } @@ -54,15 +55,17 @@ class FlipperSafeConnectWrapper @Inject constructor( fun isTryingConnected() = currentConnectingJob?.isActive ?: false - private suspend fun onActiveDeviceUpdateInternal(deviceId: String?) { - if (deviceId.isNullOrBlank()) { + private suspend fun onActiveDeviceUpdateInternal( + connectionInfo: SavedFlipperConnectionInfo? + ) { + if (connectionInfo == null || connectionInfo.id.isBlank()) { error { "Flipper id not found in storage" } connectDelegate.disconnect() return } try { - connectDelegate.reconnect(deviceId) + connectDelegate.reconnect(connectionInfo) } catch (securityException: SecurityException) { serviceErrorListener.onError(FlipperBleServiceError.CONNECT_BLUETOOTH_PERMISSION) error(securityException) { "On initial connect to device" } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt index 78ae5523b2..961daba7ce 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt @@ -7,11 +7,14 @@ import com.flipperdevices.bridge.api.manager.FlipperBleManager import com.flipperdevices.bridge.api.scanner.FlipperScanner import com.flipperdevices.bridge.api.utils.Constants import com.flipperdevices.bridge.api.utils.PermissionHelper +import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo import com.flipperdevices.core.di.provideDelegate import com.flipperdevices.core.ktx.jre.withLock import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error +import com.flipperdevices.core.log.info import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.withTimeout @@ -34,7 +37,9 @@ class FlipperServiceConnectDelegate @Inject constructor( private val scanner by scannerProvider private val adapter by adapterProvider - suspend fun reconnect(deviceId: String) = withLock(mutex, "reconnect") { + suspend fun reconnect( + connectionInfo: SavedFlipperConnectionInfo + ) = withLock(mutex, "reconnect") { // If we already connected to device, just ignore it disconnectInternal() @@ -57,7 +62,7 @@ class FlipperServiceConnectDelegate @Inject constructor( } // We try find device in manual mode and connect with it - findAndConnectToDeviceInternal(deviceId) + findAndConnectToDeviceInternal(connectionInfo) } suspend fun disconnect() = withLock(mutex, "disconnect") { @@ -72,7 +77,7 @@ class FlipperServiceConnectDelegate @Inject constructor( } catch (timeout: TimeoutCancellationException) { error(timeout.cause) { "Can't disconnect device with timeout" + - " ${Constants.BLE.DISCONNECT_TIMEOUT_MS}" + " ${Constants.BLE.DISCONNECT_TIMEOUT_MS}" } } } @@ -80,16 +85,54 @@ class FlipperServiceConnectDelegate @Inject constructor( // All rights must be obtained before calling this method @SuppressLint("MissingPermission") private suspend fun findAndConnectToDeviceInternal( - deviceId: String + connectionInfo: SavedFlipperConnectionInfo ) { - var device = adapter.bondedDevices.find { it.address == deviceId } + var device = adapter.bondedDevices.find { it.address == connectionInfo.id } if (device == null) { device = withTimeout(Constants.BLE.CONNECT_TIME_MS) { - scanner.findFlipperById(deviceId).first() + scanner.findFlipperById(connectionInfo.id).first() }.device } - bleManager.connectToDevice(device) + val connectWithTimeout = runCatching { + withTimeout(Constants.BLE.CONNECT_TIME_MS) { + bleManager.connectToDevice(device) + } + } + val exception = connectWithTimeout.exceptionOrNull() + if (exception != null) { + fallbackConnectByName(connectionInfo, exception) + } + } + + /** + * It's a fallback if the user's MAC address has changed, but the flipper is the same. + * This can happen in two cases: + * - Changing official firmware to custom firmware (and back again) + * - Updating official firmware after PR https://github.com/flipperdevices/flipperzero-firmware/pull/3723 + */ + private suspend fun fallbackConnectByName( + connectionInfo: SavedFlipperConnectionInfo, + exception: Throwable + ) { + if (connectionInfo.name == null) { + error(exception) { "Failed connect by ID and flipper name is unknown" } + throw exception + } + + val device = withTimeout(Constants.BLE.CONNECT_TIME_MS * 10) { + scanner.findFlipperByName(connectionInfo.name).filter { + it.address != connectionInfo.id + }.first() + }.device + info { "Found: $device" } + + //RemoveBondHelper.removeBand(device) + bleManager.disconnectDevice() + + withTimeout(Constants.BLE.CONNECT_TIME_MS * 10) { + bleManager.connectToDevice(device) + } } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java new file mode 100644 index 0000000000..2fc9d5228e --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java @@ -0,0 +1,13 @@ +package com.flipperdevices.bridge.service.impl.delegate; + +import android.bluetooth.BluetoothDevice; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class RemoveBondHelper { + public static void removeBand(BluetoothDevice device) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + final Method removeBond = device.getClass().getMethod("removeBond"); + removeBond.invoke(device); + } +} diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt new file mode 100644 index 0000000000..3e27195557 --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt @@ -0,0 +1,24 @@ +package com.flipperdevices.bridge.service.impl.model + +import com.flipperdevices.bridge.api.utils.Constants +import com.flipperdevices.core.preference.pb.PairSettings + +data class SavedFlipperConnectionInfo private constructor( + val id: String, + val name: String? +) { + companion object { + fun build( + pairSettings: PairSettings + ): SavedFlipperConnectionInfo { + val flipperName = if (pairSettings.deviceName.startsWith(Constants.DEVICENAME_PREFIX)) { + pairSettings.deviceName + } else "${Constants.DEVICENAME_PREFIX} ${pairSettings.deviceName}" + + return SavedFlipperConnectionInfo( + id = pairSettings.deviceId, + name = flipperName + ) + } + } +} \ No newline at end of file From 8fbc32a1136f42eb3fa6b6f8a2f0f5b9ae8f80c3 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Mon, 1 Jul 2024 17:06:49 +0100 Subject: [PATCH 02/14] add connection by name as fallback on first connection --- .../impl/manager/FlipperBleManagerImpl.kt | 2 + .../bridge/impl/scanner/FlipperScannerImpl.kt | 7 +- .../service/impl/FlipperServiceApiImpl.kt | 8 +- .../delegate/FlipperSafeConnectWrapper.kt | 52 ++++++++++-- .../delegate/FlipperServiceConnectDelegate.kt | 70 +++++---------- .../impl/delegate/RemoveBondHelper.java | 13 --- .../connection/FlipperConnectionByMac.kt | 60 +++++++++++++ .../connection/FlipperConnectionByName.kt | 85 +++++++++++++++++++ .../connection/FlipperConnectionDelegate.kt | 7 ++ .../FlipperConnectionInformationApiWrapper.kt | 33 +++++++ .../impl/di/FlipperBleServiceComponentImpl.kt | 24 +++++- .../impl/model/DeviceChangedMacException.kt | 9 ++ 12 files changed, 290 insertions(+), 80 deletions(-) delete mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt index 42019926bb..cdde8ab30f 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt @@ -101,6 +101,8 @@ class FlipperBleManagerImpl @Inject constructor( override suspend fun connectToDevice(device: BluetoothDevice) { info { "Schedule to connect: $device" } withLock(bleMutex, "connect") { + connectRequest?.cancelPendingConnection() + val connectRequestLocal = connect(device).retry( Constants.BLE.RECONNECT_COUNT, Constants.BLE.RECONNECT_TIME_MS.toInt() diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt index 3371599650..ed93c38348 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt @@ -94,16 +94,15 @@ class FlipperScannerImpl @Inject constructor( @SuppressLint("MissingPermission") override fun findFlipperByName( - deviceName: String + deviceName: String, ): Flow = flow { - /*getAlreadyBondedDevices().filter { + getAlreadyBondedDevices().filter { it.name == deviceName }.forEach { emit(it) - }*/ + } scanner.scanFlow(provideSettings(), provideFilterForDefaultScan()) - .onEach { info { "Found: ${it.device.name} (name to found is $deviceName), isTrue: ${it.device.name == deviceName}" } } .filter { it.device.name == deviceName } .map { DiscoveredBluetoothDevice(it) } .collect { diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt index 90922fd091..e15d4b32d8 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt @@ -5,6 +5,7 @@ import com.flipperdevices.bridge.api.di.FlipperBleServiceGraph import com.flipperdevices.bridge.api.manager.FlipperBleManager import com.flipperdevices.bridge.service.api.FlipperServiceApi import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper +import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionInformationApiWrapper import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo import com.flipperdevices.core.di.SingleIn import com.flipperdevices.core.di.provideDelegate @@ -48,7 +49,12 @@ class FlipperServiceApiImpl @Inject constructor( private val mutex = Mutex() private var disconnectForced = false - override val connectionInformationApi = bleManager.connectionInformationApi + override val connectionInformationApi by lazy { + FlipperConnectionInformationApiWrapper( + flipperConnectionSource = bleManager.connectionInformationApi, + safeConnectWrapper = flipperSafeConnectWrapper + ) + } override val requestApi = bleManager.flipperRequestApi override val flipperInformationApi = bleManager.informationApi override val flipperVersionApi = bleManager.flipperVersionApi diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt index f441586a78..a6a8b7a92b 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt @@ -1,7 +1,9 @@ package com.flipperdevices.bridge.service.impl.delegate +import androidx.datastore.core.DataStore import com.flipperdevices.bridge.api.error.FlipperBleServiceError import com.flipperdevices.bridge.api.error.FlipperServiceErrorListener +import com.flipperdevices.bridge.service.impl.model.DeviceChangedMacException import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo import com.flipperdevices.core.di.provideDelegate import com.flipperdevices.core.ktx.jre.FlipperDispatchers @@ -9,9 +11,13 @@ import com.flipperdevices.core.ktx.jre.launchWithLock import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.log.info +import com.flipperdevices.core.preference.pb.PairSettings +import com.flipperdevices.core.preference.pb.copy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex @@ -22,10 +28,13 @@ import javax.inject.Provider class FlipperSafeConnectWrapper @Inject constructor( scopeProvider: Provider, serviceErrorListenerProvider: Provider, - connectDelegateProvider: Provider + connectDelegateProvider: Provider, + dataStoreProvider: Provider> ) : LogTagProvider { override val TAG = "FlipperSafeConnectWrapper" + private val isConnectingMutableStateFlow = MutableStateFlow(false) + // It makes sure that we don't change the currentConnectingJob variable in different threads private val mutex = Mutex() private var currentConnectingJob: Job? = null @@ -33,6 +42,9 @@ class FlipperSafeConnectWrapper @Inject constructor( private val scope by scopeProvider private val serviceErrorListener by serviceErrorListenerProvider private val connectDelegate by connectDelegateProvider + private val dataStore by dataStoreProvider + + fun isConnectingFlow() = isConnectingMutableStateFlow.asStateFlow() suspend fun onActiveDeviceUpdate( connectionInfo: SavedFlipperConnectionInfo? @@ -41,15 +53,21 @@ class FlipperSafeConnectWrapper @Inject constructor( currentConnectingJob?.cancelAndJoin() info { "Job canceled! Call connect again" } currentConnectingJob = scope.launch(FlipperDispatchers.workStealingDispatcher) { - var errorOnDeviceUpdate: Throwable? + var jobCompleted = false + isConnectingMutableStateFlow.emit(true) do { - errorOnDeviceUpdate = runCatching { + val deviceUpdateResult = runCatching { onActiveDeviceUpdateInternal(connectionInfo) - }.exceptionOrNull() + } + val errorOnDeviceUpdate = deviceUpdateResult.exceptionOrNull() if (errorOnDeviceUpdate != null) { error(errorOnDeviceUpdate) { "Unexpected error on activeDeviceUpdate" } } - } while (isActive && errorOnDeviceUpdate != null) + if (deviceUpdateResult.getOrNull() == true) { + jobCompleted = true + } + } while (isActive && jobCompleted.not()) + isConnectingMutableStateFlow.emit(false) } } @@ -57,21 +75,39 @@ class FlipperSafeConnectWrapper @Inject constructor( private suspend fun onActiveDeviceUpdateInternal( connectionInfo: SavedFlipperConnectionInfo? - ) { + ): Boolean { if (connectionInfo == null || connectionInfo.id.isBlank()) { error { "Flipper id not found in storage" } connectDelegate.disconnect() - return + return true } try { - connectDelegate.reconnect(connectionInfo) + return connectDelegate.reconnect(connectionInfo) } catch (securityException: SecurityException) { serviceErrorListener.onError(FlipperBleServiceError.CONNECT_BLUETOOTH_PERMISSION) error(securityException) { "On initial connect to device" } + + return true } catch (bleDisabled: BluetoothDisabledException) { serviceErrorListener.onError(FlipperBleServiceError.CONNECT_BLUETOOTH_DISABLED) error(bleDisabled) { "On initial connect to device" } + + return true + } catch (changedMac: DeviceChangedMacException) { + error(changedMac) { "Mac changed from ${changedMac.oldMacAddress} to ${changedMac.newMacAddress}" } + dataStore.updateData { data -> + if (data.deviceId == changedMac.oldMacAddress) { + return@updateData data.toBuilder() + .setDeviceId(changedMac.newMacAddress) + .build() + } else { + error { "Wrong device id for mac address change request" } + return@updateData data + } + } + + return false } } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt index 961daba7ce..02a3d19b0a 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt @@ -7,9 +7,13 @@ import com.flipperdevices.bridge.api.manager.FlipperBleManager import com.flipperdevices.bridge.api.scanner.FlipperScanner import com.flipperdevices.bridge.api.utils.Constants import com.flipperdevices.bridge.api.utils.PermissionHelper +import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionByMac +import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionByName +import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionDelegate import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo import com.flipperdevices.core.di.provideDelegate import com.flipperdevices.core.ktx.jre.withLock +import com.flipperdevices.core.ktx.jre.withLockResult import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.log.info @@ -25,8 +29,9 @@ import javax.inject.Provider class FlipperServiceConnectDelegate @Inject constructor( bleManagerProvider: Provider, contextProvider: Provider, - scannerProvider: Provider, - adapterProvider: Provider + adapterProvider: Provider, + flipperConnectionByMac: Provider, + flipperConnectionByName: Provider ) : LogTagProvider { override val TAG = "FlipperServiceConnectDelegate" @@ -34,12 +39,16 @@ class FlipperServiceConnectDelegate @Inject constructor( private val bleManager by bleManagerProvider private val context by contextProvider - private val scanner by scannerProvider private val adapter by adapterProvider + private val connectionDelegates = listOf( + flipperConnectionByMac.get(), + flipperConnectionByName.get() + ) + suspend fun reconnect( connectionInfo: SavedFlipperConnectionInfo - ) = withLock(mutex, "reconnect") { + ): Boolean = withLockResult(mutex, "reconnect") { // If we already connected to device, just ignore it disconnectInternal() @@ -62,7 +71,7 @@ class FlipperServiceConnectDelegate @Inject constructor( } // We try find device in manual mode and connect with it - findAndConnectToDeviceInternal(connectionInfo) + return@withLockResult findAndConnectToDeviceInternal(connectionInfo) } suspend fun disconnect() = withLock(mutex, "disconnect") { @@ -86,53 +95,12 @@ class FlipperServiceConnectDelegate @Inject constructor( @SuppressLint("MissingPermission") private suspend fun findAndConnectToDeviceInternal( connectionInfo: SavedFlipperConnectionInfo - ) { - var device = adapter.bondedDevices.find { it.address == connectionInfo.id } - - if (device == null) { - device = withTimeout(Constants.BLE.CONNECT_TIME_MS) { - scanner.findFlipperById(connectionInfo.id).first() - }.device - } - - val connectWithTimeout = runCatching { - withTimeout(Constants.BLE.CONNECT_TIME_MS) { - bleManager.connectToDevice(device) + ): Boolean { + for (delegate in connectionDelegates) { + if (delegate.connect(connectionInfo)) { + return true } } - val exception = connectWithTimeout.exceptionOrNull() - if (exception != null) { - fallbackConnectByName(connectionInfo, exception) - } - } - - /** - * It's a fallback if the user's MAC address has changed, but the flipper is the same. - * This can happen in two cases: - * - Changing official firmware to custom firmware (and back again) - * - Updating official firmware after PR https://github.com/flipperdevices/flipperzero-firmware/pull/3723 - */ - private suspend fun fallbackConnectByName( - connectionInfo: SavedFlipperConnectionInfo, - exception: Throwable - ) { - if (connectionInfo.name == null) { - error(exception) { "Failed connect by ID and flipper name is unknown" } - throw exception - } - - val device = withTimeout(Constants.BLE.CONNECT_TIME_MS * 10) { - scanner.findFlipperByName(connectionInfo.name).filter { - it.address != connectionInfo.id - }.first() - }.device - info { "Found: $device" } - - //RemoveBondHelper.removeBand(device) - bleManager.disconnectDevice() - - withTimeout(Constants.BLE.CONNECT_TIME_MS * 10) { - bleManager.connectToDevice(device) - } + return false } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java deleted file mode 100644 index 2fc9d5228e..0000000000 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/RemoveBondHelper.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.flipperdevices.bridge.service.impl.delegate; - -import android.bluetooth.BluetoothDevice; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class RemoveBondHelper { - public static void removeBand(BluetoothDevice device) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - final Method removeBond = device.getClass().getMethod("removeBond"); - removeBond.invoke(device); - } -} diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt new file mode 100644 index 0000000000..8a9e4ba96a --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt @@ -0,0 +1,60 @@ +package com.flipperdevices.bridge.service.impl.delegate.connection + +import android.annotation.SuppressLint +import android.bluetooth.BluetoothAdapter +import com.flipperdevices.bridge.api.manager.FlipperBleManager +import com.flipperdevices.bridge.api.scanner.FlipperScanner +import com.flipperdevices.bridge.api.utils.Constants +import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo +import com.flipperdevices.core.di.provideDelegate +import com.flipperdevices.core.log.LogTagProvider +import com.flipperdevices.core.log.error +import com.flipperdevices.core.log.info +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withTimeout +import javax.inject.Inject +import javax.inject.Provider + +class FlipperConnectionByMac @Inject constructor( + bleManagerProvider: Provider, + scannerProvider: Provider, + adapterProvider: Provider +) : FlipperConnectionDelegate, LogTagProvider { + override val TAG = "FlipperConnectionByMac" + + private val bleManager by bleManagerProvider + private val scanner by scannerProvider + private val adapter by adapterProvider + + + @SuppressLint("MissingPermission") + override suspend fun connect(connectionInfo: SavedFlipperConnectionInfo): Boolean { + info { "Start connection by $connectionInfo" } + var device = adapter.bondedDevices.find { it.address == connectionInfo.id } + + if (device == null) { + device = runCatching { + withTimeout(Constants.BLE.CONNECT_TIME_MS) { + scanner.findFlipperById(connectionInfo.id).first() + }.device + }.getOrNull() + } + if (device == null) { + return false + } + + val connectWithTimeout = runCatching { + withTimeout(Constants.BLE.CONNECT_TIME_MS) { + bleManager.connectToDevice(device) + } + } + val exception = connectWithTimeout.exceptionOrNull() + + if (exception != null) { + error(exception) { "Failed connect to device by MAC" } + return false + } + return true + } + +} \ No newline at end of file diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt new file mode 100644 index 0000000000..190779c247 --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt @@ -0,0 +1,85 @@ +package com.flipperdevices.bridge.service.impl.delegate.connection + +import android.bluetooth.BluetoothAdapter +import com.flipperdevices.bridge.api.manager.FlipperBleManager +import com.flipperdevices.bridge.api.scanner.FlipperScanner +import com.flipperdevices.bridge.api.utils.Constants +import com.flipperdevices.bridge.service.impl.model.DeviceChangedMacException +import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo +import com.flipperdevices.core.di.provideDelegate +import com.flipperdevices.core.log.LogTagProvider +import com.flipperdevices.core.log.error +import com.flipperdevices.core.log.info +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.timeout +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.withTimeout +import kotlin.time.DurationUnit +import kotlin.time.toDuration +import javax.inject.Inject +import javax.inject.Provider + + +/** + * It's a fallback if the user's MAC address has changed, but the flipper is the same. + * This can happen in two cases: + * - Changing official firmware to custom firmware (and back again) + * - Updating official firmware after PR https://github.com/flipperdevices/flipperzero-firmware/pull/3723 + */ +class FlipperConnectionByName @Inject constructor( + bleManagerProvider: Provider, + scannerProvider: Provider +) : FlipperConnectionDelegate, LogTagProvider { + override val TAG = "FlipperConnectionByName" + + + private val bleManager by bleManagerProvider + private val scanner by scannerProvider + + + @OptIn(FlowPreview::class) + override suspend fun connect(connectionInfo: SavedFlipperConnectionInfo): Boolean { + info { "Start connection by $connectionInfo" } + if (connectionInfo.name == null) { + error { "Failed connect by ID and flipper name is unknown" } + return false + } + + val devices = scanner.findFlipperByName(connectionInfo.name).filter { + it.address != connectionInfo.id + }.timeout(Constants.BLE.CONNECT_TIME_MS.toDuration(DurationUnit.MILLISECONDS)) + .catch { exception -> + if (exception !is TimeoutCancellationException) { + // Throw other exceptions. + throw exception + } + }.toList() + info { "Found: $devices" } + + for (device in devices) { + info { "Connect to ${device.address}..." } + val result = runCatching { + withTimeout(Constants.BLE.CONNECT_TIME_MS) { + bleManager.connectToDevice(device.device) + } + } + info { "Connect to ${device.address} ${if (result.isSuccess) "SUCCESS" else "FAILED"}" } + if (result.isSuccess) { + info { "Connect to ${device.address} SUCCESS" } + + throw DeviceChangedMacException( + oldMacAddress = connectionInfo.id, + newMacAddress = device.address + ) + } else { + error(result.exceptionOrNull()) { "Connect to ${device.address} FAILED" } + } + } + + return false + } +} \ No newline at end of file diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt new file mode 100644 index 0000000000..38c6fa4cca --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt @@ -0,0 +1,7 @@ +package com.flipperdevices.bridge.service.impl.delegate.connection + +import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo + +interface FlipperConnectionDelegate { + suspend fun connect(connectionInfo: SavedFlipperConnectionInfo): Boolean +} \ No newline at end of file diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt new file mode 100644 index 0000000000..dcb2f560b0 --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt @@ -0,0 +1,33 @@ +package com.flipperdevices.bridge.service.impl.delegate.connection + +import com.flipperdevices.bridge.api.manager.delegates.FlipperConnectionInformationApi +import com.flipperdevices.bridge.api.manager.ktx.state.ConnectionState +import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +class FlipperConnectionInformationApiWrapper( + private val flipperConnectionSource: FlipperConnectionInformationApi, + private val safeConnectWrapper: FlipperSafeConnectWrapper +) : FlipperConnectionInformationApi { + override fun isDeviceConnected() = flipperConnectionSource.isDeviceConnected() + + override fun getConnectionStateFlow(): Flow { + return combine( + flipperConnectionSource.getConnectionStateFlow(), + safeConnectWrapper.isConnectingFlow() + ) { state, isConnecting -> + when (state) { + is ConnectionState.Disconnected -> if (isConnecting) { + ConnectionState.Connecting + } else { + state + } + + else -> state + } + } + } + + override fun getConnectedDeviceName() = flipperConnectionSource.getConnectedDeviceName() +} \ No newline at end of file diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt index 408c368afd..b001bc1a9a 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt @@ -12,6 +12,8 @@ import com.flipperdevices.bridge.service.impl.delegate.FlipperActionNotifierImpl import com.flipperdevices.bridge.service.impl.delegate.FlipperLagsDetectorImpl import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper import com.flipperdevices.bridge.service.impl.delegate.FlipperServiceConnectDelegate +import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionByMac +import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionByName import kotlinx.coroutines.CoroutineScope import javax.inject.Provider @@ -75,12 +77,27 @@ class FlipperBleServiceComponentImpl( ) } + private val flipperConnectionByMac by lazy { + FlipperConnectionByMac( + bleManagerProvider = { flipperBleManagerImpl }, + adapterProvider = { bluetoothAdapter }, + scannerProvider = { bluetoothScanner } + ) + } + private val flipperConnectionByName by lazy { + FlipperConnectionByName( + bleManagerProvider = { flipperBleManagerImpl }, + scannerProvider = { bluetoothScanner } + ) + } + private val flipperServiceConnectDelegate by lazy { FlipperServiceConnectDelegate( bleManagerProvider = { flipperBleManagerImpl }, contextProvider = { context }, - scannerProvider = { bluetoothScanner }, - adapterProvider = { bluetoothAdapter } + adapterProvider = { bluetoothAdapter }, + flipperConnectionByMac = { flipperConnectionByMac }, + flipperConnectionByName = { flipperConnectionByName } ) } @@ -88,7 +105,8 @@ class FlipperBleServiceComponentImpl( FlipperSafeConnectWrapper( scopeProvider = { scope }, serviceErrorListenerProvider = { serviceErrorListener }, - connectDelegateProvider = { flipperServiceConnectDelegate } + connectDelegateProvider = { flipperServiceConnectDelegate }, + dataStoreProvider = { pairSettingsStore } ) } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt new file mode 100644 index 0000000000..4b70a4e255 --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt @@ -0,0 +1,9 @@ +package com.flipperdevices.bridge.service.impl.model + +/** + * An Exception occurs when we realise that for some reason the device has changed its mac address + */ +data class DeviceChangedMacException( + val oldMacAddress: String, + val newMacAddress: String +) : RuntimeException() \ No newline at end of file From 99928c076a7930bb89e872397090eca3ca7b9e13 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Mon, 1 Jul 2024 18:32:17 +0100 Subject: [PATCH 03/14] Manually autoreconnect flipper --- .../impl/manager/FlipperBleManagerImpl.kt | 2 +- .../service/impl/FlipperServiceApiImpl.kt | 45 ++++++++++++------- .../delegate/FlipperSafeConnectWrapper.kt | 4 +- .../FlipperConnectionInformationApiWrapper.kt | 3 ++ 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt index cdde8ab30f..61f653c11d 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt @@ -106,7 +106,7 @@ class FlipperBleManagerImpl @Inject constructor( val connectRequestLocal = connect(device).retry( Constants.BLE.RECONNECT_COUNT, Constants.BLE.RECONNECT_TIME_MS.toInt() - ).useAutoConnect(true) + ) connectRequestLocal.enqueue() diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt index e15d4b32d8..51014163bc 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt @@ -3,6 +3,7 @@ package com.flipperdevices.bridge.service.impl import androidx.datastore.core.DataStore import com.flipperdevices.bridge.api.di.FlipperBleServiceGraph import com.flipperdevices.bridge.api.manager.FlipperBleManager +import com.flipperdevices.bridge.api.manager.ktx.state.ConnectionState import com.flipperdevices.bridge.service.api.FlipperServiceApi import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionInformationApiWrapper @@ -20,6 +21,8 @@ import com.flipperdevices.unhandledexception.api.UnhandledExceptionApi import com.squareup.anvil.annotations.ContributesBinding import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -68,23 +71,34 @@ class FlipperServiceApiImpl @Inject constructor( var previousDeviceId: String? = null scope.launch(FlipperDispatchers.workStealingDispatcher) { - pairSettingsStore.data - .collectLatest { pairSettings -> - withLock(mutex, "connect") { - if (!unhandledExceptionApi.isBleConnectionForbiddenFlow().first() && - pairSettings.deviceId != previousDeviceId - ) { - previousDeviceId = pairSettings.deviceId - flipperSafeConnectWrapper.onActiveDeviceUpdate( - if (pairSettings.deviceId == null) { - null - } else { - SavedFlipperConnectionInfo.build(pairSettings) - } - ) - } + combine( + bleManager.connectionInformationApi + .getConnectionStateFlow(), + pairSettingsStore.data + ) { connectionState, pairSetting -> + val connectionInfo = if (pairSetting.deviceId != null) { + SavedFlipperConnectionInfo.build(pairSetting) + } else { + null + } + (connectionState is ConnectionState.Disconnected) to connectionInfo + }.collect { (isDeviceDisconnected, connectionInfo) -> + withLock(mutex, "connect") { + if (unhandledExceptionApi.isBleConnectionForbiddenFlow().first()) { + return@withLock + } + + if (previousDeviceId != connectionInfo?.id) { // Reconnect + info { "Reconnect because device id changed" } + flipperSafeConnectWrapper.onActiveDeviceUpdate(connectionInfo) + previousDeviceId = connectionInfo?.id + } else if (isDeviceDisconnected && !disconnectForced) { // Autoreconnect + info { "Reconnect because device is disconnected, but not forced" } + flipperSafeConnectWrapper.onActiveDeviceUpdate(connectionInfo) + previousDeviceId = connectionInfo?.id } } + } } } @@ -113,6 +127,7 @@ class FlipperServiceApiImpl @Inject constructor( } override suspend fun reconnect() = withLock(mutex, "reconnect") { + disconnectForced = false val pairSetting = pairSettingsStore.data.first() flipperSafeConnectWrapper.onActiveDeviceUpdate( diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt index a6a8b7a92b..7cb7040598 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt @@ -67,7 +67,9 @@ class FlipperSafeConnectWrapper @Inject constructor( jobCompleted = true } } while (isActive && jobCompleted.not()) - isConnectingMutableStateFlow.emit(false) + if (jobCompleted) { + isConnectingMutableStateFlow.emit(false) + } } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt index dcb2f560b0..c2b6ee7a94 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt @@ -3,6 +3,8 @@ package com.flipperdevices.bridge.service.impl.delegate.connection import com.flipperdevices.bridge.api.manager.delegates.FlipperConnectionInformationApi import com.flipperdevices.bridge.api.manager.ktx.state.ConnectionState import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper +import com.flipperdevices.core.log.LogTagProvider +import com.flipperdevices.core.log.info import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -10,6 +12,7 @@ class FlipperConnectionInformationApiWrapper( private val flipperConnectionSource: FlipperConnectionInformationApi, private val safeConnectWrapper: FlipperSafeConnectWrapper ) : FlipperConnectionInformationApi { + override fun isDeviceConnected() = flipperConnectionSource.isDeviceConnected() override fun getConnectionStateFlow(): Flow { From a3fb7615f6966fb1fc6246997327850aa214e2f5 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Mon, 1 Jul 2024 18:35:18 +0100 Subject: [PATCH 04/14] Fix lint --- .../bridge/impl/manager/FlipperBleManagerImpl.kt | 1 - .../bridge/impl/scanner/FlipperScannerImpl.kt | 10 ++-------- .../bridge/service/impl/FlipperServiceApiImpl.kt | 3 --- .../impl/delegate/FlipperSafeConnectWrapper.kt | 1 - .../impl/delegate/FlipperServiceConnectDelegate.kt | 6 +----- .../delegate/connection/FlipperConnectionByMac.kt | 4 +--- .../delegate/connection/FlipperConnectionByName.kt | 11 +++-------- .../delegate/connection/FlipperConnectionDelegate.kt | 2 +- .../FlipperConnectionInformationApiWrapper.kt | 4 +--- .../service/impl/model/DeviceChangedMacException.kt | 2 +- .../service/impl/model/SavedFlipperConnectionInfo.kt | 6 ++++-- 11 files changed, 14 insertions(+), 36 deletions(-) diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt index 61f653c11d..5fcbf6c05e 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/manager/FlipperBleManagerImpl.kt @@ -43,7 +43,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.sync.Mutex import no.nordicsemi.android.ble.ConnectRequest import no.nordicsemi.android.ble.ConnectionPriorityRequest -import no.nordicsemi.android.ble.Request import javax.inject.Inject import javax.inject.Provider diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt index ed93c38348..ae54430075 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt @@ -15,14 +15,12 @@ import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.info import com.squareup.anvil.annotations.ContributesBinding import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat @@ -59,7 +57,7 @@ class FlipperScannerImpl @Inject constructor( } ).filter { it.address.startsWith(Constants.MAC_PREFIX) || - it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true + it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true }.map { discoveredBluetoothDevice -> var mutableDevicesList: List = emptyList() mutex.withLock { @@ -122,7 +120,7 @@ class FlipperScannerImpl @Inject constructor( return bluetoothAdapter.bondedDevices.filter { it.address?.startsWith(Constants.MAC_PREFIX) == true || - it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true + it.name?.startsWith(Constants.DEVICENAME_PREFIX) == true }.map { DiscoveredBluetoothDevice( device = it, @@ -148,8 +146,4 @@ class FlipperScannerImpl @Inject constructor( private fun provideFilterForFindById(deviceId: String): List { return listOf(ScanFilter.Builder().setDeviceAddress(deviceId).build()) } - - private fun provideFilterForFindByName(deviceName: String): List { - return listOf(ScanFilter.Builder().setDeviceAddress(deviceName).build()) - } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt index 51014163bc..d5277ef88f 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt @@ -20,11 +20,8 @@ import com.flipperdevices.core.preference.pb.PairSettings import com.flipperdevices.unhandledexception.api.UnhandledExceptionApi import com.squareup.anvil.annotations.ContributesBinding import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import java.util.concurrent.atomic.AtomicBoolean diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt index 7cb7040598..b38cf51e93 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt @@ -12,7 +12,6 @@ import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.log.info import com.flipperdevices.core.preference.pb.PairSettings -import com.flipperdevices.core.preference.pb.copy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt index 02a3d19b0a..f6ffd50731 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperServiceConnectDelegate.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.bluetooth.BluetoothAdapter import android.content.Context import com.flipperdevices.bridge.api.manager.FlipperBleManager -import com.flipperdevices.bridge.api.scanner.FlipperScanner import com.flipperdevices.bridge.api.utils.Constants import com.flipperdevices.bridge.api.utils.PermissionHelper import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionByMac @@ -16,10 +15,7 @@ import com.flipperdevices.core.ktx.jre.withLock import com.flipperdevices.core.ktx.jre.withLockResult import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error -import com.flipperdevices.core.log.info import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.withTimeout import no.nordicsemi.android.ble.exception.BluetoothDisabledException @@ -86,7 +82,7 @@ class FlipperServiceConnectDelegate @Inject constructor( } catch (timeout: TimeoutCancellationException) { error(timeout.cause) { "Can't disconnect device with timeout" + - " ${Constants.BLE.DISCONNECT_TIMEOUT_MS}" + " ${Constants.BLE.DISCONNECT_TIMEOUT_MS}" } } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt index 8a9e4ba96a..a7cee97081 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt @@ -26,7 +26,6 @@ class FlipperConnectionByMac @Inject constructor( private val scanner by scannerProvider private val adapter by adapterProvider - @SuppressLint("MissingPermission") override suspend fun connect(connectionInfo: SavedFlipperConnectionInfo): Boolean { info { "Start connection by $connectionInfo" } @@ -56,5 +55,4 @@ class FlipperConnectionByMac @Inject constructor( } return true } - -} \ No newline at end of file +} diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt index 190779c247..d0755f00f9 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt @@ -1,6 +1,5 @@ package com.flipperdevices.bridge.service.impl.delegate.connection -import android.bluetooth.BluetoothAdapter import com.flipperdevices.bridge.api.manager.FlipperBleManager import com.flipperdevices.bridge.api.scanner.FlipperScanner import com.flipperdevices.bridge.api.utils.Constants @@ -14,15 +13,13 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.timeout import kotlinx.coroutines.flow.toList import kotlinx.coroutines.withTimeout -import kotlin.time.DurationUnit -import kotlin.time.toDuration import javax.inject.Inject import javax.inject.Provider - +import kotlin.time.DurationUnit +import kotlin.time.toDuration /** * It's a fallback if the user's MAC address has changed, but the flipper is the same. @@ -36,11 +33,9 @@ class FlipperConnectionByName @Inject constructor( ) : FlipperConnectionDelegate, LogTagProvider { override val TAG = "FlipperConnectionByName" - private val bleManager by bleManagerProvider private val scanner by scannerProvider - @OptIn(FlowPreview::class) override suspend fun connect(connectionInfo: SavedFlipperConnectionInfo): Boolean { info { "Start connection by $connectionInfo" } @@ -82,4 +77,4 @@ class FlipperConnectionByName @Inject constructor( return false } -} \ No newline at end of file +} diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt index 38c6fa4cca..adfd122981 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionDelegate.kt @@ -4,4 +4,4 @@ import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo interface FlipperConnectionDelegate { suspend fun connect(connectionInfo: SavedFlipperConnectionInfo): Boolean -} \ No newline at end of file +} diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt index c2b6ee7a94..40d6000828 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionInformationApiWrapper.kt @@ -3,8 +3,6 @@ package com.flipperdevices.bridge.service.impl.delegate.connection import com.flipperdevices.bridge.api.manager.delegates.FlipperConnectionInformationApi import com.flipperdevices.bridge.api.manager.ktx.state.ConnectionState import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper -import com.flipperdevices.core.log.LogTagProvider -import com.flipperdevices.core.log.info import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -33,4 +31,4 @@ class FlipperConnectionInformationApiWrapper( } override fun getConnectedDeviceName() = flipperConnectionSource.getConnectedDeviceName() -} \ No newline at end of file +} diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt index 4b70a4e255..8acd29e918 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/DeviceChangedMacException.kt @@ -6,4 +6,4 @@ package com.flipperdevices.bridge.service.impl.model data class DeviceChangedMacException( val oldMacAddress: String, val newMacAddress: String -) : RuntimeException() \ No newline at end of file +) : RuntimeException() diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt index 3e27195557..dbb6fad000 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt @@ -13,7 +13,9 @@ data class SavedFlipperConnectionInfo private constructor( ): SavedFlipperConnectionInfo { val flipperName = if (pairSettings.deviceName.startsWith(Constants.DEVICENAME_PREFIX)) { pairSettings.deviceName - } else "${Constants.DEVICENAME_PREFIX} ${pairSettings.deviceName}" + } else { + "${Constants.DEVICENAME_PREFIX} ${pairSettings.deviceName}" + } return SavedFlipperConnectionInfo( id = pairSettings.deviceId, @@ -21,4 +23,4 @@ data class SavedFlipperConnectionInfo private constructor( ) } } -} \ No newline at end of file +} From ff43c9e9e41673824743da8c31f1bbe34d7cbc3b Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Mon, 1 Jul 2024 18:36:08 +0100 Subject: [PATCH 05/14] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e892cbabd6..a366c7da8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # 1.7.2 - In Progress +- [Feature] Fallback connection by flipper name - [FIX] Distinct fap items by id in paging sources # 1.7.1 From b7f2454a6e151134cf1a15c82c3d8fe0c52705c8 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Mon, 1 Jul 2024 18:42:52 +0100 Subject: [PATCH 06/14] Remove debug logging --- .../com/flipperdevices/bridge/api/scanner/FlipperScanner.kt | 2 +- .../com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt b/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt index c8d36104a1..9b2caba497 100644 --- a/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt +++ b/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/scanner/FlipperScanner.kt @@ -17,7 +17,7 @@ interface FlipperScanner { suspend fun findFlipperById(deviceId: String): Flow /** - * @return flipper by id + * @return flipper by name */ fun findFlipperByName(deviceName: String): Flow } diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt index dfc80b902b..2a98563ad7 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/utils/BridgeImplConfig.kt @@ -1,5 +1,5 @@ package com.flipperdevices.bridge.impl.utils object BridgeImplConfig { - const val BLE_VLOG = true + const val BLE_VLOG = false } From 7b9e8993461771ee377aec976c93c00610e9eeba Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Mon, 1 Jul 2024 19:38:46 +0100 Subject: [PATCH 07/14] Fix connection loop --- .../bridge/api/utils/Constants.kt | 6 +-- .../service/impl/FlipperServiceApiImpl.kt | 35 ++++++++----- .../delegate/FlipperSafeConnectWrapper.kt | 51 ++++++++++--------- .../connection/FlipperConnectionByName.kt | 6 +-- .../impl/model/SavedFlipperConnectionInfo.kt | 5 +- 5 files changed, 58 insertions(+), 45 deletions(-) diff --git a/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/utils/Constants.kt b/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/utils/Constants.kt index 20061d56a4..31df58f167 100644 --- a/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/utils/Constants.kt +++ b/components/bridge/api/src/main/java/com/flipperdevices/bridge/api/utils/Constants.kt @@ -2,7 +2,7 @@ package com.flipperdevices.bridge.api.utils import com.flipperdevices.core.data.SemVer import java.util.UUID -import java.util.concurrent.TimeUnit +import kotlin.time.Duration.Companion.seconds object Constants { const val DEVICENAME_PREFIX = "Flipper" @@ -76,8 +76,8 @@ object Constants { } object BLE { - private const val CONNECT_TIME_SEC = 3L - val CONNECT_TIME_MS = TimeUnit.MILLISECONDS.convert(CONNECT_TIME_SEC, TimeUnit.SECONDS) + val CONNECT_TIME = 3.seconds + val NEW_CONNECT_TIME = 15.seconds const val RECONNECT_COUNT = 1 const val RECONNECT_TIME_MS = 100L const val MAX_MTU = 512 diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt index d5277ef88f..e0cd36cec6 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt @@ -73,12 +73,9 @@ class FlipperServiceApiImpl @Inject constructor( .getConnectionStateFlow(), pairSettingsStore.data ) { connectionState, pairSetting -> - val connectionInfo = if (pairSetting.deviceId != null) { - SavedFlipperConnectionInfo.build(pairSetting) - } else { - null - } - (connectionState is ConnectionState.Disconnected) to connectionInfo + (connectionState is ConnectionState.Disconnected) to SavedFlipperConnectionInfo.build( + pairSetting + ) }.collect { (isDeviceDisconnected, connectionInfo) -> withLock(mutex, "connect") { if (unhandledExceptionApi.isBleConnectionForbiddenFlow().first()) { @@ -87,11 +84,17 @@ class FlipperServiceApiImpl @Inject constructor( if (previousDeviceId != connectionInfo?.id) { // Reconnect info { "Reconnect because device id changed" } - flipperSafeConnectWrapper.onActiveDeviceUpdate(connectionInfo) + flipperSafeConnectWrapper.onActiveDeviceUpdate( + connectionInfo, + force = true + ) previousDeviceId = connectionInfo?.id } else if (isDeviceDisconnected && !disconnectForced) { // Autoreconnect info { "Reconnect because device is disconnected, but not forced" } - flipperSafeConnectWrapper.onActiveDeviceUpdate(connectionInfo) + flipperSafeConnectWrapper.onActiveDeviceUpdate( + connectionInfo, + force = false + ) previousDeviceId = connectionInfo?.id } } @@ -107,20 +110,23 @@ class FlipperServiceApiImpl @Inject constructor( info { "Failed soft connect, because ble connection forbidden" } return@launchWithLock } - if (bleManager.isConnected() || flipperSafeConnectWrapper.isTryingConnected()) { + if (bleManager.isConnected() || flipperSafeConnectWrapper.isConnectingFlow().first()) { + info { "Skip soft connect because device already in connecting or connected stage" } return@launchWithLock } + val pairSetting = pairSettingsStore.data.first() - flipperSafeConnectWrapper.onActiveDeviceUpdate( - SavedFlipperConnectionInfo.build(pairSetting) - ) + val connectionInfo = SavedFlipperConnectionInfo.build(pairSetting) + info { "Start soft connect to $connectionInfo" } + + flipperSafeConnectWrapper.onActiveDeviceUpdate(connectionInfo, force = true) } override suspend fun disconnect(isForce: Boolean) = withLock(mutex, "disconnect") { if (isForce) { disconnectForced = true } - flipperSafeConnectWrapper.onActiveDeviceUpdate(null) + flipperSafeConnectWrapper.onActiveDeviceUpdate(null, force = true) } override suspend fun reconnect() = withLock(mutex, "reconnect") { @@ -128,7 +134,8 @@ class FlipperServiceApiImpl @Inject constructor( val pairSetting = pairSettingsStore.data.first() flipperSafeConnectWrapper.onActiveDeviceUpdate( - SavedFlipperConnectionInfo.build(pairSetting) + SavedFlipperConnectionInfo.build(pairSetting), + force = true ) } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt index b38cf51e93..5948bec653 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt @@ -46,34 +46,39 @@ class FlipperSafeConnectWrapper @Inject constructor( fun isConnectingFlow() = isConnectingMutableStateFlow.asStateFlow() suspend fun onActiveDeviceUpdate( - connectionInfo: SavedFlipperConnectionInfo? - ) = launchWithLock(mutex, scope, "onActiveDeviceUpdate") { - info { "Call cancel and join to current job" } - currentConnectingJob?.cancelAndJoin() - info { "Job canceled! Call connect again" } - currentConnectingJob = scope.launch(FlipperDispatchers.workStealingDispatcher) { - var jobCompleted = false - isConnectingMutableStateFlow.emit(true) - do { - val deviceUpdateResult = runCatching { - onActiveDeviceUpdateInternal(connectionInfo) - } - val errorOnDeviceUpdate = deviceUpdateResult.exceptionOrNull() - if (errorOnDeviceUpdate != null) { - error(errorOnDeviceUpdate) { "Unexpected error on activeDeviceUpdate" } - } - if (deviceUpdateResult.getOrNull() == true) { - jobCompleted = true + connectionInfo: SavedFlipperConnectionInfo?, + force: Boolean + ) { + launchWithLock(mutex, scope, "onActiveDeviceUpdate") { + if (force.not() && currentConnectingJob?.isActive == true) { + info { "onActiveDeviceUpdate called without force, so skip reinvalidate job" } + return@launchWithLock + } + info { "Call cancel and join to current job" } + currentConnectingJob?.cancelAndJoin() + info { "Job canceled! Call connect again" } + currentConnectingJob = scope.launch(FlipperDispatchers.workStealingDispatcher) { + var jobCompleted = false + isConnectingMutableStateFlow.emit(true) + do { + val deviceUpdateResult = runCatching { + onActiveDeviceUpdateInternal(connectionInfo) + } + val errorOnDeviceUpdate = deviceUpdateResult.exceptionOrNull() + if (errorOnDeviceUpdate != null) { + error(errorOnDeviceUpdate) { "Unexpected error on activeDeviceUpdate" } + } + if (deviceUpdateResult.getOrNull() == true) { + jobCompleted = true + } + } while (isActive && jobCompleted.not()) + if (jobCompleted) { + isConnectingMutableStateFlow.emit(false) } - } while (isActive && jobCompleted.not()) - if (jobCompleted) { - isConnectingMutableStateFlow.emit(false) } } } - fun isTryingConnected() = currentConnectingJob?.isActive ?: false - private suspend fun onActiveDeviceUpdateInternal( connectionInfo: SavedFlipperConnectionInfo? ): Boolean { diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt index d0755f00f9..1d9d6b1aa4 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt @@ -18,8 +18,6 @@ import kotlinx.coroutines.flow.toList import kotlinx.coroutines.withTimeout import javax.inject.Inject import javax.inject.Provider -import kotlin.time.DurationUnit -import kotlin.time.toDuration /** * It's a fallback if the user's MAC address has changed, but the flipper is the same. @@ -46,7 +44,7 @@ class FlipperConnectionByName @Inject constructor( val devices = scanner.findFlipperByName(connectionInfo.name).filter { it.address != connectionInfo.id - }.timeout(Constants.BLE.CONNECT_TIME_MS.toDuration(DurationUnit.MILLISECONDS)) + }.timeout(Constants.BLE.CONNECT_TIME) .catch { exception -> if (exception !is TimeoutCancellationException) { // Throw other exceptions. @@ -58,7 +56,7 @@ class FlipperConnectionByName @Inject constructor( for (device in devices) { info { "Connect to ${device.address}..." } val result = runCatching { - withTimeout(Constants.BLE.CONNECT_TIME_MS) { + withTimeout(Constants.BLE.NEW_CONNECT_TIME) { bleManager.connectToDevice(device.device) } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt index dbb6fad000..2e5dbc91d1 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt @@ -10,7 +10,10 @@ data class SavedFlipperConnectionInfo private constructor( companion object { fun build( pairSettings: PairSettings - ): SavedFlipperConnectionInfo { + ): SavedFlipperConnectionInfo? { + if (pairSettings.deviceId == null) { + return null + } val flipperName = if (pairSettings.deviceName.startsWith(Constants.DEVICENAME_PREFIX)) { pairSettings.deviceName } else { From 8244f56bebee3815b603e2dcd4b3a1a94a77e6e0 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Mon, 1 Jul 2024 19:45:59 +0100 Subject: [PATCH 08/14] Remove duplicated log from FlipperConnectionByName --- .../service/impl/delegate/connection/FlipperConnectionByName.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt index 1d9d6b1aa4..3337e59ef6 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByName.kt @@ -60,7 +60,6 @@ class FlipperConnectionByName @Inject constructor( bleManager.connectToDevice(device.device) } } - info { "Connect to ${device.address} ${if (result.isSuccess) "SUCCESS" else "FAILED"}" } if (result.isSuccess) { info { "Connect to ${device.address} SUCCESS" } From 66d455875ec5cc3153fd10d426a846ad611cc2a9 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Tue, 2 Jul 2024 14:03:53 +0100 Subject: [PATCH 09/14] Fix compile --- .../flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt | 1 - .../impl/delegate/connection/FlipperConnectionByMac.kt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt index ae54430075..8e5ca46bb9 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt @@ -101,7 +101,6 @@ class FlipperScannerImpl @Inject constructor( } scanner.scanFlow(provideSettings(), provideFilterForDefaultScan()) - .filter { it.device.name == deviceName } .map { DiscoveredBluetoothDevice(it) } .collect { emit(it) diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt index a7cee97081..f5d92b3ef7 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/connection/FlipperConnectionByMac.kt @@ -33,7 +33,7 @@ class FlipperConnectionByMac @Inject constructor( if (device == null) { device = runCatching { - withTimeout(Constants.BLE.CONNECT_TIME_MS) { + withTimeout(Constants.BLE.CONNECT_TIME) { scanner.findFlipperById(connectionInfo.id).first() }.device }.getOrNull() @@ -43,7 +43,7 @@ class FlipperConnectionByMac @Inject constructor( } val connectWithTimeout = runCatching { - withTimeout(Constants.BLE.CONNECT_TIME_MS) { + withTimeout(Constants.BLE.CONNECT_TIME) { bleManager.connectToDevice(device) } } From 2b87e09d12830ccd055d5e27eab290862b5be50c Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Tue, 2 Jul 2024 17:08:59 +0100 Subject: [PATCH 10/14] Fix state infinite loop --- .../bridge/service/impl/FlipperServiceApiImpl.kt | 2 +- .../bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt | 4 ++-- .../bridge/service/impl/model/SavedFlipperConnectionInfo.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt index e0cd36cec6..db9112d8db 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/FlipperServiceApiImpl.kt @@ -89,7 +89,7 @@ class FlipperServiceApiImpl @Inject constructor( force = true ) previousDeviceId = connectionInfo?.id - } else if (isDeviceDisconnected && !disconnectForced) { // Autoreconnect + } else if (isDeviceDisconnected && !disconnectForced && connectionInfo != null) { // Autoreconnect info { "Reconnect because device is disconnected, but not forced" } flipperSafeConnectWrapper.onActiveDeviceUpdate( connectionInfo, diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt index 5948bec653..9c984479b5 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt @@ -59,7 +59,6 @@ class FlipperSafeConnectWrapper @Inject constructor( info { "Job canceled! Call connect again" } currentConnectingJob = scope.launch(FlipperDispatchers.workStealingDispatcher) { var jobCompleted = false - isConnectingMutableStateFlow.emit(true) do { val deviceUpdateResult = runCatching { onActiveDeviceUpdateInternal(connectionInfo) @@ -79,7 +78,7 @@ class FlipperSafeConnectWrapper @Inject constructor( } } - private suspend fun onActiveDeviceUpdateInternal( + private suspend fun onActiveDeviceUpdateInternal( connectionInfo: SavedFlipperConnectionInfo? ): Boolean { if (connectionInfo == null || connectionInfo.id.isBlank()) { @@ -87,6 +86,7 @@ class FlipperSafeConnectWrapper @Inject constructor( connectDelegate.disconnect() return true } + isConnectingMutableStateFlow.emit(true) try { return connectDelegate.reconnect(connectionInfo) diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt index 2e5dbc91d1..0928a789bb 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/model/SavedFlipperConnectionInfo.kt @@ -11,7 +11,7 @@ data class SavedFlipperConnectionInfo private constructor( fun build( pairSettings: PairSettings ): SavedFlipperConnectionInfo? { - if (pairSettings.deviceId == null) { + if (pairSettings.deviceId.isNullOrBlank()) { return null } val flipperName = if (pairSettings.deviceName.startsWith(Constants.DEVICENAME_PREFIX)) { From a65475003f4293ec15a5fc6962629d29df16650c Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Tue, 2 Jul 2024 17:23:10 +0100 Subject: [PATCH 11/14] Add remove bond for old mac address --- .../delegate/FlipperSafeConnectWrapper.kt | 22 +++++++-- .../impl/di/FlipperBleServiceComponentImpl.kt | 10 +++- .../service/impl/utils/RemoveBondHelper.kt | 46 +++++++++++++++++++ 3 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt index 9c984479b5..f0fdde5bd0 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/delegate/FlipperSafeConnectWrapper.kt @@ -5,6 +5,7 @@ import com.flipperdevices.bridge.api.error.FlipperBleServiceError import com.flipperdevices.bridge.api.error.FlipperServiceErrorListener import com.flipperdevices.bridge.service.impl.model.DeviceChangedMacException import com.flipperdevices.bridge.service.impl.model.SavedFlipperConnectionInfo +import com.flipperdevices.bridge.service.impl.utils.RemoveBondHelper import com.flipperdevices.core.di.provideDelegate import com.flipperdevices.core.ktx.jre.FlipperDispatchers import com.flipperdevices.core.ktx.jre.launchWithLock @@ -28,7 +29,8 @@ class FlipperSafeConnectWrapper @Inject constructor( scopeProvider: Provider, serviceErrorListenerProvider: Provider, connectDelegateProvider: Provider, - dataStoreProvider: Provider> + dataStoreProvider: Provider>, + removeBondHelperProvider: Provider ) : LogTagProvider { override val TAG = "FlipperSafeConnectWrapper" @@ -42,6 +44,7 @@ class FlipperSafeConnectWrapper @Inject constructor( private val serviceErrorListener by serviceErrorListenerProvider private val connectDelegate by connectDelegateProvider private val dataStore by dataStoreProvider + private val removeBondHelper by removeBondHelperProvider fun isConnectingFlow() = isConnectingMutableStateFlow.asStateFlow() @@ -78,7 +81,7 @@ class FlipperSafeConnectWrapper @Inject constructor( } } - private suspend fun onActiveDeviceUpdateInternal( + private suspend fun onActiveDeviceUpdateInternal( connectionInfo: SavedFlipperConnectionInfo? ): Boolean { if (connectionInfo == null || connectionInfo.id.isBlank()) { @@ -101,8 +104,9 @@ class FlipperSafeConnectWrapper @Inject constructor( return true } catch (changedMac: DeviceChangedMacException) { - error(changedMac) { "Mac changed from ${changedMac.oldMacAddress} to ${changedMac.newMacAddress}" } - dataStore.updateData { data -> + info { "Mac changed from ${changedMac.oldMacAddress} to ${changedMac.newMacAddress}" } + + val newAddress = dataStore.updateData { data -> if (data.deviceId == changedMac.oldMacAddress) { return@updateData data.toBuilder() .setDeviceId(changedMac.newMacAddress) @@ -113,6 +117,16 @@ class FlipperSafeConnectWrapper @Inject constructor( } } + if (newAddress.deviceId == changedMac.newMacAddress && + changedMac.newMacAddress != changedMac.oldMacAddress + ) { + if (removeBondHelper.removeBond(changedMac.oldMacAddress).not()) { + error { "Failed remove bond for ${changedMac.oldMacAddress}" } + } else { + info { "Remove bond for ${changedMac.oldMacAddress}" } + } + } + return false } } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt index b001bc1a9a..f826918e86 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/di/FlipperBleServiceComponentImpl.kt @@ -14,6 +14,7 @@ import com.flipperdevices.bridge.service.impl.delegate.FlipperSafeConnectWrapper import com.flipperdevices.bridge.service.impl.delegate.FlipperServiceConnectDelegate import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionByMac import com.flipperdevices.bridge.service.impl.delegate.connection.FlipperConnectionByName +import com.flipperdevices.bridge.service.impl.utils.RemoveBondHelper import kotlinx.coroutines.CoroutineScope import javax.inject.Provider @@ -101,12 +102,19 @@ class FlipperBleServiceComponentImpl( ) } + private val removeBondHelper by lazy { + RemoveBondHelper( + adapterProvider = { bluetoothAdapter } + ) + } + private val flipperSafeConnectWrapper by lazy { FlipperSafeConnectWrapper( scopeProvider = { scope }, serviceErrorListenerProvider = { serviceErrorListener }, connectDelegateProvider = { flipperServiceConnectDelegate }, - dataStoreProvider = { pairSettingsStore } + dataStoreProvider = { pairSettingsStore }, + removeBondHelperProvider = { removeBondHelper } ) } diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt new file mode 100644 index 0000000000..b0cf6a3790 --- /dev/null +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt @@ -0,0 +1,46 @@ +package com.flipperdevices.bridge.service.impl.utils + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import com.flipperdevices.core.di.provideDelegate +import com.flipperdevices.core.log.LogTagProvider +import com.flipperdevices.core.log.error +import com.flipperdevices.core.log.info +import javax.inject.Inject +import javax.inject.Provider + +class RemoveBondHelper @Inject constructor( + adapterProvider: Provider +) : LogTagProvider { + override val TAG = "RemoveBondHelper" + + private val adapter by adapterProvider + + fun removeBond(id: String): Boolean { + info { "Request remove bond for $id" } + val pairedDevices = adapter.bondedDevices.filter { it.address == id } + if (pairedDevices.isEmpty()) { + info { "Return false because no any paired device with id $id" } + return false // Not found any paired devices + } + info { "Found ${pairedDevices.size} paired devices with id $id" } + + var isSuccess = false + for (device in pairedDevices) { + val result = removeBond(device).onFailure { + error(it) { "Failed remove bond for device with id $id and device is $device" } + } + if (result.getOrNull() == true) { + info { "Remove bond successful for $device" } + isSuccess = true + } + } + return isSuccess // At lease one bond was deleted + } + + private fun removeBond(device: BluetoothDevice): Result = runCatching { + info { "Request remove bond for $device" } + val removeBond = device.javaClass.getMethod("removeBond") + return@runCatching removeBond.invoke(device) as Boolean + } +} From 640a02e6b1813a9ff2368f3acde339323bc2979c Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Wed, 3 Jul 2024 11:23:21 +0100 Subject: [PATCH 12/14] Fix filter by name --- .../bridge/impl/scanner/FlipperScannerImpl.kt | 1 + .../impl/scanner/FlipperScannerImplTest.kt | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt index 8e5ca46bb9..ae54430075 100644 --- a/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt +++ b/components/bridge/impl/src/main/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImpl.kt @@ -101,6 +101,7 @@ class FlipperScannerImpl @Inject constructor( } scanner.scanFlow(provideSettings(), provideFilterForDefaultScan()) + .filter { it.device.name == deviceName } .map { DiscoveredBluetoothDevice(it) } .collect { emit(it) diff --git a/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt b/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt index 2093e90544..c7ee9b67e7 100644 --- a/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt +++ b/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt @@ -182,6 +182,49 @@ class FlipperScannerImplTest { Assert.assertEquals(bluetoothDevice, foundDevice!!.device) } + @Test + fun `filter device by name filter`() = runTest { + val bluetoothDevice = mockk { + every { name } returns "Flipper Dumper" + every { address } returns "" + } + + val sr = ScanResult(bluetoothDevice, 0, 0, 0, 0, 0, 0, 0, null, 0L) + + every { scanner.scanFlow(any(), any()) } returns flowOf(sr) + every { bluetoothAdapter.bondedDevices } returns emptySet() + + val flipperDevices = flipperScanner.findFlipperByName("Flipper Dumper").first() + + val foundDevice = flipperDevices + Assert.assertNotNull(foundDevice) + Assert.assertEquals(bluetoothDevice, foundDevice.device) + } + + @Test + fun `filter device by name filter and return only this one`() = runTest { + val bluetoothDevice = mockk { + every { name } returns "Flipper Dumper" + every { address } returns "" + } + val wrongBluetoothDevice = mockk { + every { name } returns "Flipper Wrong" + every { address } returns "" + } + + val sr = ScanResult(bluetoothDevice, 0, 0, 0, 0, 0, 0, 0, null, 0L) + val srWrong = ScanResult(wrongBluetoothDevice, 0, 0, 0, 0, 0, 0, 0, null, 0L) + + every { scanner.scanFlow(any(), any()) } returns flowOf(sr,srWrong) + every { bluetoothAdapter.bondedDevices } returns emptySet() + + val flipperDevices = flipperScanner.findFlipperByName("Flipper Dumper").toList() + Assert.assertEquals(flipperDevices.size, 1) + val foundDevice = flipperDevices.first() + Assert.assertNotNull(foundDevice) + Assert.assertEquals(bluetoothDevice, foundDevice.device) + } + @Test fun `block device with incorrect name`() = runTest { val bluetoothDevice = mockk { From f16fec7f226ccab77a82e8b4c33117da6185d091 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Wed, 3 Jul 2024 11:56:33 +0100 Subject: [PATCH 13/14] Fix android lint --- .../bridge/service/impl/utils/RemoveBondHelper.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt index b0cf6a3790..4e63338bd5 100644 --- a/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt +++ b/components/bridge/service/impl/src/main/java/com/flipperdevices/bridge/service/impl/utils/RemoveBondHelper.kt @@ -1,5 +1,6 @@ package com.flipperdevices.bridge.service.impl.utils +import android.annotation.SuppressLint import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import com.flipperdevices.core.di.provideDelegate @@ -16,6 +17,7 @@ class RemoveBondHelper @Inject constructor( private val adapter by adapterProvider + @SuppressLint("MissingPermission") fun removeBond(id: String): Boolean { info { "Request remove bond for $id" } val pairedDevices = adapter.bondedDevices.filter { it.address == id } From a57012514cc610e051a73bfc39a25998455ef6db Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Wed, 3 Jul 2024 14:47:09 +0100 Subject: [PATCH 14/14] Fix lint --- .../bridge/impl/scanner/FlipperScannerImplTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt b/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt index c7ee9b67e7..262fa65917 100644 --- a/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt +++ b/components/bridge/impl/src/test/java/com/flipperdevices/bridge/impl/scanner/FlipperScannerImplTest.kt @@ -215,7 +215,7 @@ class FlipperScannerImplTest { val sr = ScanResult(bluetoothDevice, 0, 0, 0, 0, 0, 0, 0, null, 0L) val srWrong = ScanResult(wrongBluetoothDevice, 0, 0, 0, 0, 0, 0, 0, null, 0L) - every { scanner.scanFlow(any(), any()) } returns flowOf(sr,srWrong) + every { scanner.scanFlow(any(), any()) } returns flowOf(sr, srWrong) every { bluetoothAdapter.bondedDevices } returns emptySet() val flipperDevices = flipperScanner.findFlipperByName("Flipper Dumper").toList()