Antoine163
Antoine163

Reputation: 11

How to Bluetooth LE background connection?

Sorry for my bad English.

Context

My main goal is to build a electronic lock, the key would be a phone connected in BLE. The proximity of the phone must unlock the lock.

What I want to do

A application who this connect automatically at a BLE devices when the phone is a proximity. So,the application need run in background. But I am a begin in android/Kotlin. I read the official trainings of google and I was thinking using WorkManager.

The problems

BLE connection

I don't have a problem about the BLE connection. For this, I use BluetoothDevice.connectGatt() (view doc) with autoConnect parameter at true. The callback in correctly called when a device is connected or the connection is lost BluetoothGattCallback.onConnectionStateChange() (view doc)

WorkManager behavior

To try to understand the behavior of WorkManager, I added 1min delay Thread.sleep(60000) in doWork() function of a Worker. And here is what I noticed:

  1. When Worker is running (last minute in my case) and WorkManager.cancelAllWorkByTag() is called:
    • The onStopped() of the Worker is called but the worker continue to run.
  2. When Worker is running and the BLE device is connected and the application is closed by de the user:
    • The Worker is stopped
    • The BLE device lost connection
    • The Worker is resurrect after a minute and the BLE callback (connection/disconnection) work again, even after end of run of Worker.

My Worker code (not very clean)

class BleWorker(
    appContext: Context,
    workerParams: WorkerParameters
) : Worker(appContext, workerParams) {

    private val bluetoothManager: BluetoothManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    private var bleDevice: BluetoothDevice? = null
    private var bluetoothGatt: BluetoothGatt? = null


    private val SERV_UUID = UUID.fromString("44707b20-3459-11ee-aea4-0800200c9a66")
    private val CHAR_UUID_UNLOCK = UUID.fromString("44707b21-3459-11ee-aea4-0800200c9a66")
    private val CHAR_UUID_STATE = UUID.fromString("44707b22-3459-11ee-aea4-0800200c9a66")

    private var service: BluetoothGattService? = null
    private var charUnlock: BluetoothGattCharacteristic? = null
    private var charState: BluetoothGattCharacteristic? = null

    init {
        Log.i("learnBle","BleWorker ($id:$tags) init" )
    }

    protected fun finalize() {
        Log.i("learnBle","BleWorker ($id) finalize" )
    }

    override fun onStopped() {
        Log.i("learnBle","BleWorker ($id) onStopped" )
        super.onStopped()
    }


    override fun doWork(): Result {
        var result: Result
        try {
            Log.i("learnBle","BleWorker ($id) doWork started" )

            bleDevice = bluetoothManager.adapter.bondedDevices.find {
                it.address == BLE_ADD
            }

            if (bleDevice != null)
            {
                bluetoothGatt = bleDevice!!.connectGatt(
                    applicationContext,
                    true,
                    gattCallback_auto2)
            }
            else
            {
                Log.i("learnBle","BleWorker ($id) doWork no device bonded !" )
                Result.failure()
            }

            Thread.sleep(60000)
            Log.i("learnBle","BleWorker ($id) doWork ended" )
            result = Result.success()
        } catch (e: Exception)
        {
            Log.i("learnBle","BleWorker ($id) doWork ended with FAILURE" )
            result = Result.failure()
        }

        return result
    }


    private val gattCallback_auto2: BluetoothGattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
            super.onConnectionStateChange(gatt, status, newState)

            if (    status == BluetoothGatt.GATT_SUCCESS
                &&  newState == BluetoothGatt.STATE_CONNECTED) {

                val ret = gatt!!.discoverServices()
                Log.i("learnBle","BleWorker:Callback ($id) auto discover services starting:$ret" )
            }
            else {
                Log.i("learnBle","BleWorker:Callback ($id) auto Connexion failed !" )
            }
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
            super.onServicesDiscovered(gatt, status)

            // Note: Here gatt == bluetoothGatt
            val services = gatt?.services

            service = gatt?.getService(SERV_UUID)
            charUnlock = service?.getCharacteristic(CHAR_UUID_UNLOCK)
            charState = service?.getCharacteristic(CHAR_UUID_STATE)

            // Unlock
            if (charUnlock != null) {
                val value = byteArrayOf(0x01)
                gatt?.writeCharacteristic(
                    charUnlock!!,
                    value,
                    BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)

                Log.i("learnBle","BleWorker:Callback ($id) unloked !" )
            }
        }
    }
}

Can you help me ?

Thank you for your help.

Upvotes: 1

Views: 548

Answers (1)

mxkmn
mxkmn

Reputation: 543

Android and iOS can trigger app functions when the system detects BLE advertisements. Check android library for this: Android Beacon Library

And yes, you don't need to use background services or smth like that 😎

Upvotes: 0

Related Questions