7

One part of my application connects to a device through Bluetooth and normally works fine but occasionally it won't connect and I get the following error

03-11 10:29:20.328: E/BluetoothComService(8059): accept() failed
03-11 10:29:20.328: E/BluetoothComService(8059): java.io.IOException: Operation Canceled
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothSocket.acceptNative(Native Method)
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothSocket.accept(BluetoothSocket.java:316)
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothServerSocket.accept(BluetoothServerSocket.java:105)
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothServerSocket.accept(BluetoothServerSocket.java:91)
03-11 10:29:20.328: E/BluetoothComService(8059):    at com.mypackage.name.bluetooth.BluetoothService$AcceptThread.run(BluetoothService.java:298)

This is the line where I get the exception

socket = mmServerSocket.accept();    

And this is the complete AcceptThread

private class AcceptThread extends Thread {
    // The local server socket
    private BluetoothServerSocket mmServerSocket;
    public boolean successInit = false;

    public AcceptThread() {
        closeAllConnections();

        /*
         * if(mmServerSocket != null) { try { mmServerSocket.close(); } catch
         * (IOException e) { e.printStackTrace(); } }
         */
        BluetoothServerSocket tmp = null;

        // Create a new listening server socket
        while (!successInit) {
            try {
                tmp = mAdapter
                        .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);

                successInit = true;
            } catch (Exception e) {

                successInit = false;
            }
        }

        /*
         * try { tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,
         * MY_UUID); successInit= true; } catch (IOException e) { Log.e(TAG,
         * "listen() failed", e); tmp = null; successInit = false; }
         */
        mmServerSocket = tmp;
    }

    public void run() {
        if (D)
            Log.d(TAG, "BEGIN mAcceptThread" + this);
        setName("AcceptThread");
        BluetoothSocket socket = null;

        // Listen to the server socket if we're not connected
        while (mState != STATE_CONNECTED) {
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                mAdapter.cancelDiscovery();

                socket = mmServerSocket.accept();
            } catch (IOException e) {
                Log.e(TAG, "accept() failed", e);
                Log.e("Error", "This isn't connecting");
                break;
            }

            // If a connection was accepted
            if (socket != null) {
                synchronized (BluetoothService.this) {
                    switch (mState) {
                    case STATE_LISTEN:
                    case STATE_CONNECTING:
                        // Situation normal. Start the connected thread.
                        connected(socket, socket.getRemoteDevice());
                        break;
                    case STATE_NONE:
                    case STATE_CONNECTED:
                        // Either not ready or already connected. Terminate new
                        // socket.
                        try {
                            socket.close();
                        } catch (IOException e) {
                            Log.e(TAG, "Could not close unwanted socket", e);
                        }
                        break;
                    }
                }
            }
        }
        if (D)
            Log.i(TAG, "END mAcceptThread");
    }

    public void cancel() {
        if (D)
            Log.d(TAG, "cancel " + this);
        try {
            mmServerSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "close() of server failed", e);
        }
    }
}     

Here is the function I call at the beginning of AcceptThread in hopes to close everything to restart it

public void closeAllConnections() {
    if (mmInStream != null) {
        try {mmInStream.close();}
        catch  (Exception e){Log.e(TAG, "close() of connect socket failed", e);}
    }
    if (mmOutStream != null) {
        try {mmOutStream.close();}
        catch (Exception e){Log.e(TAG, "close() of connect socket failed", e);}
    }
    if (mmSocket != null) {
        try {
            mmSocket.close();
            //mmSocket.connect();
        }
        catch (IOException e) {
            Log.e(TAG, "close() of connect socket failed", e);
        }
    }
}

I've read through the Bluetooth Docs and SO questions but I haven't found anything that works for me and it gets a bit confusing for me as this is my first time connecting through BT.

Note

The only "fix" I have found when this happens is to turn off the BT adapter, force close the program, restart BT adapter and restart app, which is not good for obvious reasons. I tried restarting the adapter programmatically but I still can't connect.

Can anyone see what might be wrong in my BlutoothService class, which is where AcceptThread is located? Or how I would go about resolving this issue? Thanks!

Update

It does, in fact, seem like the connection is sometimes closed on one Thread and trying to reconnect on another. The problem is that I can't figure out what would cause it to try and connect on a separate Thread or how to fix it when this happens.

The only way I can successfully reproduce this is if my BT device is turned off then I turn the BT adapter off. When I turn everything back on then I get the exception and cannot connect. I have customers that it happens to randomly and periodically so I'm hoping the issues are related.

codeMagic
  • 44,549
  • 13
  • 77
  • 93
  • Which device, OS ? Does the same behaviour occur on multiple devices? – Pararth Jul 16 '14 at 04:22
  • @user2450263 devices and Oss I've tried are Nexus 7, Galaxy Tab 3 (7"), JB, and ICS. I'm not currently having the issue but I haven't posted an answer yet because part of it was a hardware issue and the software-side I haven't taken the time to sort through and post the actual fix. – codeMagic Jul 16 '14 at 13:41
  • thanks for that info, do post the solution later, bluetooth issues have been many, i asked about devices as discovered many os and device specific bluetooth issues after diving into BLE related development – Pararth Jul 16 '14 at 13:46
  • @user2450263 I will try to post something soon in case it can be helpful. I have noticed the same. BT problems can differ between devices and OS which gets very frustrating. – codeMagic Jul 16 '14 at 13:48
  • The newer android OS versions include an update for bluetooth. If there is no active bluetooth communication for certain time, it turns off BT. The only way out is to restart adapter after this happens. Check if similar is happening in your case. – Ashwini Shahapurkar Jul 21 '14 at 08:19
  • @user2450263 I have posted an answer in case it might be useful to you or someone else. I will add more details later if I come up with any – codeMagic Jul 23 '14 at 19:20
  • Thanks, yes it would surely be a help – Pararth Jul 23 '14 at 23:46

3 Answers3

3

Well, part of my problem was a hardware issue that was found out to be a problem on the third-party manufacturers end. They're firmware wasn't quite right and when it was reading the BT address, it was occasionally being corrupted.

On the software side, it was running the AcceptThread in two separate Threads periodically. What I did to fix that was to create a function to close the socket and input/output streams...

public void closeAllConnections()
    {
        if (mmInStream != null)
        {
            try {mmInStream.close();}
            catch  (Exception e){Log.e(TAG, "close() of connect socket failed", e);}
        }
        if (mmOutStream != null)
        {
            try {mmOutStream.close();}
            catch (Exception e){Log.e(TAG, "close() of connect socket failed", e);}
        }
        if (mmSocket != null)
        {
          try {
                mmSocket.close();
                Log.e("TAG", "close() of connect socket successfu;");
              } catch (IOException e) {
                   Log.e("TAG", "close() of connect socket failed", e);}
        }

Then in the BluetoothCom class, I noticed it wasn't always checking for the BT object to be null before trying to restart the service.

private void setupService() {

    // Initialize the BluetoothChatService to perform bluetooth connections
    if((mChatService != null) && (mChatService.getState() != 0)) {
        mChatService.stop();        
    }

    // I didn't have this so it would always start a new instance of the Service
    if (mChatService == null)
        mChatService = new BluetoothService(mHandler);

    mChatService.start();
}

This seems to have helped and I no longer have those problems. However, now testing on the Samsung Galaxy Tab 4 and I am once again having connection issues but only on this device. Maybe this information can help someone and I will update my answer if I figure anything else out with the new problem.

Note: As stated above, this Bluetooth app uses modified code from Android's BluetoothChat app.

Also, I have read (and noticed) that different manufacturers implement the BT stack differently which can lead to headaches (at least if yo don't know enough about it).

codeMagic
  • 44,549
  • 13
  • 77
  • 93
1

Although this is old post but I recently contact same issue so I want to write down the way I solve it.

It seems your code is from Google samples BlutoothChat (as it looks same, sorry if I misunderstand). I also create my own application that base on this sample (on API level 10). I meet the accept() fail issues if I try to connect one device to other device but at the end I solve this question by simply remove some code in MainActivty

On the Google samples main activity it contain many methods when activity change (Start, Pause, etc).

Original code have this

@Override
public synchronized void onResume() {
    super.onResume();
    if(D) Log.e(TAG, "+ ON RESUME +");
    if (mChatService != null) {
        if (mChatService.getState() == ChatService.STATE_NONE) {
          mChatService.start();
        }
    }
}

This code start the Chat Service and running the AcceptThread to listening incoming connections.

When application start, this method will be call "once" and create the AcceptThread. If you do any other things that make the main activity onPause() pause (In Google samples case, if you click menu to start device_list activity the main activity will pause), when the application back to main activity it will call create AcceptThread method "one more time", this cause the problem because one thread already running but you try to interrupt it. And at the end happen accept() fail error and throw java.io.IOException: Operation Canceled error.

So to avoid this is simply remove the codes in OnResume()

@Override
public synchronized void onResume() {
    super.onResume();
    if(D) Log.e(TAG, "+ ON RESUME +");
}

or if you don't want delete any codes because you afraid cause some problem, put this code (mChatService != null) mChatService.stop(); in...

@Override
public synchronized void onPause() {
    super.onPause();
    if (mChatService != null) mChatService.stop();
    if(D) Log.e(TAG, "- ON PAUSE -");
}

Both works perfect in my project. Method 1 not create the new thread if activity resume, and Method 2 kill all the thread if you leave the current activity and disconnect all current connections if you already have one (it will start the thread again once you turn back). Method 1 wouldn't return any error but method 2 will throw accept fail again if you leave the current activity, so I suggest to use method 1.

Need to notice that this error usually happen after you modify Google samples BlutoothChat, it will never appear on the original app.

I have seen many post talk about this issue, but not see any one come out with this answer, so just want to share this. Hope this is helpful.

Shawn Lien
  • 461
  • 3
  • 8
  • 20
  • Thanks for the reply and, yes, it is based off of the Google Chat example. It was already put into place when I started so now I get to find and fix the bugs since I am now the lead developer. I think I have "solved" the issue for now but not exactly sure which part solved it since I've made so many changes. We have a static Globals class that holds a reference to the `BluetoothCom` class which is used to interface with the `BluetoothService` class so I'm wondering if that has anything to do with it. I am also running into a problem that I need to access the bluetooth device multiple times – codeMagic Apr 07 '13 at 16:03
  • And when I try I run into problems because it is trying to read from the device for two different things. I have to get a battery reading from the bluetooth device and have it poll for a temperature reading. Anyway, thanks again for the reply and I will look at it some more when I have time and let you know if it helped – codeMagic Apr 07 '13 at 16:04
  • @ShawnLien I tried your solution but it does not works for me sometimes in rare case it works otherwise not works.If possible than paste your code at paste.org – Khan Aug 29 '13 at 12:19
0

That exception should occur when the BluetoothServerSocket is closed or garbage collected. I suspect that the exception is happening on an OLD copy of the thread. So something like: When you create the new thread the old thread gets cancelled and thus the BluetoothServerSocket is closed and thus accept correctly fails with that error. Check this in the debugger and/or logging on which thread the various events occur; e.g. set breakpoints on the line after accept and perhaps on the cancel function, then inspect the thread IDs there -- is the exception occurring on a previous thread?

alanjmcf
  • 3,430
  • 1
  • 17
  • 14
  • It does look like this may be happening but the app freezes when debugging while I'm trying to figure out what thread is being used for what. I do see two `AcceptThread`s being created. Can you suggest how to go about ensuring it uses the correct `Thread`? – codeMagic Mar 11 '13 at 17:28