kenjdavidson / react-native-bluetooth-classic

⚛ Bluetooth classic Android(Bluetooth)/IOS(ExternalAccessory) module for serial communication
https://kenjdavidson.github.io/react-native-bluetooth-classic
MIT License
248 stars 93 forks source link

discoverDevices() not returning a result #41

Closed frnkschmtt closed 4 years ago

frnkschmtt commented 4 years ago

Hello, I am currently facing the issue that I don't get any results from the discoverDevices() function.

I am able to communicate with devices which are already paired system wise via getConnectedDevices() and list() functions. Discovery via system settings works as expected. (Testing with Android 10 on Google Pixel 3a)

I tried both with await keyword and promise chaining with no success. Any ideas?

Thanks

kenjdavidson commented 4 years ago

Hey, I'll be completely honest I don't / haven't used the features that aren't available on IOS. I've never tested the discovery apis portion, I really just ported it from the react-native-bluetooth-serial lib, which I most likely could have messed things up.

I'll also be completely honest, I can't see myself looking into this issue within the next month - time crunch at work and a bunch of family stuff planned. If you can provide some debugging and a pull request, I'd be more than happy to accept it. But can't offer much more than that until at least late March/early April.

kenjdavidson commented 4 years ago

Taking a quick look at the code, I can see a potential issue.

Bluetooth.ACTION_FOUND is NOT resolving the promise, nor is it sending a CONNECTION message back to React Native. Bluetooth.ACTION_DISCOVERY_FINISHED is what actually causes the discovery promise to be resolved. Without reading https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery() I have no idea what causes the discovery to be finished?

This is a direct copy from the original library, I haven't looked to see if discovery is a problem there. There is a bunch of logging in that receiver, so you should be able to see in the log whether it's:

a) Returning from Discovery in the receiver and not resolving promise - there is nothing that could accidentally clear out the promise (which I can see being another issue) but for the time being it should resolve.

b) Not returning from discovery at all (if this is the case, there isn't much I can do about it) except implement an internal timeout and manage the discoveryPromise better (which I should do anyhow).

Thoughts - CancelDiscovery should also reject the startDiscovery promise I would think? Or resolve with the currently found devices during the allotted time?

kenjdavidson commented 4 years ago

Another possible issue is that an Activity Pause occurs which is disrupting the discoveryPromise.

nikolic-milan commented 4 years ago

I am currently looking for a library for react-native classic bluetooth and have tested the library react-native-bluetooth-serial-next wich is also a port from react-native-bluetooth-serial. That library has the same problem for functions listUnpaired() & discoverUnpairedDevices() but looks like it is manufacturer specific. On my Xiaomi phone it never returns a result, on Samsung it always returns a empty array and the Nokia I tested on works fine. This library's discoverDevices() function returns an empty array on my Samsung tablet, and never returns on both the Xiaomi and Nokia phone.

I have now also tested the react-native-bluetooth-serial library and the same thing happens as with your library. Samsung returns [ ], Xiaomi and Nokia don't return anything.

kenjdavidson commented 4 years ago

Well it's promising that I didn't mess up the port, although sad that it doesn't work in the original library either. As mentioned before, this functionality isn't available on IOS with External Accessory, so it's not going to be high on my radar.

With regards to:

1) the promise just not being resolved, I'm going to guess this is due to the mDiscoveryPromise being lost during the application lifecycle events. If this is the case, we cannot save it to the Bundle and we'll need to convert the promise to an event (DISCOVERY_FINISHED). The Enable Bluetooth API is also managed with a saved promise, I wonder if it's not working as well.

2) the fact that the promise returns on some phones, but the result is []. I'm assuming everything is setup properly and you can see the device if you're discovering from the system menu, which means that (a) either the devices are not being applied to the promise properly or (b) the BluetoothAdapter just isn't giving the library devices.

Feel free to provide as much information you can and we can plan away to get through it.

kenjdavidson commented 4 years ago

Some quick notes that I noticed just looking at this:

discoverDevices

cancelDiscovery

kenjdavidson commented 4 years ago

I had a time to take a look at the discover devices, I'm using my Huawei P20. There was definitely an issue - apparently the BluetoothDevice.FOUND action is fired multiple times for the same device, this was causing the unpairedDevices list to contain duplicates (which maybe was the issue ). I've updated so that only a single entry (per device) is stored and returned.

 LOG  Attempting to discover devices...
 LOG  Unpaired Devices
 LOG  [
{"address": "xxx", "class": 1028, "id": "xxx", "name": "iHome iBT230"}, 
{"address": "xxx", "class": 7936, "id": "xxx", "name": null}, 
{"address": "xxx", "class": 7936, "id": "xxx", "name": null}, 
{"address": "xxx", "class": 1084, "id": "xxx", "name": "[TV] Samsung 6 Series (50)"}]

This has two null devices - no clue what they are - but Android settings is smart enough to filter them out. But as you can tell, they do come back as expect. I haven't followed through with performing pairing, but again, I'm going to trust it works until someone provides differently.

kenjdavidson commented 4 years ago

I'm still not completely happy with how it's done, but I'll leave those changes for when I get to the multiple device connection and refactoring the configuration (v0.11.x).

kenjdavidson commented 4 years ago

I've updated the BluetoothClassicExample with the discover device functionality. I've given it a few runs through and haven't had any instances of it failing. I've cleaned it up (above) published v0.10.6.

If you can do your own debugging and let me know or submit a pull request I'm down. But without any more information it looks like it's working. If I can find a couple other android devices to try, I'll re-open if there are any failures.

Actually I'll keep it open for a bit longer to allow people to post comments and any debugging results they've found.

Cauen commented 4 years ago

Tested today RNBluetoothClassic.list() working fine but RNBluetoothClassic.discoverDevices() returning empty array

Phone: Xiaomi Redmi Note 8 Pro Android 9

Tested in BluetoothClassicExample ( v0.10.6. ) and my own code.

kenjdavidson commented 4 years ago

Any chance you could provide a little more debugging? Are you seeing the ACTION_FOUND coming into the log at all? It should be coming in numerous times for each device, if you're not seeing any of those then you're always going to get an empty array from discoverDevices. As mentioned, it works great on my phone, so it's a device/device thing.

Huawei P20 EMUI 9.1.0

They seem old, but any one of these might help your case:

I'd be more than happy to help out if anyone is willing to screen share or update the task with logs. If you're using the BluetoothClassicExample and still no dice, I really need some help debugging.

Cauen commented 4 years ago

I dont know what you mean with "ACTION_FOUND", but this is my logs image Do you think that the problem should be because i've commented this line? image Before commenting, caused a error "Overriding RNBluetoothClassic"... I want to help, just tell me how, i'm new react-native learner xD

Edit: This is the error: image

kenjdavidson commented 4 years ago

That's an issue with the new React Native auto linking. I think I posted a comment somewhere, that when you're working with the BluetoothClassicExample you should turn off auto linking so that you can't create the Package/Module manually. It's unlikely that this issue will have anything to do with this line - but you're doing the right thing in commenting it out and allowing Autolinking to take over.

You're going to want to put a breakpoint in BluetoothDiscoveryReceiver#onReceive as this is the method that accepts and documents the discovered device:

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.d(this.getClass().getSimpleName(), "Bluetooth Discovery Receiver: " + action);

        if (BluetoothDevice.ACTION_FOUND.equals(action)) {   // <-- THIS GUY RIGHT HERE
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

            if (!unpairedDevices.containsKey(device.getAddress())) {
                unpairedDevices.put(device.getAddress(), device);
            }
        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            onComplete.onDiscoveryComplete(unpairedDevices.values());
        }
    }

If this line never gets hit, or never comes back as TRUE then no devices are actually found and you will get an empty array unpairedDevices

kenjdavidson commented 4 years ago

My goal, eventually, is to have different configuration available upon creation. For example, I may provide DelimitedMessageBuffer and ByteArrayBuffer, but someone may want to use their own custom BluetoothDataBuffer which does something else, the only way to inject that is through the getPackage() method - which will require autolinking to be turned off for this module once it's completed.

Cauen commented 4 years ago

Now it is working, it was just missing to enable "location permissions" Thank you for your help :D

kenjdavidson commented 4 years ago

Can you update with what exactly you did for the others having this issue? I'll close it off once posted. Thanks for the update, keep safe!

Cauen commented 4 years ago

App dont asks for location permissions on Android This permission is needed to manage Bluetooth on Androd I've only go to the app config (in my device) and turned on the "Location permission" image I think that is recommended to add a "Location Permissions Ask" in Android version of the BluetoothClassicExample

eipporko commented 4 years ago

Hi!

I've been trying to use this library, and as @nikolic-milan says I've found some misbehavior using discoverDevices method.

I've build a little program and I tested it using 2 diferent Android devices:

The test runs perfectly on the Samsung tablet, but when I test it on the Xiaomi device the method discoverDevices() never returns nothing, not an empty list. :(

  enableBluetooth = async () => {
    try {
      console.log("Looking for unpaired Devices...")
      this.setState({ bolEnable: true});
      const unparied = await RNBluetoothClassic.discoverDevices();
      console.log(unparied)
      this.setState({ lista: unparied});
    } catch (error) {
      console.log(error)
    }
  }

Testing with other methods like list(), it works perfectly and returns the expected data.

I checked out permissions as you commented previously and in both devices are right. Could be something related with Xiaomi or Android 10?

kenjdavidson commented 4 years ago

At this point anything is possible. I've tested a number of versions up to 9. But I don't have a divice with 10 available. If I can get my hands on one I will definitely reopen and debug this.

It looks like 10 has a number of Bluetooth issues

https://help.elitehrv.com/article/135-clear-bluetooth-cache-android

From what I can tell there are no api changes what would be affecting it.

eipporko commented 4 years ago

I think that I found a solution to make it work in mi Xiaomi Mi A3.

You were right, seems to be an issue related with the API 29 which was the default API configured when I init the project using react-native init.

So I change it to use the API 28 modifying the build.gradle file

android/build.gradle

buildscript {
    ext {
        buildToolsVersion = "28.0.3"
        minSdkVersion = 16
        compileSdkVersion = 28
        targetSdkVersion = 28
    }
 ...
}

I don't know what, but seems that something in API 29 is not agree with the discoverDevices()'s way to be.

I hope this solution could be helpful to others with the same issue & thanks @kenjdavidson for this module. :)

kenjdavidson commented 4 years ago

Thanks for the note. I've looked at the api and there's nothing documented. When and if I can get my phone upgraded to 10 I'll look into this issue.

Until then, I'll update the Readme and docs noting that api 29 has issues with this one call.

Thanks. Ken.

sunilkumarkm commented 4 years ago

We need to enable the ACCESS_FINE_LOCATION permission https://developer.android.com/about/versions/10/privacy/changes#location-telephony-bluetooth-wifi

https://reactnative.dev/docs/permissionsandroid

I have tested it in API 29 and it's working fine.