BluetoothSocket - connection timeout - android

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.

Related

Android 7 (?) ignoring timeout in BluetoothServerSocket.accept

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.

In documentation, why is BluetoothServerSocket.accept() called in a while loop if it blocks the thread anyway?

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.

Must I call Looper.prepare() in a thread?

I've searched for the relationship between thread and looper, and I just know what the two words's literal meaning now. When come to specific conditions, I'm still a little confused.
I came across this issue when I try to build an app about communicating with a bluetooth device. I got problems in the connect thread.
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
MY_UUID_INSECURE);
}
} catch (IOException e) {
tip("Socket Type: " + mSocketType + "create() failed");
}
mmSocket = tmp;
}
public void run() {
Looper.prepare();
setName("ConnectThread" + mSocketType);
// Always cancel discovery because it will slow down a connection
if(mBluetoothAdapter.isDiscovering())
mBluetoothAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
e2.printStackTrace();
}
connectionFailed();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothThreads.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice, mSocketType);
Looper.loop();
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
}
}
this code is download from the Android Developers's sample. and the Looper.prepare(), Looper.loop() are added by myself. Withou calling these two methods, the app will crash down. And I got a warning from the android studio:Can't create handler inside thread that has not called Looper.prepare(). That's why I add the two methods.
I want to ask, do I call the two methods in the right way?
Why I must call them while I have not used Toast or Handler as others do?
I want to ask, do I call the two methods in the right way?
Yes
Why I must call them while I have not used Toast or Handler as others do?
Looper.loop() and Looper().prepare() are used to create a MessageQueue and to handle this MessageQueue android recommends to use Handler , so if you use Looper.loop() and Looper.prepare() then you should use Handler also .
Well, finally I figure out what's wrong with my own project. There is nothing to do with the build.gradle, but thank Android Dev all the same.
Actually it's myself to blame. In the sample project BluetoothChat, methods like Log.d() is called to indicate the debug information. But I didn't know where to find Log.d()'s result, so I replaced them with Toast. I thought that would be fine. But I find out that Toast cannot be used in ConnectThread, otherwise it will throw an Exception:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
And I used Toast in method connectionFailed(), that's where the problem is.
Actually, you do not need Looper.prepare() and Looper.loop(). Those are only required when you would like to attach a Handler to the thread, which in your case, I cannot see such requirement.
Essentially, Once you call Looper.prepare(), It assigns a message queue to this thread such that all subsequent messages passed by handlers will be handled one by one in a queue manner.
Note: For debugging always use Log.d() / Log.w() / Log.e() and avoid using toasts.

killing asynctask when doInBackground hangs on a TCP Socket

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.

Creation of Socket instance blocks thread for random time if no connection available

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.

Categories

Resources