12

I am compiling for SDK 10 (2.3.3):

android:minSdkVersion="10"
android:targetSdkVersion="16"

I am testing on two Sony Ericsson smartphones. One has Android 2.3.7 on it and the other 4.0.1.

I am using listenUsingInsecureRfcommWithServiceRecord to open a new server socket on bluetooth and listen to connections.

If the connection is accepted successfully then everything works fine. I can even try to cancel the server socket but that does not seem to bother the connection socket just created.

BUT when I want to cancel the server socket before having accepted any connections as soon as the line bluetoothServerSocket.close(); gets executed the entire activity closes and the process dies. And furthermore this is not a regular exception which I could handle.

Actually even logcat itself quits!! and I have to quickly execute it again in order to grab the errors you can see below:

Zygote  D  Process 25471 terminated by signal (11)
  295         InputDispatcher  W  channel '2c2e20a8 com.pligor.test/activities.MainActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x8
  295         InputDispatcher  E  channel '2c2e20a8 com.pligor.test/activities.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
  295                dalvikvm  D  GC_FOR_ALLOC freed 1299K, 21% free 13252K/16583K, paused 93ms
  295         InputDispatcher  W  Attempted to unregister already unregistered input channel '2c2e20a8 com.pligor.test/activities.MainActivity (server)'
  295        BluetoothService  D  Tracked app 25471 diedType:10
  295        BluetoothService  D  Removing service record 10009 for pid 25471
  132          SurfaceFlinger  D  Release buffer at 0x61c08
  295           WindowManager  I  WINDOW DIED Window{2c2e20a8 com.pligor.test/activities.MainActivity paused=false}
  295         ActivityManager  I  Process com.pligor.test (pid 25471) has died.
  295         ActivityManager  W  Force removing ActivityRecord{2c021800 com.pligor.test/activities.MainActivity}: app died, no saved state
  295           WindowManager  W  Failed looking up window
  295           WindowManager  W  java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@2bf3e798 does not exist
  295           WindowManager  W    at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7165)
  295           WindowManager  W    at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7156)
  295           WindowManager  W    at com.android.server.wm.WindowState$DeathRecipient.binderDied(WindowState.java:1545)
  295           WindowManager  W    at android.os.BinderProxy.sendDeathNotice(Binder.java:417)
  295           WindowManager  W    at dalvik.system.NativeStart.run(Native Method)
  295           WindowManager  I  WIN DEATH: null
  295      BluetoothEventLoop  D  Property Changed: UUIDs : 11
  295    hAdapterStateMachine  D  BluetoothOn process message: 51
  295     InputManagerService  W  Got RemoteException sending setActive(false) notification to pid 25471 uid 10040

Note: Process terminated by signal (11) means Segmentation fault (http://en.wikipedia.org/wiki/SIGSEGV).

EDIT

I create the Bluetooth server socket using the following code (Scala):

private val bluetoothServerSocket: BluetoothServerSocket = try {
    bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(MY_SERVICE_NAME_INSE‌​CURE, MY_UUID_INSECURE); 
} 
catch { 
    case e: IOException => throw new ServerSocketException; 
} 

I use this code to close the Bluetooth socket:

try { 
    isCancelled = true; 
    bluetoothServerSocket.close(); 
} catch { 
    case e: IOException => throw new NotClosedException; 
}
Phil
  • 35,852
  • 23
  • 123
  • 164
George Pligoropoulos
  • 2,919
  • 3
  • 33
  • 65
  • I've done a little research, and it seems that signal (11) terminations are caused by programs accessing memory they shouldn't be accessing or that doesn't exist. I don't know why your program would be using excessive memory, but you may want to check how much it's using. Posting the code may be helpful for us trying to troubleshoot here. – hBrent Feb 22 '13 at 18:31
  • Ok sure I have no problem. But which portion of the code would you like me to add exactly? – George Pligoropoulos Feb 24 '13 at 02:11
  • Does you call to `bluetoothServerSocket.close();` throws an exception when no connection was established? – Houf Feb 24 '13 at 14:32
  • Houf I don't get you exactly. No it does not throw an exception. I get the errors you see above – George Pligoropoulos Feb 24 '13 at 20:20
  • Please post your relevant code. – Phil Feb 28 '13 at 17:43
  • @Phil all the relevant code is too long to write it. Here are a few snippets (scala). bluetooth server socket is created like that: `private val bluetoothServerSocket: BluetoothServerSocket = try { bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(MY_SERVICE_NAME_INSECURE, MY_UUID_INSECURE); } } catch { case e: IOException => throw new ServerSocketException; }` and later this code is executed to close the bluetooth socket: `try { isCancelled = true; bluetoothServerSocket.close(); } catch { case e: IOException => throw new NotClosedException; }` – George Pligoropoulos Feb 28 '13 at 18:53

3 Answers3

6

I have experienced a similar issue and the root cause of the problem was calling close on a socket more than once. To correct this problem, I wrapped my bluetooth sockets in a special class to prevent the close method from being called more than once.

Be aware that closing the streams created by a bluetooth socket are capable of calling close on the socket. The following solves the problem.

public class CloseOnceBluetoothSocket 
{
private final BluetoothSocket mSocket;
private boolean mIsClosed;

public CloseOnceBluetoothSocket(BluetoothSocket socket)
{
    this.mSocket = socket;
}

public void connect() throws IOException
{
    mSocket.connect();
}

public InputStream getInputStream() throws IOException
{
    return new FilterInputStream(mSocket.getInputStream()) {
        @Override
        public void close() throws IOException
        {
            CloseOnceBluetoothSocket.this.close();
        }
    };
}

public OutputStream getOutputStream() throws IOException
{
    return new FilterOutputStream(mSocket.getOutputStream()) {
        @Override
        public void close() throws IOException
        {
            CloseOnceBluetoothSocket.this.close();
        }
    };
}

public void close() throws IOException
{
    synchronized (mSocket) {
        if (!mIsClosed) {
            mSocket.close();
            mIsClosed = true;
        }
    }
}
}
Justin Breitfeller
  • 13,737
  • 4
  • 39
  • 47
  • Thanks Justin. I will keep your class because it might be useful in other situations. I replaced all pieces of my code with CloseOnceBluetoothSocket but unfortunately this did not make a difference. I am not having a problem with closing a bluetooth socket the problem is closing a bluetooth **server** socket. I am still getting this: `Process 9414 terminated by signal (11).` But I guess I must find a workaround like yours or decide that API 10 is too buggy for my situation and maybe a newer API would have solved this issue. Thanks anyways! – George Pligoropoulos Mar 01 '13 at 11:29
  • Have you implemented a similar solution for your server socket? – Justin Breitfeller Mar 01 '13 at 15:35
  • Yes! I already did it using just and if statement. But there is no problem of executing stop twice. The entire thing crashes on first call .. !! – George Pligoropoulos Mar 02 '13 at 09:46
  • CloseOnceBluetoothSocket caused the connectivity between two devices to be lost! I don't know if this gives you any hint – George Pligoropoulos Mar 04 '13 at 22:23
  • Well the code doesn't contain any significant magic. All it does is ensure close isn't called twice. I would suspect your connectivity problem stems from somewhere else – Justin Breitfeller Mar 04 '13 at 22:35
2

If closing the socket causes that much damage then why not just create a boolean flag that is set to true when the user is connected and set to false when the user disconnects; then only call close when the user was previously connected.

Jeremy
  • 771
  • 6
  • 15
  • Your workaround is ok but have you tested it and experienced the same problem as me? – George Pligoropoulos Feb 24 '13 at 02:14
  • No I have not see the same issue that you are describing and I have not tested it but it just makes sense to only close the socket if it was previously opened. Agree? – Jeremy Feb 24 '13 at 04:45
  • The socket is indeed opened but it is just not connected (not accepted yet).. And If I remember correctly from other readings in the internet if you leave an listening bluetooth server socket open you spend lots of battery. One more reason is that listening sockets are not infinite. So opening a new socket ~20 times is enough to get an exception – George Pligoropoulos Feb 24 '13 at 09:46
  • Apologies, what I meant was only close the socket if the user was previously connected. – Jeremy Feb 24 '13 at 16:05
0

I think I have found a workaround for this issue. The "I think" is because this has not been tested on many devices yet.

code below is Scala

For the workaround I take advantage of the overloaded method accept(int) which has a timeout

So we have a variable for the state of the infinite loop you are seeing below

private var toContinue = true;

We just repeat accepting in a while loop

while (toContinue) {
  try {
    //this is a blocking call and will only return on a successful connection or an exception, or on timeout
    val socket = bluetoothServerSocket.accept(10000); //msec

    connectionAccepted(socket);
  } catch {
    case e: IOException => {
      Logger("accepting timed out");
    }
  }
}

So now instead of calling the bluetoothServerSocket.close() we are just setting variable to false

def cancel() {
    toContinue = false;
}

Actual code is a little bit more complex since you need a callback function to do something upon the exit of the while loop but the main issue is resolved as shown above

George Pligoropoulos
  • 2,919
  • 3
  • 33
  • 65