2

I'm having trouble with the case where a SocketChannel.connect times out. The code bellow works fine in my old Android 2.3 phone and my old Android 3.2 tablet but crashes in Android 4.2.2:



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvResult = (TextView) findViewById(R.id.tvResult);
        btnTest = (Button) this.findViewById(R.id.btnTest);
        btnTest.setOnClickListener(doTest);
    }

    protected View.OnClickListener doTest = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new AsyncTest().execute();
        }
    };

    private class AsyncTest extends AsyncTask {
        private String result = "";

        @Override
        protected Void doInBackground(Void... params) {
            InetSocketAddress addr = new InetSocketAddress("192.168.0.13", 4040);
            boolean bConnect = false;
            try {
                SocketChannel clntChan = SocketChannel.open();
                bConnect = clntChan.connect(addr);
                if (bConnect) {
                    clntChan.close();
                    result = "Connected";
                } else {
                    result = "Did not connected";
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception " + e.getMessage());
                result = "Exception " + e.getMessage();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void ret) {
            tvResult.setText (result);
        }
    }


I am testing the case where there is no one is listening on port 4040 at 192.168.0.13. In Android 2.3 the try/catch will catch a "Connection timeout" exception (as expected). In Android 4.x I get the following:


    FATAL EXCEPTION: AsyncTask #1
    java.lang.RuntimeException: An error occured while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:299)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
        at java.util.concurrent.FutureTask.run(FutureTask.java:239)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
        at java.lang.Thread.run(Thread.java:838)
    Caused by: java.lang.AssertionError: java.net.SocketTimeoutException: failed to connect to /192.168.0.13 (port 4040) after 90000ms
        at libcore.io.IoBridge.connect(IoBridge.java:102)
        at java.nio.SocketChannelImpl.connect(SocketChannelImpl.java:177)
        at br.com.dqsoft.testeconnect.MainActivity$AsyncTest.doInBackground(MainActivity.java:63)
        at br.com.dqsoft.testeconnect.MainActivity$AsyncTest.doInBackground(MainActivity.java:1)
        at android.os.AsyncTask$2.call(AsyncTask.java:287)
        at java.util.concurrent.FutureTask.run(FutureTask.java:234)
        ... 4 more
    Caused by: java.net.SocketTimeoutException: failed to connect to /192.168.0.13 (port 4040) after 90000ms
        at libcore.io.IoBridge.connectErrno(IoBridge.java:176)
        at libcore.io.IoBridge.connect(IoBridge.java:112)
        at libcore.io.IoBridge.connect(IoBridge.java:100)
        ... 9 more

A could not find a way to catch this exception, the app is closed by the OS. While a timeout is not a normal condition, I should be able to detect that and present an error message to the user instead of just crashing.

The case where the connect succeeds works fine in all the devices.

Edit: The initial paragraph erroneously mentioned Android 2.4 instead of 4.2.2.

Edit: Simplified the code as suggest by Jarred and added additional information.

Update: Tested on a Android 4.4.4 device and it worked. I will close this as a bug of Android 4.2.2 (maybe on this specific device).

dquadros
  • 43
  • 6
  • Try catching the aforementioned exception that is causing the error: `catch (SocketTimeoutException e)` – Willis Apr 28 '15 at 21:09
  • @Willis I've tried that without success. Not surprising, since SocketTimeoutException is a descendant of IOException. – dquadros Apr 28 '15 at 22:53
  • @dquadros Can you please try my answer. – Jared Burrows Apr 28 '15 at 23:55
  • I had a similar (equally absurd) issue http://stackoverflow.com/questions/29825330/why-a-socket-is-not-instanceof-closeable-at-runtime and the reason was that they have changed the class/interface hierarchy but did not reflect that in the docs. I suggest to set the target_API=minimal_API=API_of_the_device and see if it works. Two more options are to catch throwable and to set an unhandled exception handler. – 18446744073709551615 Apr 29 '15 at 12:00

1 Answers1

0

Remove most of this:

You are starting a Thread from a Thread form another Thread. Just start a single Thread from your Main Thread, using your AsyncTask.

protected View.OnClickListener doTest = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(new threadTest()).start();
    }
};

private class threadTest implements Runnable {
    @Override
    public void run() {
        try {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    new AsyncTest().execute();
                }
            });
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }
}

Change it to:

protected View.OnClickListener doTest = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new AsyncTest().execute();
    }
};

For the Exception and Timeout:

IOException is the parent class of SocketTimeoutException, so your AsyncTask code should work. When in doubt, you can catch everything by using Exception.

Other things to check:

  • Can you ping 192.168.0.13?
  • How do you know that port is open? Try telnet or netcat.
  • Do you have a service running and configured correctly on the port?
  • Do you have the correct Android Permissions?
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
  • the main point is that the code does not work when the connect times out. As mentioned in the question, I am purposely testing a closed port. The app has INTERNET permission, leaving that out gives a very specific exception. Regarding the superfluous tasks, they come from the original code I (grossly) simplified. I will try your version, but it should not be related to the problem of crashing instead of catching the timeout exception. – dquadros Apr 29 '15 at 00:03
  • I have addressed you main point in my answer, with new simplified code, and I suggested using Exception instead. The way the threads are being called will surely cause you an issue in the future as well. – Jared Burrows Apr 29 '15 at 00:11
  • I will try your code tomorrow, I've already tried using Exception with the same result. Don't worry, I will inform the results here. I liked the simplified code, if there is no new issue but the problem continues I will update the question to focus on the main problem. The original code has been running for years in tens of devices with Android 2.x and 3.x (but the timeout case probably has not occurred many times). – dquadros Apr 29 '15 at 00:30
  • Very rare to ever see a 3.x device. Pretty soon you will be able to target 4.0+ anyways because 2.x is very rare as it is so old. – Jared Burrows Apr 29 '15 at 00:34
  • @Jarred-Burrows As noted on the updated question, the problem continued with the simplified (and better) code. Yes, 3.x is rare and 2.x is pretty old, but this an old app and my users are still using them. But they want to start using 4.x devices, and that is what started all this. Anyway, the problem seems to be specific to this Android version and I will follow your suggestion for the coding. Thanks for the help! – dquadros Apr 29 '15 at 11:39
  • It can't be Java, maybe it is device specific. What devices are you using? – Jared Burrows Apr 29 '15 at 12:13