Disconnect a bluetooth socket in Android
Asked Answered
L

6

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();
                    }
                }
            }
        }
}
Lilithe answered 13/6, 2010 at 9:56 Comment(0)
T
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;
}
Timepleaser answered 14/6, 2010 at 18:26 Comment(12)
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.Lilithe
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.Timepleaser
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.Lilithe
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[]..Timepleaser
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.Timepleaser
This is very disappointing that so much time was spent providing help but no answer was accepted and no votes up?Timepleaser
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 helpLilithe
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.Locally
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 https://mcmap.net/q/190309/-do-i-need-to-close-inputstream-outputstream-if-i-close-bluetoothsocket.) 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.Biocellate
@Biocellate 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.Timepleaser
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.Biocellate
@Biocellate Both the question and the answer have helped many people. The question was asked five years ago. What's your point/goal exactly?Timepleaser
U
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.

Unbelieving answered 31/3, 2014 at 18:23 Comment(1)
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.Anaesthesia
B
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

Brasher answered 18/7, 2010 at 8:22 Comment(0)
W
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());
            }    
         }
Walter answered 15/9, 2010 at 20:18 Comment(0)
M
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;
    }
}
Marna answered 27/6, 2012 at 17:31 Comment(1)
I am not able to connect again after calling stop method. any idea?Hinckley
T
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.

Tidwell answered 24/3, 2014 at 20:38 Comment(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.Foofaraw

© 2022 - 2024 — McMap. All rights reserved.