3

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();
}
NFC guy
  • 10,151
  • 3
  • 27
  • 58
Mr. Robot
  • 65
  • 1
  • 5
  • Welcome to StackOverflow! Well written question. What type of phone(s) are you testing with (Manufacturer, Model, Android version, etc...)? – Jack May 08 '12 at 21:03
  • Well thank you! We are using Android 4.0.3 running on Nexus S's. I will edit that in. – Mr. Robot May 08 '12 at 21:47
  • Have you looked at the LogCat at the BondState changes when attempting to connect (on both devices) ? Does it give an error? – Jack May 09 '12 at 02:09
  • Not seeing anything in the logs about it ever changing, but I made a few to check the state off BluetoothDevice on both sides and they both claim to be bonded at all times. I will also add that I tried this: http://stackoverflow.com/questions/3397071/service-discovery-failed-exception-using-bluetooth-on-android since I discovered this error in the logs: E/BluetoothEventLoop.cpp(135): onCreateDeviceResult: D-Bus error: org.bluez.Error.AlreadyExists (Already Exists) and this did not help. Same problem, just minus service records and dynamic port selection. – Mr. Robot May 09 '12 at 18:40

1 Answers1

1

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!

Radu
  • 2,076
  • 2
  • 20
  • 40
  • Everything we do is in a try/catch block; that's how I know it's passing me an unconnected socket instead of throwing an IOException like it is suppose to. Furthermore, I cannot send anything from the server side because while it is unconnected the Input/Output streams, while retrievable via get functions, throw exceptions when you try to use them unconnected. In fact, the point of failure right now IS when the server tries to send data. Furthermore, both the secure and insecure functions use SDP, the UUIDs representing a service record, so what you're saying makes no sense. – Mr. Robot May 10 '12 at 13:24
  • My bad, I meant SPP!!!! And while you are at it, try only one code at a time. This means use your client SPP code(00001101-0000-1000-8000-00805F9B34FB) with some Android server app and viceversa! – Radu May 10 '12 at 13:35