Android 7(?)忽略BluetoothServerSocket.accept中的超时

时间:2017-08-01 00:35:42

标签: android bluetooth

当我使用某个超时值调用BluetoothServerSocket.accept(...)时,运行Android 7.0的Alcatel A30只会忽略超时并永久阻止。这是Android中的新错误还是这款特定手机的愚蠢行为?

这是一个简单的代码部分,用于演示问题(只需将其粘贴到任何活动中并将<uses-permission android:name="android.permission.BLUETOOTH" />添加到您的清单中):

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();

预期输出(来自任何旧的Android设备;异常显示在1秒后):

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);

我的Android 7.0 Alcatel A30的输出:

Getting Bluetooth adapter...
Registering service profile...
Accepting connection with timeout...

然后它就坐在那里直到我杀了应用程序或直到我实际连接到服务,在这种情况下我得到了

`Accepted!`

即使在我连接之前等了好几分钟之后。

更新

似乎代码示例可能会造成一些混乱(Re:已删除的答案)。通常当有人在SO上发布异常时,他们正在寻求帮助以解决问题。这是我所追求的。通过设置超时然后不连接,我明确要求例外。问题是我在我的Android 7.0设备上获得例外。

2 个答案:

答案 0 :(得分:0)

似乎在接受连接时未使用超时。通过套接字发送或接收数据时(建立连接后),它用作超时。

通过超时调用BluetoothServerSocket.accept(...)最终以setOption(...)调用LocalSocketImpl中的SocketOptions.SO_TIMEOUT,而后者又使用setsockopt进行设置:

  

SO_RCVTIMEO::设置超时值,该值指定输入函数完成之前等待的最长时间。 ...如果接收操作阻塞了这么长时间却没有接收到其他数据,那么如果没有接收到数据,它将以部分计数或errno设置为[EAGAIN]或[EWOULDBLOCK]返回。

     

SO_SNDTIMEO::设置超时值,以指定输出功能由于流控制阻止数据发送而阻塞的时间。如果这一次发送操作已被阻止,则它将返回一个部分计数,或者如果没有发送数据,则将errno设置为[EAGAIN]或[EWOULDBLOCK]。

答案 1 :(得分:0)

在与Android 7.0一起运行的设备上,我遇到了同样的问题。 其他设备(Android 8和9)具有预期的行为:如果接受未在其配置的超时时间内获得传入连接,则会引发IOException。

我最终要做的(不是很漂亮)是使用Timer从另一个线程关闭BluetoothServerSocket,这导致accept引发IOException,就像是超时一样。

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;
}

如果有人知道更好的解决方案,我很想听听!