javaandroidbluetoothbluetooth-lowenergy

Android's CompanionDeviceManager associate() fails to find any device when filtering by service


I'm using Google Android's CompanionDeviceManager API in order to find Bluetooth LE nearby devices able to measure Heart Rate.

    final BluetoothLeDeviceFilter DEVICE_FILTER =
                    new BluetoothLeDeviceFilter.Builder()
                        .setScanFilter( new ScanFilter.Builder()
                        /*    .setServiceUuid(
                                    ParcelUuid.fromString( "0000180D-0000-1000-8000-00805f9b34fb" )
                        */
                            .build() ).build();

    final AssociationRequest PAIRING_REQ = new AssociationRequest.Builder()
                    .addDeviceFilter( DEVICE_FILTER )
                    .setSingleDevice( false )
                    .build();

    final CompanionDeviceManager DEV_MANAGER =
                    (CompanionDeviceManager) getSystemService(
                                        Context.COMPANION_DEVICE_SERVICE );

    DEV_MANAGER.associate( PAIRING_REQ, new CompanionDeviceManager.Callback() {
            @Override
            public void onDeviceFound(@NonNull IntentSender chooserLauncher)
            {            
                try {
                    startIntentSenderForResult( chooserLauncher, RCQ_SELECT_DEVICE,
                                            null, 0, 0, 0 );
                } catch (IntentSender.SendIntentException e) {
                    Log.e( LOG_TAG, "onAssociationPending: failed to send intent" );
                }
            }
            @Override
            public void onFailure(CharSequence errorMessage)
            {
                Log.e( LOG_TAG, "[FAIL] " + errorMessage );
            }
        }, null );

Please note the commented lines at the beginning of the source code. I know this code works because, it its current state, it shows a dialog with all Bluetooth LE devices nearby, including the ones I'm interested in.

The problem comes when I remove the comment marks above, therefore filtering by the heart rate service. In my own device with Android 12, nothing appears on screen, not even a dialog with an empty device list. I've even tried to leave it there for hours, but nothing changes.

My expectations are:

It is possible I am making some mistake invoking the API, though. Any ideas? Any help will be truly appreciated.

FWIW, I'm aware of this question with no answer.


Solution

  • I encountered a similar situation in Android 12 while using Bluetooth classic.

    In my case, I used Companion Device Manager API as shown in the code below.

    fun startDiscovering(activity: Activity) {
        Log.d(
            TAG,
            "discovering state : ${bluetoothAdapter.isDiscovering} findStart!"
        )
    
        if (!bluetoothAdapter.isDiscovering) {
            _state.value = BluetoothState.STATE_DISCOVERING
            val executor: Executor = Executor { it.run() }
            if(Build.VERSION.SDK_INT>=33){
                deviceManager.associate(pairingRequest, executor, discoveringCallback)
            }else{
                deviceManager.associate(pairingRequest,discoveringCallback,null)
            }
    
            activity.registerReceiver(receiver, filter)
        }
    }
    

    On a device running API 33, a system dialog was displayed, but it was not displayed on a device running API 31.

    In conclusion, I found that callback in associate() function behaves differently between API 31 and API 33.

    Here is the reference for the associate() function in each version. (the common parts have been omitted)

    
    
    // @param callback will be called once there's at least one device found for user to choose from
    
    public void associate (AssociationRequest request, 
                        CompanionDeviceManager.Callback callback, 
                        Handler handler)
    
    // @param callback The callback used to notify application when the association is created.
    
    public void associate (AssociationRequest request, 
                    Executor executor, 
                    CompanionDeviceManager.Callback callback)
    
    

    According to the above comment, On API 31, the system dialog is displayed only when other devices are discovered.

    I believe this is the reason why system dialog is not displayed.

    In my case, no device matched my filter, so no dialog was displayed.

    However, I confirmed that it still worked for scanning devices even though the callback functions like onDeviceFound and onFailure were not invoked using BroadcastReceiver.

    The device starts discovering when the associate() function invoked and finishes a few seconds later.

    So, you can detect the discovering state using BroadcastReceiver to solve this problem.

    This is an IntentFilter that you can use with BroadcastReceiver for this case.

    private val filter = IntentFilter().apply {
            addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
            addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
        }
    

    PS. English isn't my first language. So even though I tried to make it readable, there still might be some awkward expressions.😅