I'm developing a flutter application (fairly new to this).
My app need to connect to a BLE device and get data when it is close by. As I found Android Companion Device Manage seems to suit my requirement. BLE Hardware which I'm supposed to work is still under development(firmware) and I'm trying to test above using available hardware(a heart rate censor) without a success.
I have a couple of questions.
1. Can I use any BLE device as a Companion Device? or
Does device firmware need to support it?
2. Should I must use secure connection (enter pin) using device.createBond()? or
Can I use Just Work using device.connectGatt()?
3. Assuming if this work in Android, is there a Companion Device equivalent in iOS?
After going through the official document https://developer.android.com/guide/topics/connectivity/companion-device-pairing
and How to pair Bluetooth device programmatically Android below is my implementation.
public final var SELECT_DEVICE_REQUEST_CODE:Int = 12
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pairingRequestFilter = IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
pairingRequestFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST)
registerReceiver(pairingReceiver, pairingRequestFilter)
// To skip filters based on names and supported feature flags (UUIDs),
// omit calls to setNamePattern() and addServiceUuid()
// respectively, as shown in the following Bluetooth example.
val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder()
.setNamePattern(Pattern.compile("two"))
.build()
// The argument provided in setSingleDevice() determines whether a single
// device name or a list of them appears.
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
.addDeviceFilter(deviceFilter)
.setSingleDevice(true)
.build()
// When the app tries to pair with a Bluetooth device, show the
// corresponding dialog box to the user.
deviceManager.associate(pairingRequest,
object : CompanionDeviceManager.Callback() {
override fun onDeviceFound(chooserLauncher: IntentSender) {
startIntentSenderForResult(chooserLauncher,
SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
}
override fun onFailure(error: CharSequence?) {
// Handle the failure.
}
}, null)
}
This is how call device.createBond()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
SELECT_DEVICE_REQUEST_CODE -> when(resultCode) {
Activity.RESULT_OK -> {
// The user chose to pair the app with a Bluetooth device.
val deviceToPair: BluetoothDevice? =
data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)
deviceToPair?.let { device ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.BLUETOOTH_CONNECT
) != PackageManager.PERMISSION_GRANTED
) {
return
}
}
device.createBond()
// Maintain continuous interaction with a paired device.
}
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
I tried to do device.setPin() as follows. But always end up with BluetoothDevice.BOND_NONE
private var pairingReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent!!.action
Log.d("BRC: BLE ",action.toString())
val device: BluetoothDevice? =intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
PAIRING_PIN =intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR)
val pin = intent.getIntExtra("android.bluetooth.device.extra.PAIRING_KEY", 0)
when (action) {
BluetoothDevice.ACTION_PAIRING_REQUEST -> {
val type =intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR)
if (type == BluetoothDevice.PAIRING_VARIANT_PIN) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(
getContext(),
Manifest.permission.BLUETOOTH_CONNECT
) != PackageManager.PERMISSION_GRANTED
) {
return
}
}
device?.setPin("0".toByteArray(charset("UTF-8")))
//setPairing confirmation if neeeded
}else if (type == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
Log.d("TAG","BLE device PAIRING_VARIANT_PASSKEY_CONFIRMATION-${device?.bondState}")
}
}
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
val type =intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR)
if (type == BluetoothDevice.BOND_BONDING) {
Log.d("TAG:","BLE device BOND_BONDING-${device?.bondState}")
}else if (type == BluetoothDevice.BOND_BONDED) {
Log.d("TAG:","BLE device BOND_BONDED-${device?.bondState}")
}else if (type == BluetoothDevice.BOND_NONE) {
Log.d("TAG:","BLE device BOND_NONE-${device?.bondState}") //★Always end up in here
}
}
}
}
}
Technically if your device can do BLE advertising, then you should be able to use CompanionDeviceManager to associate with your device.
See https://developer.android.com/guide/topics/connectivity/bluetooth/connect-bluetooth-devices#connection-techniques.
If the two devices have not been previously paired, then the Android framework automatically shows a pairing request notification or dialog to the user during the connection procedure, as shown in figure 1.
I'm not familiar with iOS, sorry I can't help here.