Reputation: 12742
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.
Upvotes: 3
Views: 834
Reputation: 73
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!
Upvotes: 1
Reputation: 1
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.
Upvotes: 0