Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with BluetoothServerSocket.accept() returning unconnected BluetoothSocket

I'm working on making an Android application (EDIT1: developing on 4.0.3 on Nexsus S's) that shares data P2P over Bluetooth and have run into a nasty snag on making the two devices connect. Our app uses NFC to pass the MAC address of one device to another. The sender then creates a BlueToothServer socket instance and calls accept, while the receiver creates a BluetoothDevice instance with the MAC address it received and attempts to connect.

The problem we're having is with the returned BluetoothSocket from accept(). The documentation for it clearly states that it should return a connected socket, but it instead returns a disconnected one which cannot even then be connected by calling connect(). I've checked the MAC address the socket has and it clears out, and the mere fact accept() returned implies that is successfully connected long enough to make the socket, so I'm at a loss here for what else to try.

It is also worth mentioning that the receiver's BluetoothSocket claims during this time that it actually is connected. Obviously it just simply hangs while it waits for data that never comes from the other device, but using checkpoints in multiple places we know that up until that time it is always claiming to be connected.

Any help or advice on this would be appreciated.

Other relevant information:

The sender and receiver are the same app, but represent different activities. This app is a game in which joining the game requires somebody who has the game to give it to someone else, which is initiated by the NFC (the data that is ultimately suppose to be sent over Bluetooth is the game data necessary for joining). The sender has the game and is in an activity that gives it away, while the receiver is in an activity that wants to receive it and then move into the giving away activity to allow them to pass it on to someone else. To make this clear now, while it would be possible for us to merge these two activities right now, we are going to be using the same technique later on as part of the actual game where it will not be possible to make sure both are on the same activity, so we need to overcome the different activity problem at some point anyway.

We are also sure that the UUIDs match properly. We have a global class with a constant UUID that we just generated at some point and use. It would appear depending on your purpose that UUIDs need to be something specific in certain instances, but so I have understood it as well, for this particular use, we were suppose to generate our own.

The BluetoothServerSocket is created and accept() called from it as part of a callback for NdefPushbackComplete, so the other phone definitely has the Ndef message before the sender begins to setup its server socket.

Neither the client or server bluetooth code is running on its own thread as commonly seen online, which is by design since we want the exchange to happen completely before either side can do anything else.

If I neglected to mention anything important, I would be happy to provide it. Finally, here is the client and server side code. This version of it uses an insecure socket, however I have tried it both ways and the phones being used to test this are paired.

Client:

String s = new String(msg.getRecords()[0].getPayload());
otherBluetoothMACAddress = s;
Log.d(DEV, "WaitingToGetGame: Other BT Address... "
    + otherBluetoothMACAddress);


Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();

Log.d(DEV, "WaitingToGetGame: Getting remote device");
BluetoothDevice bluetoothDevice = bluetoothAdapter
    .getRemoteDevice(otherBluetoothMACAddress);

ObjectInputStream objectInputStream;
ObjectOutputStream objectOutputStream;


try
{
  Log.d(DEV, "WaitingToGetGame: Getting BluetoothSocket");
  bluetoothSocket = bluetoothDevice
      .createInsecureRfcommSocketToServiceRecord(SDP_UUID);

  Log.d(DEV, "WaitingToGetGame: Connecting...");
  bluetoothSocket.connect();

  Log.d(DEV,
      "WaitingToGetGame: Bluetooth "
          + ((bluetoothSocket.isConnected()) ? ("is ") : ("is NOT "))
          + "Connected.");

  Log.d(DEV, "WaitingToGetGame: Getting input stream");
  bluetoothInputStream = bluetoothSocket.getInputStream();

  Log.d(DEV,
      "WaitingToGetGame: Bluetooth "
          + ((bluetoothSocket.isConnected()) ? ("is ") : ("is NOT "))
          + "Connected.");
  Log.d(DEV, "WaitingToGetGame: Getting output stream");
  bluetoothOutputStream = bluetoothSocket.getOutputStream();

  Log.d(DEV,
      "WaitingToGetGame: Bluetooth "
          + ((bluetoothSocket.isConnected()) ? ("is ") : ("is NOT "))
          + "Connected.");
  objectInputStream = new ObjectInputStream(bluetoothInputStream);
  objectOutputStream = new ObjectOutputStream(bluetoothOutputStream);

  Log.d(DEV, this.getClass().getSimpleName() + ": Receiving game data");
  game = (Game) objectInputStream.readObject();

  Log.d(DEV, this.getClass().getSimpleName() + ": Sending acknowledgment.");
  objectOutputStream.writeObject(new Boolean(true));
  objectInputStream.close();
  objectOutputStream.close();
  bluetoothInputStream.close();
  bluetoothOutputStream.close();
  bluetoothInputStream = null; // GC
  bluetoothOutputStream = null; // GC
  bluetoothSocket.close();
  bluetoothSocket = null;

Server:

BluetoothServerSocket bluetoothServerSocket = null;
ObjectInputStream objectInputStream;
ObjectOutputStream objectOutputStream;

try
{

  Log.d(DEV, this.getClass().getSimpleName()
      + ": Getting BluetoothServerSocket");
  // bluetoothServerSocket = bluetoothAdapter
  // .listenUsingRfcommWithServiceRecord(user.getName(), SDP_UUID);
  bluetoothServerSocket = bluetoothAdapter
      .listenUsingInsecureRfcommWithServiceRecord(user.getName(), SDP_UUID);

  Log.d(DEV, this.getClass().getSimpleName() + ": Waiting for connection.");
  bluetoothSocket = bluetoothServerSocket.accept();
  bluetoothServerSocket.close();
  BluetoothDevice dev = bluetoothSocket.getRemoteDevice();
  Log.d(DEV, dev.getAddress());

  Log.d(DEV, this.getClass().getSimpleName() + ": Bluetooth "
      + ((bluetoothSocket.isConnected()) ? ("is ") : ("is NOT "))
      + "Connected.");
  // At this point bluetoothSocket should be ready to use.

  Log.d(DEV, this.getClass().getSimpleName() + ": Getting input stream");
  bluetoothInputStream = bluetoothSocket.getInputStream();

  Log.d(DEV, this.getClass().getSimpleName() + ": Bluetooth "
      + ((bluetoothSocket.isConnected()) ? ("is ") : ("is NOT "))
      + "Connected.");


  Log.d(DEV, this.getClass().getSimpleName() + ": Getting output stream");
  bluetoothOutputStream = bluetoothSocket.getOutputStream();

  // Log.d(DEV, this.getClass().getSimpleName()
  // + ": Attempting direct connect()");
  // bluetoothSocket.connect();

  objectInputStream = new ObjectInputStream(bluetoothInputStream);
  objectOutputStream = new ObjectOutputStream(bluetoothOutputStream);


  Log.d(DEV, this.getClass().getSimpleName() + ": Sending game data.");
  objectOutputStream.writeObject(game);

  Log.d(DEV, this.getClass().getSimpleName() + ": Waiting for response.");
  Boolean response = (Boolean) objectInputStream.readObject();

  objectInputStream.close();
  objectOutputStream.close();
  bluetoothInputStream.close();
  bluetoothOutputStream.close();
  bluetoothInputStream = null; // GC
  bluetoothOutputStream = null; // GC
  bluetoothSocket.close();
  bluetoothSocket = null;
}// try
catch( IOException e )
{
  Log.d(
      DEV,
      this.getClass().getSimpleName() + ": "
          + java.util.Arrays.toString(e.getStackTrace()));
  e.printStackTrace();
  return;
}
catch( ClassNotFoundException e )
{
  // TODO Auto-generated catch block
  e.printStackTrace();
}
like image 241
Mr. Robot Avatar asked Mar 15 '26 05:03

Mr. Robot


1 Answers

I cannot find the fault in your program so far, but:

Obviously it just simply hangs while it waits for data that never comes from the other device, but using checkpoints in multiple places we know that up until that time it is always claiming to be connected.

Actually, this means the 2 devices connect, just that your BT client app does not get the socket connected -> it connects somewhere else.

You can try several things:

  • You must surround your blocking calls with try/catch, otherwise you cannot say for certain your code worked as expected. So surround bluetoothSocket.connect(); in your client code, and bluetoothSocket = bluetoothServerSocket.accept(); in the server code. The client code should catch an IOException on bt client connection error. I think this is the best way I found to determine if client connection succeeded or not! Ofc .isConnected() could do it afterwards, as in your code.

  • Run your bluetooth code inside an ASyncTask, if not (even better) a service. I know you'd rather not do it, but it may save you a lot of pain. My BT code runs perfectly from ASyncTasks - with custom UUID, between 2 Androids, between 1 Android and 1 BT-USB dongle, 1 Android - 1 embedded device, etc.

  • Change the UUID to SDP, and see if it works then!

One question though, have you tried transmitting something from the server side? It should work.. Let me know if any of this worked!

like image 62
Radu Avatar answered Mar 16 '26 20:03

Radu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!