Uncaught exception thrown by finalizer for Bluetooth Reconnection in Android - java.io.IOException: socket not created

34 views Asked by At

Thanks in advance for helping. I am trying to work with a Serial Bluetooth connection where I am connecting and communicating to the IOT device serially over Bluetooth. So the device has On/Off switch which can basically close the Bluetooth connection when turned off the device. So I am trying to achieve that when the device is turned ON the app should auto-detect and auto-connect with the device all the time. So far I am able to achieve that by the code below.

Unfortunately, until the following error is encountered. Once this error is encountered the app keeps closing the connection all the time after connection requested with IOT device.

Error Log:

2024-01-03 16:01:00.535  8401-8416  System                  com.mybluetooth.iot           E  Uncaught exception thrown by finalizer
2024-01-03 16:01:00.541  8401-8416  System                  com.mybluetooth.iot           E  java.io.IOException: socket not created
                                                                                                        at android.net.LocalSocketImpl.shutdownInput(LocalSocketImpl.java:371)
                                                                                                        at android.net.LocalSocket.shutdownInput(LocalSocket.java:238)
                                                                                                        at android.bluetooth.BluetoothSocket.close(BluetoothSocket.java:780)
                                                                                                        at android.bluetooth.BluetoothSocket.finalize(BluetoothSocket.java:371)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
                                                                                                        at java.lang.Daemons$Daemon.run(Daemons.java:145)
                                                                                                        at java.lang.Thread.run(Thread.java:1012)
2024-01-03 16:01:00.541  8401-8416  System                  com.mybluetooth.iot           E  Uncaught exception thrown by finalizer
2024-01-03 16:01:00.541  8401-8416  System                  com.mybluetooth.iot           E  java.io.IOException: socket not created
                                                                                                        at android.net.LocalSocketImpl.shutdownInput(LocalSocketImpl.java:371)
                                                                                                        at android.net.LocalSocket.shutdownInput(LocalSocket.java:238)
                                                                                                        at android.bluetooth.BluetoothSocket.close(BluetoothSocket.java:780)
                                                                                                        at android.bluetooth.BluetoothSocket.finalize(BluetoothSocket.java:371)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
                                                                                                        at java.lang.Daemons$Daemon.run(Daemons.java:145)
                                                                                                        at java.lang.Thread.run(Thread.java:1012)
2024-01-03 16:01:00.542  8401-8416  System                  com.mybluetooth.iot           E  Uncaught exception thrown by finalizer
2024-01-03 16:01:00.542  8401-8416  System                  com.mybluetooth.iot           E  java.io.IOException: socket not created
                                                                                                        at android.net.LocalSocketImpl.shutdownInput(LocalSocketImpl.java:371)
                                                                                                        at android.net.LocalSocket.shutdownInput(LocalSocket.java:238)
                                                                                                        at android.bluetooth.BluetoothSocket.close(BluetoothSocket.java:780)
                                                                                                        at android.bluetooth.BluetoothSocket.finalize(BluetoothSocket.java:371)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
                                                                                                        at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
                                                                                                        at java.lang.Daemons$Daemon.run(Daemons.java:145)
                                                                                                        at java.lang.Thread.run(Thread.java:1012)

**Error log for making Re-connect attempt after turning ON the IOT device**

2024-01-03 17:21:33.547 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.572 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.582 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.591 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.600 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.606 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.613 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.626 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.634 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.643 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:33.655 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_DISCONNECTED 
2024-01-03 17:21:34.273 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.282 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.282 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.282 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.282 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.283 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.284 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.284 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.284 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.285 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.285 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL_CONNECTED 
2024-01-03 17:21:34.540 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: Disconnected State
2024-01-03 17:21:34.560 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL Event 
2024-01-03 17:21:34.585 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: Disconnected State
2024-01-03 17:21:34.611 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: This is ACL Event 
2024-01-03 17:21:34.612 18742-18742 BaseActivity            com.mybluetooth.iot           E  Base Activity: Connected
2024-01-03 17:21:34.612 18742-18742 DashboardA...........Kt com.mybluetooth.iot           E  :: This is for re-connected
2024-01-03 17:21:34.636 18742-18777 BT IO                   com.mybluetooth.iot           E  readByteArrayStream: Connection Disconnected from finally
2024-01-03 17:21:34.636 18742-18777 BT IO                   com.mybluetooth.iot           E  All Connections closed

BluetoothServiceProviderIO.kt

class BluetoothServiceProviderIO(val bluetoothSocket: BluetoothSocket?) {

    private val inputStream: InputStream? = try {
        bluetoothSocket?.inputStream
    } catch (e: IOException) {
        Log.e("BT IO", "Could not Open Input Stream")
        e.printStackTrace()
        throw SocketStreamException("Couldn't open InputStream socket")
    }

    private val outputStream: OutputStream? = try {
        bluetoothSocket?.outputStream
    } catch (e: IOException) {
        Log.e("BT IO", "Could not Open OutputStream")
        e.printStackTrace()
        throw SocketStreamException("Couldn't open OutputStream socket")
    }

    /**
     * Send array of bytes to bluetooth output stream.
     *
     * @param bytes data to send
     * @return true if success, false if there was error occurred or disconnected
     */
    fun send(data: ModelSendData): Boolean {
        if (bluetoothSocket?.isConnected != true) return false

        return try {
            outputStream?.write(data.data)
            outputStream?.flush()
            true
        } catch (e: IOException) {
            e.printStackTrace()
            false
        }
    }

    @ExperimentalCoroutinesApi
    fun readByteArrayStream(
        delayMillis: Long = 10,
        bufferCapacity: Int = 32
                           ): Flow<ByteArray> = channelFlow {

        if (inputStream == null) {
            Log.e("BT IO", "input Stream is null")
            throw NullPointerException("inputStream is null. Perhaps bluetoothSocket is also null")
        }

        val buffer = ByteArray(bufferCapacity)
        while (isActive) {
            try {
                if (inputStream.available() > 0) {
                    val numBytes = inputStream.read(buffer)
                    val readBytes = ByteArray(size = numBytes)
                    for (i in 0 until numBytes) {
                        readBytes[i] = buffer[i]
                    }
                    delay(delayMillis)
                    this.trySend(readBytes).isSuccess
                }
            } catch (e: IOException) {
                Log.e("BT IO", "readByteArrayStream: Connection Disconnected from Catch")
                closeConnections()
                error("Couldn't read bytes from flow. Disconnected")
            } finally {
                if (bluetoothSocket?.isConnected != true) {
                    Log.e("BT IO", "readByteArrayStream: Connection Disconnected from finally")
                    closeConnections()
                    break
                }
            }
        }
    }.flowOn(Dispatchers.IO)

    /**
     * Close the streams and socket connection.
     */
    fun closeConnections() {
        try {
            inputStream?.close()
            outputStream?.close()
            bluetoothSocket?.close()
            Log.e("BT IO", "All Connections closed")
        } catch (e: Exception) {
            Log.e("BT IO", "Connections close failed ${e.printStackTrace()}")
        }
    }
}

BluetoothServiceProvider.kt

@SuppressLint("MissingPermission")
class BluetoothServiceProvider(private val context: Context) {

    val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager

    @Volatile
    var currentBluetoothSocket: BluetoothSocket? = null
    private var bluetoothServiceProviderIO: BluetoothServiceProviderIO? = null

    @SuppressLint("HardwareIds")
    fun isBluetoothAvailable() =
            !(bluetoothManager.adapter == null || TextUtils.isEmpty(bluetoothManager.adapter.address))

    
    fun isBluetoothEnabled() = bluetoothManager.adapter.isEnabled

    
    fun getIO(bluetoothSocket: BluetoothSocket): BluetoothServiceProviderIO {

        if (bluetoothServiceProviderIO?.bluetoothSocket == bluetoothSocket) {
            return bluetoothServiceProviderIO as BluetoothServiceProviderIO
        }

        currentBluetoothSocket = bluetoothSocket
        bluetoothServiceProviderIO = BluetoothServiceProviderIO(bluetoothSocket)
        return bluetoothServiceProviderIO as BluetoothServiceProviderIO
    }

    
    fun getIO(): BluetoothServiceProviderIO {
        return getIO(currentBluetoothSocket!!)
    }

    @SuppressLint("MissingPermission")
    fun enable() = bluetoothManager.adapter.isEnabled

    fun disable() = !bluetoothManager.adapter.isEnabled

    @SuppressLint("MissingPermission")
    fun bondedDevices(): List<BluetoothDevice> = bluetoothManager.adapter.bondedDevices.toList()

    fun startDiscovery() = bluetoothManager.adapter.startDiscovery()
    
    fun isDiscovering() = bluetoothManager.adapter.isDiscovering

    fun cancelDiscovery() = if (bluetoothManager.adapter.isDiscovering) {
        bluetoothManager.adapter.cancelDiscovery()
        true
    } else {
        false
    }

    @ExperimentalCoroutinesApi
    fun discoverDevices(): Flow<BluetoothDeviceWrapper> = callbackFlow<BluetoothDeviceWrapper> {
        val filter = IntentFilter(BluetoothDevice.ACTION_FOUND).apply {
            addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
            addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
        }
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                when (intent?.action) {
                    BluetoothDevice.ACTION_FOUND -> {
                        val device =
                                intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) as BluetoothDevice?
                        val rssi =
                                intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, MIN_VALUE).toInt()
                        device?.let { trySend(BluetoothDeviceWrapper(it, rssi)).isSuccess }
                    }

                    BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
                    }

                    BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                    }
                }
            }
        }
        context.registerReceiver(receiver, filter)
        awaitClose {
            context.unregisterReceiver(receiver)
        }
    }.flowOn(Dispatchers.IO)

    @ExperimentalCoroutinesApi
    fun checkDiscoveryStatus(): Flow<String> = callbackFlow<String> {
        val filter = IntentFilter(BluetoothDevice.ACTION_FOUND).apply {
            addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
            addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
        }
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                when (intent?.action) {
                    BluetoothDevice.ACTION_FOUND -> {
                    }

                    BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
                        trySend(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
                    }

                    BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                        trySend(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
                    }
                }
            }
        }
        context.registerReceiver(receiver, filter)
        awaitClose {
            context.unregisterReceiver(receiver)
        }
    }.flowOn(Dispatchers.IO)

    @ExperimentalCoroutinesApi
    fun getDeviceBondState(): Flow<Int> = callbackFlow<Int> {
        val filter = IntentFilter().apply {
            addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
        }

        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                if (intent?.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
                    val mDevice: BluetoothDevice =
                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)!!
                    //3 cases:
                    //case1: bonded already
                    if (mDevice.bondState == BluetoothDevice.BOND_BONDED) {
                        Log.e("BluetoothServiceProvider", "BroadcastReceiver: BOND_BONDED.")
                    }
                    //case2: creating a bond
                    if (mDevice.bondState == BluetoothDevice.BOND_BONDING) {
                        Log.e("BluetoothServiceProvider", "BroadcastReceiver: BOND_BONDING.")
                    }
                    //case3: breaking a bond
                    if (mDevice.bondState == BluetoothDevice.BOND_NONE) {
                        Log.e("BluetoothServiceProvider", "BroadcastReceiver: BOND_NONE.")
                    }
                    trySend(mDevice.bondState).isSuccess
                }
            }
        }
        context.registerReceiver(receiver, filter)
        awaitClose {
            context.unregisterReceiver(receiver)
        }
    }.flowOn(Dispatchers.IO)

    fun connectToDevice(bluetoothDevice: BluetoothDevice,
                        uuid: UUID,
                        secure: Boolean = true): Deferred<BluetoothSocket> =
            CoroutineScope(Dispatchers.IO).async {
                val bluetoothSocket =
                        if (secure) bluetoothDevice.createRfcommSocketToServiceRecord(uuid)
                        else bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid)
                bluetoothSocket.apply {
                    currentBluetoothSocket = this@apply
                    connect()
                }
            }

    @Throws(java.lang.Exception::class)
    fun createBond(btDevice: BluetoothDevice?): Boolean {
        val btClass = Class.forName("android.bluetooth.BluetoothDevice")
        val createBondMethod = btClass.getMethod("createBond")
        return createBondMethod.invoke(btDevice) as Boolean
    }

    @ExperimentalCoroutinesApi
    fun aclEvents(): Flow<AclEvent> = callbackFlow<AclEvent> {
        val filter = IntentFilter().apply {
            addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
            addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
            addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED)
        }
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                intent?.let {
                    val device =
                            it.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) as? BluetoothDevice
                    trySend(AclEvent(it.action ?: "", device)).isSuccess
                }
            }
        }
        context.registerReceiver(receiver, filter)
        awaitClose {
            context.unregisterReceiver(receiver)
        }
    }.flowOn(Dispatchers.IO)

    fun closeConnections() = bluetoothServiceProviderIO?.closeConnections()
}

BTConnect.kt

class BTConnect(private val bluetoothServiceProvider: BluetoothServiceProvider) {

    private val uuid: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")

    @SuppressLint("MissingPermission")
    fun connect(device: BluetoothDevice) = flow {
        var state: BtConnection? = null
        try {
            bluetoothServiceProvider.connectToDevice(device, uuid, true).await()
            bluetoothServiceProvider.currentBluetoothSocket.apply {
                state = if (bluetoothServiceProvider.currentBluetoothSocket!!.isConnected) {
                    BtConnection.BtConnectedState(socket = bluetoothServiceProvider.currentBluetoothSocket!!)
                } else {
                    BtConnection.BtDisconnectedState
                }
            }
        } catch (e: IOException) {
            state = BtConnection.BtDisconnectedState
        } catch (e: Exception) {
            state = BtConnection.BtDisconnectedState
            Log.e("BT Connect", "connect: Failed other than IO Exception $e")
        }
        emit(state)
    }
}

BaseActivity.kt

@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
class BaseActivity : AppCompatActivity() {
    
    var connectionState = MutableLiveData<BtConnection>()
    private var progressDialog: CustomDialog? = null    
    lateinit var connectionStatus: Job

    @Inject
    lateinit var btConnect: BTConnect

    @Inject
    lateinit var bluetoothServiceProvider: BluetoothServiceProvider

    @Inject
    lateinit var preferenceHelper: PreferenceHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        setContentView(layoutResId)
        window.insetsController?.hide(WindowInsets.Type.statusBars())

        MyApplication.component.inject(this)
    }

    @SuppressLint("MissingPermission")
    override fun onResume() {
        isScreenVisible = true
        super.onResume()
    }

    @SuppressLint("MissingPermission", "SetTextI18n")
    open fun checkForBondedDevices(): BluetoothDevice? {
        var device: BluetoothDevice? = null
        if (bluetoothServiceProvider.bondedDevices().isEmpty()) {
            device = null
        } else {
            for (i in bluetoothServiceProvider.bondedDevices()) {
                if ((i.name == preferenceHelper.deviceName) && (i.address == preferenceHelper.deviceAddress)) {
                    device = i
                }
            }
        }
        return device
    }

    open fun checkSendStatus(device: BluetoothDevice) {
        lifecycleScope.launch {
            if (isDeviceConnected) isDeviceConnected = false
            withContext(Dispatchers.Main) {
                while (!isDeviceConnected && isScreenVisible) {
                    btConnect.connect(device).safeCollect {
                        delay(200)
                        if (it != null) {
                            checkBtConnectionState(it)
                        }
                        return@safeCollect
                    }
                }
            }
        }
    }

    @SuppressLint("MissingPermission")
    open suspend fun checkBtConnectionState(btConnection: BtConnection) {
        connectionState.postValue(btConnection)
        when (btConnection) {
            is BtConnection.BtConnectingLoadingState -> {
                isDeviceConnected = false
                showLog("Connecting Loading State")
            }

            is BtConnection.BtConnectedState -> {
                isDeviceConnected = true
                showLog("Connected")
                if (!firstTimeNotConnectCheck) {
                    onDeviceConnected()
                } else {
                    onDeviceReconnected()
                }
                startCommunicationWithDevice()
            }

            is BtConnection.BtErrorConnectingState -> {
                isDeviceConnected = false
                showLog("Connecting State")
            }

            is BtConnection.BtDisconnectedState -> {
                isDeviceConnected = false
                showLog("Disconnected State")
                onDeviceDisconnected()
            }
        }
    }

    open fun checkConnectionStateOfPairedDevice(device: BluetoothDevice) {
        connectionStatus = lifecycleScope.launch {
            withContext(Dispatchers.Main) {
                bluetoothServiceProvider.aclEvents().safeCollect {
                    when (it.action) {
                        BluetoothDevice.ACTION_ACL_CONNECTED -> {
                            isDeviceConnected = true
                        }

                        BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
                            isDeviceConnected = false
                            checkSendStatus(checkForBondedDevices()!!)
                        }
                    }
                }
            }
        }
    }

    @SuppressLint("MissingPermission")
    open suspend fun startCommunicationWithDevice() {
        bluetoothServiceProvider.readByteArrayStream().safeCollect {
            if (it.isNotEmpty()) {
                processReceivedBytesForResponse(it)
            } else {
                showLog("Data not received")
            }
        }
    }

    open fun processReceivedBytesForResponse(byteArray: ByteArray) {
        try {
            if (byteArray.isNotEmpty()) {
                // Process data
            }
        } catch (e: Exception) {
            showLog("From Processed Received Data: $e")
        }
    }

    override fun onPause() {
        bluetoothServiceProvider.closeConnections()
        isScreenVisible = false
        super.onPause()
    }

    private fun showLog(s: String) {
        Log.e(tag, "Base Activity: $s")
    }


    open fun showToast(msg: String) {
        if (msg.isNotEmpty()) Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }

    open fun showProgressDialog() {
        progressDialog = CustomDialog(this)
        progressDialog!!.setMessage("Please wait...")
        progressDialog!!.setProgressStyle(ProgressDialog.STYLE_SPINNER)
        progressDialog!!.isIndeterminate = true
        progressDialog!!.setCancelable(false)
        progressDialog!!.show()
    }

    open fun hideProgressDialog() {
        if (progressDialog != null) {
            progressDialog!!.dismiss()
        }
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) {
            hideSystemUI()
        }
    }

    abstract fun onDeviceConnected()
    abstract fun onDeviceReconnected()
    abstract fun onDeviceDisconnected()
}

This is complete implementation for establishing and re-establishing the connection after device is restarted/switch ON.

Appreciate for your time. Thank you.

This is one my refernce code for developing this codebase. https://github.com/ThanosFisherman/BlueFlow

0

There are 0 answers