When I call BluetoothServerSocket.accept(...) with some timeout value, my Alcatel A30 running Android 7.0 just ignores the timeout and blocks forever. Is this a new bug in Android or some stupidity with this particular phone?
Here's a simple code-section to demonstrate the problem (just paste this into any activity and add <uses-permission android:name="android.permission.BLUETOOTH" /> to your manifest):
Thread test = new Thread() {
#Override
public void run() {
try {
System.out.println("Getting Bluetooth adapter...");
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
System.out.println("Registering service profile...");
BluetoothServerSocket server = bt.listenUsingRfcommWithServiceRecord
("Test", UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
System.out.println("Accepting connection with timeout...");
server.accept(1000); // Android 7.0 gets stuck here rather than timing out
System.out.println("Accepted!");
} catch (Exception e) {
System.err.println("Got an error:");
e.printStackTrace();
}
}
};
test.setDaemon(true);
test.start();
Expected output (from any of my older Android devices; exception shows up after 1s):
Getting Bluetooth adapter...
Registering service profile...
Accepting connection with timeout...
Got an error:
java.io.IOException: Connection timed out
at android.bluetooth.BluetoothSocket.acceptNative(Native Method)
at android.bluetooth.BluetoothSocket.accept(BluetoothSocket.java:364)
at android.bluetooth.BluetoothServerSocket.accept(BluetoothServerSocket.java:113)
at line containing server.accept(1000);
Output from my Android 7.0 Alcatel A30:
Getting Bluetooth adapter...
Registering service profile...
Accepting connection with timeout...
And then it sits there until I kill the app or until I actually connect to the service, in which case I get
`Accepted!`
even after many minutes of waiting before I connect.
Update:
It seems like the code-sample is maybe creating some confusion (Re: deleted answer). Usually when someone posts an exception on SO, they are looking for help how to fix it. This is not what I'm after. By setting the timeout and then not connecting, I am explicitly asking for the exception. The problem is that I am not getting the exception on my Android 7.0 device.
I have the same problem with a device that's running with Android 7.0.
Other devices (Android 8 and 9) have the expected behavior : a IOException is raised when the accept did not get an incoming connection within its configured timeout.
What I ended up doing (and it's not very pretty) is using a Timer to close the BluetoothServerSocket from another thread, this caused accept to raise the IOException as though it was a timeout.
BluetoothServerSocket mBssOutCom = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("blabla", uuid);
Timer securityTimer = new Timer();
try {
//! there seems to be a bug in android 7.0
//! that makes the connect timeout never fire. This timer will do the dirty trick
//! Should only have an effect if the bug is active
securityTimer.schedule(new TimerTask() {
#Override
public void run() {
// this code will be executed after 2 seconds
if (mBssOutCom != null) {
try {
mBssOutCom.close();
} catch (IOException e) {
e.printStackTrace();
}
mBssOutCom = null;
}
}
}, 1500);
socketCom = mBssOutCom.accept(1000);
} catch (IOException e) {
Log.d(TAG, "tryToConnect: accept timedOut");
}
securityTimer.cancel();
if (mBssOutCom != null) {
mBssOutCom.close();
mBssOutCom = null;
}
If anyone is aware of a better solution, I'd love to hear it!
It looks like the timeout isn't used when accepting connections. It is used as the timeout when sending or receiving data through the socket (after the connection has been made).
Calling BluetoothServerSocket.accept(...) with a timeout ends up calling the setOption(...) in LocalSocketImpl with SocketOptions.SO_TIMEOUT, which in turn uses setsockopt to set:
SO_RCVTIMEO: Sets the timeout value that specifies the maximum amount of time an input function waits until it completes. ... If a receive operation has blocked for this much time without receiving additional data, it shall return with a partial count or errno set to [EAGAIN] or [EWOULDBLOCK] if no data is received.
SO_SNDTIMEO: Sets the timeout value specifying the amount of time that an output function blocks because flow control prevents data from being sent. If a send operation has blocked for this time, it shall return with a partial count or with errno set to [EAGAIN] or [EWOULDBLOCK] if no data is sent.
Related
It's there any way to set connection timeout to BluetoothSocket?
If my device is offline, connection process takes a few seconds and then returns an error. I need to set the timeout to 1 second. It is possible?
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(APP_UUID);
// socket.setProxyConnectionTimeout(1000); <- some like this
socket.connect();
BluetoothSocket class have PROXY_CONNECTION_TIMEOUT, but never used...
Thanks for answers.
BTW:
I try some like this:
socket.connect();
Thread.sleep(1000);
socket.close(); // but socket is not closed & still connecting
You can't change timeout of BluetoothSocket.connect(). As documentation:
This method will block until a connection is made or the connection fails. If this method returns without an exception then this socket is now connected.
A workaround.
Ex: timeout 5s. Using CountDownTimer to check if connect is complete(success or fail). After 5s, if connection is incomplete then use BluetoothSocket.close() to cancel.
As BluetoothSocket documentation:
close() can be used to abort this call from another thread.
Ok this code is only slightly tested but it works so far:
Task.Run(() =>
{
while (true)
{
socket = pairedBTDevice.CreateRfcommSocketToServiceRecord(UUID.FromString(uuid));
socket.ConnectAsync();
Thread.Sleep(1000);
if (socket.IsConnected)
{
// call a function here
// my function blocks for the application lifetime
return;
}
else
{
socket.Close();
}
}
});
I hope this helps.
socket.connect();
This method will block until a connection is made or the connection
so.if connect could not establish.
Thread.sleep(1000);
Would not excute.
Or other method behind socket.connect(); would not execte too.
In the android documentation, the following code occurs in the run() segment of a thread:
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(socket);
mmServerSocket.close();
break;
}
}
However, the accept() method blocks the thread. I therefore do not understand why a while() loop is needed, especially since in all possible situations the while loop is broken in its first run.
Any ideas?
Normally there wouldn't be a break after accepting and processing one socket: you would loop accepting sockets indefinitely.
It's a stupid example.
I modified the standard Bluetoothchat example to send 4 bytes of data at a time to a bluetooth device every half a second. It works fine if I start the App fresh. However, there is a problem if I reconnect as follows:
While Bluetooth is connected, I click the connect button again on the menu and select the same device. This disconnects the bluetooth (not sure whether this is the right procedure to disconnect). Then, I connect again by selecting the device, and it will be reconnected. After reconnection, a very strange problem appears: instead of sending the data every half a second, it will send the data every quarter a second. If I go through the process again and reconnect, the time interval will become even shorter. It gets to a point that the bluetooth device on the receiving end can't keep up with the data. At this point, the only way out is to kill the app and restart again. Then everything becomes normal, till next time I try to reconnect again.
I have tried different things but nothing appear to fix this. For example, I made sure the thread sending the data is killed when disconnected so no multiple threads are sending the data. I was wondering whether the baud rate changed when reconnected, but then why would the baud rate affect the Thread.sleep(500); statement (which is responsible for controlling the half a second data send). Any help would be greatly appreciated.
Here is the code, the SendClass is created under the MainActivity:
class SendClass implements Runnable {
public void run() {
bytearr[0]=0;bytearr[1]=0;bytearr[2]=0;bytearr[3]=0;
while (!Thread.currentThread().isInterrupted()) {
if (mChatService==null || mChatService.getState()
!=BluetoothChatService.STATE_CONNECTED) {
continue;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mChatService.write(bytearr);
}
}//end of run
}//end of runnable
Then under STATE_CONNECTED:
case BluetoothChatService.STATE_CONNECTED:
setStatus(getString(R.string.title_connected_to,mConnectedDeviceName));
/*
if(sendingThread!=null){
//sendingThread.stop();
sendingThread.interrupt();
if(D) Log.i(TAG, "after sendingThread");
sendingThread = null;
}*/
sendingThread = new Thread(new SendClass());
sendingThread.start();
break;
As you can see, I tried to kill the thread before creating a new one but that didn't make any difference. Any suggestions?
You are creating a thread that never actually stops, even after you create a new thread and assign to the same variable that particular thread wont stop running.
You need to make sure that the thread will stop after it disconnects.
Here is my suggestion
Change your SendClass to:
class SendClass implements Runnable {
private boolean stopped = false;
public void setStopped(boolean s){
this.stopped = s;
}
public void run() {
bytearr[0]=0;bytearr[1]=0;bytearr[2]=0;bytearr[3]=0;
while (!Thread.currentThread().isInterrupted() && !stopped) {
if (mChatService==null || mChatService.getState() !=BluetoothChatService.STATE_CONNECTED) {
continue;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mChatService.write(bytearr);
}
}//end of run
}//end of runnable
Then when you start your thread keep the reference to the Runnable so you can call the setStopped(true); like this
SendClass sc = new SendClass();
sendingThread = new Thread(sc);
sendingThread.start();
When you disconnect the bluetooth dont forget to call sc.setStopped(true); so your thread will finish by not going into the while.
IPStringI have a fairly simple Activity which works as follows:
in the onCreate, I call an AsyncTask TCP Socket connection to a remote server over a proprietary port, send a quick ASCII command to the server and process the response via the onPostExecute(). Works great, it's fast and functional.
However, if the remote server is down -- or I mistakenly entered in the wrong IP address for the communication -- my AsyncTask will hang with the "logging in" dialog spinning on the screen for as long as it takes for the Socket to timeout.
I've been digging away at this for the last 2 days trying to figure out how to call the cancel(), but am failing. Any suggestions?
Here's the current code:
public class Scratchpad extends AsyncTask<Object, String, String>{
private String returningResponse;
private volatile Socket socket;
#Override
protected void onPreExecute(){
//throw the "Logging In" dialog up
initializeDialog();
super.onPreExecute();
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String result) {
//send the ASCII result off to the function that parses through it
loginTriggerResult(result);
super.onPostExecute(result);
}
#Override
protected String doInBackground(Object... params) {
/*
* bunch of stuff goes here to strip out the proper
* values from the Object for IP address, Port number etc.
* params[0], params[1] etc.
*
*/
InetAddress serverIP = null;
String IPString = (String) params[1];
int portnumber = (Integer) params[2];
//convert the String "IPString" into an InetAddress
try {
serverIP = InetAddress.getByName(IPString);
} catch (UnknownHostException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//and then, bingo bango bongo, make the connection with the 'try':
try{
socket = new Socket(serverIP.getHostAddress(), portnumber);
//I tried this too, forcing the timeout... It crashes immediately.
//SocketAddress socketAddress = new InetSocketAddress(serverIP.getHostAddress(), portnumber);
//socket.connect(socketAddress, 3000);
Log.d("networking","Connection Completed");
//bunch of stuff goes here that happens AFTER the solid connection,
//which we never get to if the Socket fails on a "waiting to connect" thing.
//reader.close();
//outputStream.close();
}catch (SocketException e){
e.printStackTrace();
Log.w("socket","socket closed with SocketException");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.w("socket","socket closed with IOException");
}
return returningResponse; //postExecute will handle this.
}
}
I'm not getting any of the Log messages until the 3 or 4 minutes pass that the socket times out.
I'm thinking there must be a way to run a TimerTask or something OUTSIDE of the AsyncTask to track how long it's taking to make the connection (or not) and then somehow "cancel()" the running 'try' inside the AsyncTask, but I've been failing on numerous styles.
This HAS to be something that's commonly used, and there's gotta be an easy solution?
UPDATE:
The path I was heading down was trying to use the socket connection from within AsyncTask to "monitor itself", and if/when it timed out without getting a solid connection -- trigger the cancel() of the AsyncTask.
I was trying to do this from within the AsyncTask itself, with this.cancel() inside the IOException catch of the Socket timeout.
The solution so far is to implement a timer outside of the AsyncTask that launches at the same time as the AsyncTask, and call the cancel from there after however many seconds have transpired.
This solution does gracefully exit from the connection attempt, and I can put any sort of Dialog triggers or Toasts inside the onCancelled() stanza of the AsyncTask.
It doesn't crash anymore, BUT: the socket is still trying to connect in the background, even though the task is cancelled.
It definitely seems like the simplest solution (which you have tried) would be to set the timeout when calling connect. Can you post the stack trace generated when the connect with timeout fails?
What exactly is it are you trying to do? Detect when you've entered the wrong IP address or the server is down? Only show logging in for a certain amount of time? I get that you're asking how to cancel your async task, but that doesn't seem to be the root of the problem you're trying to solve.
I think MisterSquonnk is exactly right, you should just be able to set the timeout duration when call connect. You may have something else broken in your code if that timeout doesn't work. Alternatively if you just want something that lets you do your own timeout without having anything to do with socket, see below:
If you want to assume you're connection has failed in a shorter amount of time than the socket takes to time out, start a new CountDownTimer http://developer.android.com/reference/android/os/CountDownTimer.html at the same time and in the same place in your activity as you start your async task with whatever you want the timeout duration to be.
After the timer runs out (and onFinish in the timer is called), call cancel on the async task, or otherwise prevent onPostExecute from executing, and do whatever you want to do when a login attempt fails in your UI. Similarly if the login attempt succeeds before the timer runs out, cancel the timer or otherwise prevent onFinish from being called.
Trying to open socket connection in separate thread by calling this:
Socket sc = sc = new Socket(address, Integer. parseInt(port));
But there is a problem, if there is no active internet connection this thread is blocking, on my htc hero 2.1 for 30 seconds, and my android emulator 2.2 for 2 minutes.
I tryed to use this method before opening connection:
public static boolean isOnline()
{
ConnectivityManager cm = (ConnectivityManager) app.getInstance().getSystemService(Context. CONNECTIVITY_SERVICE);
if(null != cm.getActiveNetworkInfo())
return cm.getActiveNetworkInfo().isConnectedOrConnecting();
return false ;
}
public void run()
{
if(!isOnline())
throw new IOException("internet not connected");
Socket sc = new Socket(address, Integer. parseInt(port));
}
And everything is good, But if active connection will lost after isOnline was called, and before new Socket call, its still blocks for random time.
My question is there any possibility to solve that problem? or just block this thread for fixed time for about 2-3 seconds?
I suggest calling thread.interrupt() after a timeout.
You might also want to get the Android source and see where it's spending all that time. It might give you some insight on other approaches to dealing with this.