43

I'm developing a program in which, from an Android Phone, I have to connect as a client to a Bluetooth medical sensor. I'm using the official Bluetooth API and no problem during connection (SPP profile), but when I end the socket, the sensor is still connected to my phone (although I have close the connection).

Are there any way to make a Bluetooth disconnection? I think there is an intent called ACTION_ACL_CONNECTED, which does that. Can anyone explain me how to use this?

Thanks in advance.

EDITED: Here is the code, if anyone needs additional info, it's a Nonin 4100 medical sensor.

Set<BluetoothDevice> pairedDevices = Activa.myBluetoothAdapter.getBondedDevices();
        // If there are paired devices
        if (pairedDevices.size() > 0) {
            // Loop through paired devices
            for (BluetoothDevice device : pairedDevices) {
                // Add the name and address to an array adapter to show in a ListView
                String name = device.getName();
                if (name.contains("Nonin")) {
                    try {
                        found = true;
//                      socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//                      handler.sendEmptyMessage(5);
//                      Activa.myBluetoothAdapter.cancelDiscovery();
//                      socket.connect();
                        BluetoothDevice hxm = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(device.getAddress());
                        Method m;
                        try {
                            m = hxm.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                            socket = (BluetoothSocket)m.invoke(hxm, Integer.valueOf(1));
                            handler.sendEmptyMessage(5);
                            socket.connect();
                        } catch (Exception e) {
                            handler.sendEmptyMessage(7);
                            e.printStackTrace();
                            break;
                        }
                        handler.sendEmptyMessage(6);
                        InputStream in = socket.getInputStream();
                        OutputStream out = socket.getOutputStream();
                        byte[] retrieve = { 0x44, 0x31};
                        out.write(retrieve);
                        byte [] ack = new byte [1];
                        in.read(ack);
                        if (ack[0] == 0x15) {
                            cancelMeasurement();
                            return;
                        }
                        byte [] data = new byte [3];
                        long timeStart = System.currentTimeMillis();
                        this.timePassed = System.currentTimeMillis() - timeStart;
                        while ((this.timePassed < (this.time))&&(this.finished)) {
                            try {
                                in.read(data);
                                processData(data);
                                Thread.sleep(1000);
                                this.timePassed = System.currentTimeMillis() - timeStart;
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        in.close();
                        out.close();
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
}
user365610
  • 443
  • 1
  • 5
  • 9

6 Answers6

73

Please remember to close your Input/output streams first, then close the socket.

By closing the streams, you kick off the disconnect process. After you close the socket, the connection should be fully broken down.

If you close the socket before the streams, you may be bypassing certain shutdown steps, such as the (proper) closing of the physical layer connection.

Here's the method I use when its time to breakdown the connection.

/**
 * Reset input and output streams and make sure socket is closed. 
 * This method will be used during shutdown() to ensure that the connection is properly closed during a shutdown.  
 * @return
 */
private void resetConnection() {
        if (mBTInputStream != null) {
                try {mBTInputStream.close();} catch (Exception e) {}
                mBTInputStream = null;
        }

        if (mBTOutputStream != null) {
                try {mBTOutputStream.close();} catch (Exception e) {}
                mBTOutputStream = null;
        }

        if (mBTSocket != null) {
                try {mBTSocket.close();} catch (Exception e) {}
                mBTSocket = null;
        }

}

EDIT: Adding code for connect():

// bluetooth adapter which provides access to bluetooth functionality. 
BluetoothAdapter        mBTAdapter      = null;
// socket represents the open connection.
BluetoothSocket         mBTSocket   = null;
// device represents the peer
BluetoothDevice         mBTDevice       = null; 

// streams
InputStream           mBTInputStream  = null;
OutputStream          mBTOutputStream = null;

static final UUID UUID_RFCOMM_GENERIC = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

/**
 * Try to establish a connection with the peer. 
 * This method runs synchronously and blocks for one or more seconds while it does its thing 
 * SO CALL IT FROM A NON-UI THREAD!
 * @return - returns true if the connection has been established and is ready for use. False otherwise. 
 */
private  boolean connect() {

        // Reset all streams and socket.
        resetConnection();

        // make sure peer is defined as a valid device based on their MAC. If not then do it. 
        if (mBTDevice == null) 
                mBTDevice = mBTAdapter.getRemoteDevice(mPeerMAC);

        // Make an RFCOMM binding. 
        try {mBTSocket = mBTDevice.createRfcommSocketToServiceRecord(UUID_RFCOMM_GENERIC);
        } catch (Exception e1) {
                msg ("connect(): Failed to bind to RFCOMM by UUID. msg=" + e1.getMessage());
                return false;
        }

        msg ("connect(): Trying to connect.");

        try {
                mBTSocket.connect();
        } catch (Exception e) {
                msg ("connect(): Exception thrown during connect: " + e.getMessage());
                return false;
        }

        msg ("connect(): CONNECTED!");

        try {
                mBTOutputStream = mBTSocket.getOutputStream();
                mBTInputStream  = mBTSocket.getInputStream();
        } catch (Exception e) {
                msg ("connect(): Error attaching i/o streams to socket. msg=" + e.getMessage());
                return false;
        }

        return true;
}
Brad Hein
  • 10,997
  • 12
  • 51
  • 74
  • 1
    Hi, First of all, thansk for your reply, but It doesn't solve me the problem. I was doing something similar (that is, first closing the streams and then the socket) and so I disconnect the connection from the Android side, but the medical sensor I was using is not advertised of the disconnection and keeps on connected, so I can't connect again to it unless I reset it. – user365610 Jun 15 '10 at 10:44
  • Please post your code. At this point we're just guessing. I've seen the problem you're reporting but there can be many causes so before I waste more of my time guessing, please post your code. – Brad Hein Jun 15 '10 at 14:26
  • I have edited the original post with the code. I have replaced the part of closing the streams and the socket with the one you passed me, but it remains equal. Thanks in advance. – user365610 Jun 16 '10 at 12:05
  • Thanks for posting the code it is very helpful. 1. Missing RFCOMM Binding. 2. try-catch is trying too many things - if one thing fails then the code that follows it never executes. Specifically, put the two stream closes in a try-catch, and the socket.close in another. 3. why the getClass() and getMethod() approach? I'm not familiar with this approach but it feels risky. 4. What are you doing the sendEmptyMessage() stuff for? It seems superfluous 5. Why Integer.valueOf(1) instead of just `1`? 6. When reading, just use in.read() to read a single byte as an integer, much simpler than a byte[].. – Brad Hein Jun 16 '10 at 12:42
  • I just edited my answer to include the `connect()` method from my code. I removed a bunch of extras and just left the basics. You can define a `msg(String)` method or just remove those calls from `connect()`. I always like to have a msg() handy so I can write message to the screen or device `Log` as I'm debugging. So to use the code, define mPeerMac as a string that holds your peer's bluetooth MAC, then just call `connect()`. if `connect()` returns true then go ahead and use the input/output streams. – Brad Hein Jun 16 '10 at 12:50
  • This is very disappointing that so much time was spent providing help but no answer was accepted and no votes up? – Brad Hein Jun 24 '10 at 19:40
  • Brad, I was some time out. First of all, thanks a lot for your help, and my apologizes for not putting more attention, i hope you understand I'm new on this. I put the two responses (yours and Per's one) as correct because as I've seen yours is the correct answer in the normal case, but in my concrete case Per's answer is the good one. Thanks to both of you for your help – user365610 Aug 13 '10 at 14:14
  • 1
    Brad, I am trying to learn Bluetooth SPP for an app I am working on at work. In addition to helping solve a frustrating disconnection problem I was having, your code (with comments) is one of the most concise explanations I have seen of trying to connect with SPP. Do you have any post or articles anywhere that expound on this? I think this is the beginnings of a solid tutorial. – jaredkwright Mar 15 '16 at 03:31
  • I am puzzled as to why closing the streams first would make any difference because, based on the source code, closing the streams appears only to close the socket. (See stackoverflow.com/a/26035118/2317905.) If that is correct, you need close only the socket and not the streams. (This seems to be the approach taken in BluetoothChat, though it is known to be buggy.) Perhaps the source code has changed since this answer was posted. – stevehs17 Jan 21 '18 at 21:18
  • @stevehs17 I was going for the cleanest tear-down possible. What's the harm in ensuring that the streams are torn down properly? On the other hand if you want to make optimistic assumptions that may be at your peril. My coding style tends to be quite bulletproof, and this sort of habit is a good example of what makes my code so reliable. – Brad Hein Feb 07 '18 at 04:05
  • Brad: Two issues need to be distinguished: writing bulletproof code vs. what happens when you close Bluetooth streams and sockets, whether there is a particular order in which they should be closed, and whether closing them in a particular order will solve the OP's problem. My point concerns the second issue: closing the streams does the exact same thing as closing the socket since the close methods on the stream classes simply call BluetoothSocket#close() on the socket. Therefore the order in which the streams and socket are closed should have no bearing on the OP's problem. – stevehs17 Feb 09 '18 at 00:41
  • @stevehs17 Both the question and the answer have helped many people. The question was asked five years ago. What's your point/goal exactly? – Brad Hein Feb 21 '18 at 14:29
19

I found that if I call socket.close() too soon after a recent communication via the OutputStream, then the close fails and I cannot reconnect. I added a Thread.sleep(1000) just prior to the call to close() and this seems to solve it.

djilk
  • 387
  • 3
  • 4
  • solution from djilk works for me !, adding the Thread.sleep(1000) before reseting the conection it releases the bluetooth. Tested on 3 SPP devices included the CsR 417 Thanks. – user3523720 Apr 11 '14 at 15:11
11

HI,

I've seen the exact same problem (HTC Desire). Despite closing the socket by the book (as Brad suggests), the next connect() blocks forever - until ended by close() by another thread.

I circumvented the problem by always calling BluetoothAdapter.disable()/.enable() before connecting. Awful, unfriendly hack, I know...

I suspect that some of the present BT issues are manufacturer specific, as some app implementors seem to live happily with createRfcommSocketToServiceRecord(), which definitely fails on my HTC Desire (Android 2.1 update 1).

I have seen indications (sorry, don't have references) that HTC Desire's BT stack differs from the Nexus One, although they seem to be very similar devices...

BR Per

(addition) Here's a very simple activity to reproduce the problem (without my disable/enable 'cure'):

package com.care2wear.BtTest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class BtTestActivity extends Activity {
    private static final String TAG="BtTest";

    BluetoothAdapter mBtAdapter = null;
    BluetoothDevice mBtDev = null;
    BluetoothSocket mBtSocket = null;
    InputStream isBt;
    OutputStream osBt;  
    String mAddress = "00:18:E4:1C:A4:66";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        init();

        connect();  // ok
        disconnect(); // ok
        connect(); // this invariably fails - blocked until BT is switched off by someone else, or the peer device turns off/goes out of range
        disconnect();
    }

    private void init() {
        Log.d(TAG, "initializing");
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
        mBtDev = mBtAdapter.getRemoteDevice(mAddress);
        Log.d(TAG, "initialized");
    }

    private void connect() {
        try {
            Log.d(TAG, "connecting");
            Method m = mBtDev.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
            mBtSocket = (BluetoothSocket) m.invoke(mBtDev, 1);
            mBtSocket.connect();
            Log.d(TAG, "connected");
        } catch (SecurityException e) {
            Log.e(TAG, "SecEx", e);
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "NsmEx", e);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "IArgEx", e);
        } catch (IllegalAccessException e) {
            Log.e(TAG, "IAccEx", e);
        } catch (InvocationTargetException e) {
            Log.e(TAG, "ItEx", e);
        } catch (IOException e) {
            Log.e(TAG, "IOEx", e);
        }

    }

    private void disconnect() {
        Log.d(TAG, "closing");

        if (isBt != null) {
            try {
                isBt.close();
            } catch (IOException e) {
                Log.e(TAG, "isBt IOE", e);              
            }
            isBt = null;
        }
        if (osBt != null) {
            try {
                osBt.close();
            } catch (IOException e) {
                Log.e(TAG, "osBt IOE", e);              
            }
            osBt = null;
        }
        if (mBtSocket != null) {
            try {
                mBtSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "socket IOE", e);                
            }
            mBtSocket = null;
        }
        Log.d(TAG, "closed");       
    }
}

If anyone can spot if I'm doing it wrongly, feel free to comment :)

(addition 2) I think I got it to work now:

  1. The official method of connecting RFCOMM (via SDP) now actually seems to work (HTC Desire, 2.1 update 1), BUT I had to remove and re-pair the BT device. Go figure..
  2. Reconnection may still fail (service discovery failure) if I reconnect 'too quickly' (quit app, then immediately restart). Guess the connection is not completely down yet..
  3. If I always end the (last) activity not only with finish(), but also with Runtime.getRuntime().exit(0);, it works a lot better. Go figure again...

If anyone can explain this, I'll happily learn. /Per

(addition 3) Finally got the Froyo (2.2) update for my Desire, and as far as I can see, SPP now works :) /Per

Per Laursen
  • 111
  • 4
2

I was developing an app that conects to a BT device. Your code works fine in my HTC Wildfire but with a Samsung Galaxy I5700 doen't work. Both os are 2.1 update but.....

The exception was 'InvocationTargetException'

The only thing I had to modify is the disconnect().

private void disconnect() {
         if(Conectado){ 
            try {
                ***mBtSocket.close();***
                 texto.setText(texto.getText()+"\nDesconectado");
                 Conectado = false;
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                texto.setText(texto.getText()+"\n"+e1.getMessage());
            } 
            catch (Exception e2) {
                // TODO Auto-generated catch block
                texto.setText(texto.getText()+"\n"+e2.getMessage());
            }    
         }
ekatz
  • 963
  • 3
  • 18
  • 38
user343464
  • 21
  • 2
1

Hey so I have been using the Bluetooth Chat application from The Android Development site and they provide a stop() method in BluetoothChatService class. So I simply created an instance of it in my main class and and called the stop function from my disconnect button.

Here is how I call it in my main class

// Member object for the chat services

private BluetoothManager mChatService = null;
case R.id.disconnect:
        mChatService.stop();
break;

The stop() method in BluetoothChatService

private AcceptThread mAcceptThread;
private ConnectThread mConnectThread;
public synchronized void stop() 
{
    if (mConnectThread != null)
    {
        mConnectThread.cancel(); mConnectThread = null;
    }
    if (mConnectedThread != null) 
    {
        mConnectedThread.cancel(); mConnectedThread = null;
    }
    if (mAcceptThread != null) 
    {
        mAcceptThread.cancel(); mAcceptThread = null;
    }
}
hlovdal
  • 26,565
  • 10
  • 94
  • 165
0

I have the same Issue. This is the trouble with the Bluetooth Module CSR BC417, present in many devices as serial to bluetooth adapter with SPP profile. With another Bluetooth module android device works well, and the bluetooth release the conection after the socket is closed, but with devices with this CSR core not. Tested on SPP Bluetooth to Serial Adaptor based on CSR BC417, and Bluetooth module from Actisys. Both with Android 4.0 devices. I dont know why but is a compatibility issue between harwares, try to change the serial adaptor for another with a different Core. I tryout programatically to find a solution, even disabling a bluetooth, but is impossible, the trouble is originated on the CSR module.

  • 1
    Never start an answer with "I have the same issue". I had to read very carefully to determine that there is actually some potentially useful information here. – Scott Solmer Mar 24 '14 at 20:59