How to get the connected device list from a specific bluetooth profile

5k Views Asked by At

Is there anyway to get the connected devices list from support profiles (HDD,Spp and audio). The requirement is like my device will support HDD,SPP and Audio, so i have to filter the devices which supports all these profiles. Is there anyway to filter the devices?

2

There are 2 best solutions below

4
Mackovich On

Yes that is possible but your Android application must target SDK 11 or later (Android 3.0.X).

The solution to your question is that you have to query all BluetoothDevices known by your Android device. By known I mean all paired connected or unconnected devices and unpaired connected devices.

We will filter out unconnected devices later since you only want currently connected devices.


  • First you need to retrieve the BluetoothAdapter:

final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();

  • Second you need to make sure Bluetooth is available and turned on :

if (btAdapter != null && btAdapter.isEnabled()) // null means no Bluetooth!

If the Bluetooth is not turned out you can either use btAdapter.enable() which is not recommended in the documentation or ask the user to do it : Programmatically enabling bluetooth on Android

  • Third you need to define an array of states (to filter out unconnected devices):

final int[] states = new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING};

  • Fourth, you create a BluetoothProfile.ServiceListener which contains two callbacks triggered when a service is connected and disconnected :

    final BluetoothProfile.ServiceListener listener = new BluetoothProfile.ServiceListener() {
        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
        }
    
        @Override
        public void onServiceDisconnected(int profile) {
        }
    };
    

Now since you have to repeat the querying process for all available Bluetooth Profiles in the Android SDK (A2Dp, GATT, GATT_SERVER, Handset, Health, SAP) you should proceed as follow :

In onServiceConnected, place a condition that check what is the current profile so that we add the found devices into the correct collection and we use : proxy.getDevicesMatchingConnectionStates(states) to filter out unconnected devices:

switch (profile) {
    case BluetoothProfile.A2DP:
        ad2dpDevices.addAll(proxy.getDevicesMatchingConnectionStates(states));
        break;
    case BluetoothProfile.GATT: // NOTE ! Requires SDK 18 !
        gattDevices.addAll(proxy.getDevicesMatchingConnectionStates(states));
        break;
    case BluetoothProfile.GATT_SERVER: // NOTE ! Requires SDK 18 !
        gattServerDevices.addAll(proxy.getDevicesMatchingConnectionStates(states));
        break;
    case BluetoothProfile.HEADSET: 
        headsetDevices.addAll(proxy.getDevicesMatchingConnectionStates(states));
        break;
    case BluetoothProfile.HEALTH: // NOTE ! Requires SDK 14 !
        healthDevices.addAll(proxy.getDevicesMatchingConnectionStates(states));
        break;
    case BluetoothProfile.SAP: // NOTE ! Requires SDK 23 !
        sapDevices.addAll(proxy.getDevicesMatchingConnectionStates(states));
        break;
}

And finally, the last thing to do is start the querying process :

btAdapter.getProfileProxy(yourContext, listener, BluetoothProfile.A2DP);
btAdapter.getProfileProxy(yourContext, listener, BluetoothProfile.GATT); // NOTE ! Requires SDK 18 !
btAdapter.getProfileProxy(yourContext, listener, BluetoothProfile.GATT_SERVER); // NOTE ! Requires SDK 18 !
btAdapter.getProfileProxy(yourContext, listener, BluetoothProfile.HEADSET);
btAdapter.getProfileProxy(yourContext, listener, BluetoothProfile.HEALTH); // NOTE ! Requires SDK 14 !
btAdapter.getProfileProxy(yourContext, listener, BluetoothProfile.SAP); // NOTE ! Requires SDK 23 !
1
Eliav Louski On

extending @Mack answer, I was able to create a composable that return list of currently connected devices, with a refresh button:

here's the complete code:

package com.example.audio_output_selector

import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.core.app.ActivityCompat

@Composable
fun getConnectedBluetoothDevices(): List<BluetoothDevice> {
    val context = LocalContext.current
    val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    val bluetoothAdapter: BluetoothAdapter = bluetoothManager.getAdapter()
    if (bluetoothAdapter.isEnabled == false) {
        Log.i("MainActivity", "Bluetooth is not enabled")
    }

    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.BLUETOOTH_CONNECT
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        Log.i("MainActivity", "Should Requesting Bluetooth permission")
        return emptyList()
    }

    var connectedBluetoothDevices by remember {
        mutableStateOf(
            ConnectedBluetoothDevices(
                emptyList(), emptyList(), emptyList(), emptyList(), emptyList()
            )
        )
    }

    var currentBluetoothProfile: BluetoothProfile? = null
    var isRefreshing by remember { mutableStateOf(false) }

    LaunchedEffect(bluetoothAdapter, currentBluetoothProfile, isRefreshing) {
        if (isRefreshing) {
            bluetoothAdapter.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
                override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
                    currentBluetoothProfile = proxy
                    connectedBluetoothDevices = handleBluetoothService(profile, proxy)
                }

                override fun onServiceDisconnected(profile: Int) {
                    if (profile == BluetoothProfile.A2DP) {
                        Log.i("MainActivity", "A2DP devices disconnected")
                    }
                }
            }, BluetoothProfile.A2DP)
        }
        isRefreshing = false
    }


    //  bluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP) == BluetoothProfile.STATE_CONNECTED
    bluetoothAdapter.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
        override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
            connectedBluetoothDevices = handleBluetoothService(profile, proxy)
        }

        override fun onServiceDisconnected(profile: Int) {
            if (profile == BluetoothProfile.A2DP) {
                Log.i("MainActivity", "A2DP devices disconnected")
            }
        }
    }, BluetoothProfile.A2DP)

    Button(onClick = { isRefreshing = true }) {
        Text("Refresh BT")
    }

    // currently we are relating only on A2DP devices
    // but we could use them later with a little change if needed
    return connectedBluetoothDevices.a2dpDevices
}

fun handleBluetoothService(profile: Int, proxy: BluetoothProfile): ConnectedBluetoothDevices {
    val states = intArrayOf(
        BluetoothProfile.STATE_CONNECTED,
//        BluetoothProfile.STATE_CONNECTING,
//        BluetoothProfile.STATE_DISCONNECTED,
//        BluetoothProfile.STATE_DISCONNECTING
    )

    val ad2dpDevices = mutableListOf<BluetoothDevice>()
    val gattDevices = mutableListOf<BluetoothDevice>()
    val gattServerDevices = mutableListOf<BluetoothDevice>()
    val headsetDevices = mutableListOf<BluetoothDevice>()
    val sapDevices = mutableListOf<BluetoothDevice>()

    when (profile) {
        BluetoothProfile.A2DP -> ad2dpDevices.addAll(proxy.getDevicesMatchingConnectionStates(states))
        BluetoothProfile.GATT -> gattDevices.addAll(proxy.getDevicesMatchingConnectionStates(states))
        BluetoothProfile.GATT_SERVER -> gattServerDevices.addAll(
            proxy.getDevicesMatchingConnectionStates(
                states
            )
        )

        BluetoothProfile.HEADSET -> headsetDevices.addAll(
            proxy.getDevicesMatchingConnectionStates(
                states
            )
        )

        BluetoothProfile.SAP -> sapDevices.addAll(proxy.getDevicesMatchingConnectionStates(states))
    }
    return ConnectedBluetoothDevices(
        ad2dpDevices,
        gattDevices,
        gattServerDevices,
        headsetDevices,
        sapDevices
    )
//    to get the connected devices of selected profile
//    if (profile == BluetoothProfile.A2DP) {
//        val a2dp = proxy as BluetoothProfile
//        val devices = a2dp.connectedDevices
//        Log.i("MainActivity", "A2DP devices: $devices")
//    }
}

data class ConnectedBluetoothDevices(
    val a2dpDevices: List<BluetoothDevice>,
    val gattDevices: List<BluetoothDevice>,
    val gattServerDevices: List<BluetoothDevice>,
    val headsetDevices: List<BluetoothDevice>,
    val sapDevices: List<BluetoothDevice>,
)