32feet icon indicating copy to clipboard operation
32feet copied to clipboard

"BluetoothClient.DiscoverDevicesAsync" This method does not work correctly on the Android platform

Open squarezx opened this issue 1 year ago • 3 comments

I'm using classic bluetooth, developing on the Android platform, and my intended goal is to call my callback method when a bluetooth device is discovered, but using the DiscoverDevicesAsync method doesn't work. Using DiscoverDevices is correct, but need to wait 30 seconds before the results are returned, and I don't want to use such a synchronous method.

squarezx avatar Dec 13 '23 02:12 squarezx

Can you explain "does not work correctly" in more detail please?

peterfoot avatar Dec 18 '23 11:12 peterfoot

"does not work correctly" It means I cannot receive any BluetoothDeviceInfo, if I use sync method I can receive the BluetoothDeviceInfo discovered by the device as expected.

So I checked the code and I found something...... In the class '../Platforms/Android/BluetoothClient.android.cs'. public async IAsyncEnumerable<BluetoothDeviceInfo> DiscoverDevicesAsync([EnumeratorCancellation] CancellationToken cancellationToken) { if (InTheHand.AndroidActivity.CurrentActivity == null) throw new NotSupportedException("CurrentActivity was not detected or specified");

        List<BluetoothDeviceInfo> devices = new List<BluetoothDeviceInfo>();
        var waitable = new AutoResetEvent(false);

        HandlerThread handlerThread = new HandlerThread("ht");
        handlerThread.Start();
        Looper looper = handlerThread.Looper;
        Handler handler = new Handler(looper);

        BluetoothDiscoveryReceiver receiver = new BluetoothDiscoveryReceiver();
        IntentFilter filter = new IntentFilter();
        filter.AddAction(BluetoothDevice.ActionFound);
        filter.AddAction(BluetoothAdapter.ActionDiscoveryFinished);
        filter.AddAction(BluetoothAdapter.ActionDiscoveryStarted);
        InTheHand.AndroidActivity.CurrentActivity.RegisterReceiver(receiver, filter, null, handler);

        EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.AutoReset);

        receiver.DeviceFound += (s, e) =>
        {
            var bdi = new AndroidBluetoothDeviceInfo(e);
            if (cancellationToken.IsCancellationRequested)
            {
                ((BluetoothAdapter)_radio).CancelDiscovery();
            }
            else
            {
                if (!devices.Contains(bdi))
                {
                    devices.Add(bdi);
                    waitable.Set();
                }
            }
        };

        ((BluetoothAdapter)_radio).StartDiscovery();

        receiver.DiscoveryComplete += (s, e) =>
        {
            InTheHand.AndroidActivity.CurrentActivity.UnregisterReceiver(receiver);
            handle.Set();
            handlerThread.QuitSafely();
            waitable.Set();
        };

        handle.WaitOne();

        while(((BluetoothAdapter)_radio).IsDiscovering && !cancellationToken.IsCancellationRequested)
        {
            waitable.WaitOne();
            if(devices.Count > 0)
            {
                yield return devices[devices.Count - 1];
            }
        }

        yield break;
    }

I think the code "handle.WaitOne();" should not be used. It will block the current thread and prevent the subsequent While loop from being executed. what you think?

squarezx avatar Jan 03 '24 03:01 squarezx

Is this still an issue?

M0n7y5 avatar Jan 22 '24 15:01 M0n7y5

Fixed in 4.0.36

peterfoot avatar Mar 10 '24 17:03 peterfoot