10
votes

I have an application which talks to a custom device through RFCOMM via Bluetooth. The communication code is based on BluetoothTalk sample project. It was running without any problem before on either Galaxy S3, Galaxy S2, Galaxy Note and Nexus 7.

Recently, Nexus 7 were upgraded to Android 4.2 and since then, the problem happens as follows:

  1. When you use the app to set up a connection for the first time, meaning that the device is just turned on and app is just launched, no problem, you can get data normally.

  2. Then if you stop the communication, and try to restart, the communication fails with error "java.io.IOException: bt socket closed, read return: -1". From then on, no matter how many times you try to reconnect, it simply always fails.

  3. The only way to make it work again is, if you reboot the custom device AND the app, then try to connect, the communication becomes normal. But then, once you stop and restart the communication, it keeps on failing.

I borrowed a Nexus 4 with Android 4.2 and the issue remains.

It's really annoying because the main value of our device is relying upon the Bluetooth RFCOMM application. I double checked about the documentation on BT in Android 4.2 and didn't see any significant change. I'm fairly confident about the code on my side because it works for any Android device not running 4.2

Any hint or suggestion would be greatly appreciated. The device need to be demoed in the very beginning of December and we really want to address this issue ASAP.

EDIT: Now that 4.2.1 has been released and the problem is still not solved. Can we at least get some confirmation regarding if it's under working and will be fixed soon?

8

8 Answers

6
votes

This doesn't help you much, but note that Google introduced a completely new Bluetooth stack with 4.2.

This should be a good thing - in my experience as a user and developer, Android with Bluez (the old combo) never functioned reliably, so I was quite pleased to hear that they undertook a total rewrite.

I guess all I can say is that it sounds like you have encountered a bug or quirk in the new stack. It is unfortunate to hear that there are problems with the new stack too.

Regarding your demo, note that Google publishes the firmware images for all of their Nexus devices (https://developers.google.com/android/nexus/images), and flashing them to your devices is fairly easy.

So I suggest you file a bug report and then flash your devices to 4.1.2.

1
votes

It happened to me in my tests too. I the BluetoothChat sample code you should look at connectionLost method. I don't remember if there is any variable that keeps the number of lost connections, but you could add that yourself. in the connectionLost method, test if the number of lost connections is less than a predefined number (in my case 3). If that's true, send a message to the UI with mHandler ( a toast) and call connect(device) again. If that's not true (you lost connection more than 3 times), call stop() method.

Also be sure to open the socket in ConnectThread like this:

    public ConnectThread(BluetoothDevice device, boolean isSecure) {
        mmDevice = device;
        BluetoothSocket tmp = null;
        mSocketType = isSecure ? "Secure" : "Insecure";
        // Get a BluetoothSocket for a connection with the given BluetoothDevice
        if (isSecure) {
            // reflection is better to use
            Method m = null;
            try {
                Log.d(TAG, "create reflection");
                m = device.getClass().getMethod("createRfcommSocket",new Class[] { int.class });
                } catch (NoSuchMethodException e1) {
                e1.printStackTrace();
            }
            try {
                tmp = (BluetoothSocket) m.invoke(device, 1);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            mmSocketFallBack = tmp;
        } else {
            Log.d(TAG, "create insecure");
            try {
                tmp = device
                        .createInsecureRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        mmSocket = mmSocketFallBack;
    }

Your connectionLost should look something like:

public void connectionLost() {
    init = false;
    Log.d(TAG, "connectionLost -> " + mConnectionLostCount);
    mConnectionLostCount++;
  if (mConnectionLostCount < 3) {
    // Send a reconnect message back to the Activity
        Message msg = mHandler.obtainMessage(cBluetooth.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(WebAppInterface.TOAST, "Connection lost. Reconnecting...");
        msg.setData(bundle);
        mHandler.sendMessage(msg);
        connect(mSavedDevice,true);     
    } else {
    mConnectionLostCount = 0;
    Message msg = mHandler.obtainMessage(cBluetooth.MESSAGE_TOAST);
    Bundle bundle = new Bundle();
    bundle.putString(WebAppInterface.TOAST,"Device connection was lost!");
    msg.setData(bundle);
    mHandler.sendMessage(msg);
    cBluetooth.this.stop();
    }
}

I hope you can adapt it for your case. You could check this links too, they helped me a lot:

  1. remote control example
  2. connection dying solution
  3. Bluetooth Service example to get inspired from
1
votes

I had a similar problem and spent some time debugging this issue. I am running Android 4.3 and found the only basic modification that was required to BluetoothChat sample code was setting:

MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

I am trying to connect to a HC-05 bluetooth module and I believe it uses the SPP profile as indicated by this UUID. You can query a device's UUID with this line of code:

UUID uuid = device.getUuids()[0].getUuid();

Verify the UUID and see if it matches up with a profile that makes sense, based on the type of device you are connecting to.

MY_UUID_SECURE = uuid;

The UUID may indicate a different type of device profile, depending on what you are connecting to. With the proper UUID, I was still able to run the AcceptThread code and listen as a BluetoothServerSocket without any issues. I hope this helps, however, I am relatively new to Android and Bluetooth development, so if my assumptions are wrong, please correct me.

0
votes

Encountered similar problems. Fixed some of them by building with the 4.2 SDK with 17 as build target.

0
votes

I have finally figured out the solution.

It turns out that it's not a bug in the new Bluetooth driver. It's a corner case not considered in Bluetooth sample application, more specifically, BluetoothChat project, provided by Android SDK.

In the Bluetooth service code inside BluetoothChat sample project, when connection is failed or lost, it always starts the service over to restart listening mode. It's no problem when the phone is used as server, however, in some scenarios when the phone is used as client, once it goes into listening mode, you can't connect to other servers. Because in "connect" function, it doesn't cancel (In)SecureAcceptThread. So essentially you are listening to other connections as server while trying to connect to other servers as client. It's conflicting.

The way to solve this issue is, if you are sure your phone will not be used as server listening to incoming connection, then simply remove the code to restart listening mode once connection is failed or stopped.

0
votes

Encountered with the same problem with my Nexus 4.

In my case, I think the problem lies in SDP service discovery protocol. The device and the server must use the same UUID for a certain service.

I'm using the SPP(serial port protocol) in my application. So I change UUID from what is in BluetoothChat sample code as "fa87c0d0-afac-11de-8a39-0800200c9a66" to SPP "00001101-0000-1000-8000-00805F9B34FB". Now it's fine.

And I disable the listening code for acceptThread in BluetoothChatService.start(). More concise now. Hope it will help.

0
votes

I have same experience on 4.2.2.

But I had found that the bluetooth stack starts to misbehave only after my application falls or is killed without properly cleaning resources (socket and/or streams). Before that when I close my app properly it works fine.

For example when I close socket and then kill the app I can start it and connect again normally. If I kill my application without previously closing socket it is damned and I have to reboot the device to make it work again.

So it seems new Android bluetooth stack has some bug in implicit cleaning mechanism. When an application falls Android should clean open bluetooth resources which is not happening.

My bluetooth managing code is in Application subclass. There is no way - or I cannot see one - how to hook on Application destroy and do the cleaning.

Any suggestion appreciated

EDIT:

I have made quite a messy workaround.

I created separate application containing single remote service. I moved all the communication code (socket and streams opening, reading, writing, closing) into this remote service. Then if the main application crashes or is killed, the service is still running. When I start the application again the connection is still up. Bytes read from bluetooth input stream are transferred into main application via standard service messaging. For my case this is ok as I transfer very small amounts of data.

Still when the application containing the service is killed same mess happens.

0
votes

I am facing the same problem. This works for me:

try {
    Thread.sleep(1000); 
}
catch(Exception e3)
{
    Toast.makeText(getApplicationContext(), "wa ni sud sa thread sleep!", Toast.LENGTH_SHORT).show();
}
btSocket.close();