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

Device that gets connected to, returns zero connected devices when running getConnectedDevices() #244

Closed jpinto321 closed 1 year ago

jpinto321 commented 1 year ago

Mobile Device Environment Provide a list of operating systems on which this issue is relevant.

Application Environment Provide information about your development environment:

Describe the bug If I connect device1 to device2 (device2.connect()), and then I do getConnectedDevices() on both phones, only device1 will return connected devices, while device2 always returns zero devices connected.

To Reproduce Steps to reproduce the behavior: 1- device1 and device2 are paired using the bluetooth settings 2- set device2 in accept mode 3- device1 connects to device2 -> device2.connect() 4- run getConnectedDevices() on device1, which returns device2 as being connected 5- run getConnectedDevices() on device2, which doesn't return any device connected

Expected behavior When device1 connects to device2, device2 should return connected devices when I run -> getConnectedDevices()

kenjdavidson commented 1 year ago

https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/c75c527b0c352cb1619df07a972a9714c73017cc/android/src/main/java/kjd/reactnative/bluetooth/RNBluetoothClassicModule.java#L665

Accept is adding the new connection to the connection map. Does communication between the two devices work properly? Using write should be verifying the connection before write.

Do you see any errors in the log? Are you able to debug the get connected devices?

kenjdavidson commented 1 year ago

If when accepting a device the Bluetooth socket doesn't have access to the remote device, this is an issue, and will require rewriting a chunk of stuff to maintain the device as well as the connection.

Can you please confirm?

jpinto321 commented 1 year ago

Some things I've noticed:

1- the accept promise never resolves to anything.

RNBluetoothClassic.accept({ delimiter: '\r' }).then((device) => console.log('accept device')).catch((error) => console.log(error))

When running RNBluetoothClassic.accept({ delimiter: '\r' }), it will never resolve (console.log('accept device')). If I run it twice, it will throw an error saying it's already in the accept mode.

2- I have to try and connect 4 or 5 times until it's able to connect. The times it's not able to connect, it throws this error: [Error: java.io.IOException: read failed, socket might closed or timeout, read ret: -1]

3- When I do connect, device1 returns device2 when I run getConnectedDevices, while device2 doesn't return anything.

I've never tried debugging native code, so I would have to do some research on how to do that.

kenjdavidson commented 1 year ago

Ok, so this is a pretty confusing way to list issues.

1 - If accept never returns, then the device is never actually connected.

2 - Socket exceptions are generally device issues, and not really library issues. More debugging will be needed, but the two that I have work fine. Unfortunately I can't help much unless you're willing to buy me some more android devices to test with.

3 - You should be able to debug the code, if you're saying that the connection does finally work (which is the opposite of what you're saying in 1) then you should be able to debug the code to:

a) Confirm that mConnections is being updated on hte accept side b) That Connection#getNativeDevice is actually returning the active device

If (a) is working, ie. the connection is being added to the map (which if not you should see other errors) then the next issue could be that the AcceptSocket handles the native device differently. In which case, you should be able to confirm this, and if this is the case, the whole management of connected devices needs to be re-worked.

jpinto321 commented 1 year ago

So, I think I understand what the problem is.

1- device1 is in the react-native app and is the one that is going to scan for device2. I'm scanning from inside the react-native app. 2- device2 is in the Bluetooth settings screen, so that it's advertising, which means that the react-native app is in the background and no code is running. 3- device1 is done with scanning and finds device2. 4- device1 connects to device2 (device2.connect()). While device2 is still in the Bluetooth settings screen. 5- Now the issue is that device2 had the app in the background, and thus the accept never resolved. And even when I go back to the app, the accept doesn't resolve. The only way it would resolve, is if the app was on the foreground when device1 was connecting to device2.

I just don't understand why sometimes the accept resolves, when the app is in the background.

Is there a way to actually advertise when inside an app?

kenjdavidson commented 1 year ago

Even more questions/commnets:

  1. How are you starting accept on device2 if you're in the bluetooth settings screen?
  2. device1 attempting to connect to device2 when it's not in accept mode is going to be why you're getting IOExceptions

Your workflow should be this:

1 - Pair the two devices, just pair them.
2 - Once you know they are paired start accept device2 3 - Start connect on device1

You should not be starting accept and then leaving the application. React Native doesn't like doing things in the background, and therefore this wasn't really a planned workflow.

No, there is no way to advertise inside the library, as I mentioned:

1 - this is bluetooth classic, the devices should be paired before anything. Once paired they will continue to pair whenever they are in distance and enabled. 2 - if this is a common workflow (which hasn't come up until now, so I doubt it) then a new function needs to be added to the library to allow calls to enable discovery. But this just enables discovery, you still need to go through the pairing logic and confirm that both are doing so.

jpinto321 commented 1 year ago

Thanks for the quick replies.

In regards to your question 1, I did open the react-native app on device2 and the accept was ran, before going to the bluetooth screen.

I think I now understand that the worflow that I currently have will not work. (one device in the bluetooth screen, another in the app, etc)

I actually thought that device2 needed to be advertising (even though they were already paired), to be able to be connected to, but since it's not needed, I can definitely change the functionality so that both devices are inside the react-native app.

Thank you for your help, I now understand better what to do and not to do.

kenjdavidson commented 1 year ago

Essentially yes, before starting connect or accept you should ensure that the devices are paired in both. It probably should be doable so that:

but unless React Native has fixed some of their background stuff, this is difficult. Natively the socket needs to be shut down on leave and brought back up if it was open on leave. Honestly, if you're doing this much work with bluetooth you should really be doing it natively, and not using react native.

Sahil1608032 commented 1 year ago

RNBluetoothClassic.getConnectedDevices in my case return [] array

i am using an application in debug mode and trying to connect with another device. (Android)

can you please let me how to fix it?

kenjdavidson commented 1 year ago

RNBluetoothClassic.getConnectedDevices in my case return [] array

i am using an application in debug mode and trying to connect with another device. (Android)

can you please let me how to fix it?

I suggest you open a new issue with substantially more information. I'm guessing you haven't read the documentation and are using the library incorrectly (which is the reason for like 90% of these types of questions).