Mohru
Mohru

Reputation: 743

HttpURLConnection.connect() fails after network reconnection on Android 10

My app connects to an external device using it's WiFi (the device works as a server). With introduction of Android 10 I needed to implement separate WiFi connectivity flow for different plaftorms (WifiNetworkSpecifier for Android 10+ and wifiManager.enableNetwork for < Android 10). The connectivity flow itself works fine, but I have some problems with stream communication.

In the app I have the ability to upload files to that external device. To do that I need to use HttpURLConnection. So I run:

val url = URL(UPDATE_FIRMWARE_URL)
val connection = (url.openConnection() as HttpURLConnection)
with(connection) {
    doInput = true
    doOutput = true
    useCaches = false
    requestMethod = METHOD_POST
    //setRequestProperty(HEADER_CONNECTION, "Keep-Alive")
    setRequestProperty("Connection", "close")
    connectTimeout = 6000
    setRequestProperty(HEADER_USER_AGENT, "Android Multipart HTTP Client 1.0")
    setRequestProperty(HEADER_CONTENT_TYPE, "multipart/form-data; boundary=$boundary")
}
connection.connect()

val outputStream = connection.outputStream
DataOutputStream(outputStream).use { outputStream ->
  // actual file upload
}

Now, the actual update consists of two files, and after first upload the device restarts, and I need to reconnect to it's wifi and upload the second file.

On Android < 9 the entire upload flow (with two files) works fine but on Android 10, after I send the first file and reconnect to the device's WiFi, when I call connection.connect() I get ConnectExcpetion with internal cause connect failed: ENETUNREACH (Network is unreachable) (which really makes no sense, cause I'm connected to that network...)

java.net.ConnectException: Failed to connect to (...)
        at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:1409)
        at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:1359)
        at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:221)
        at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:144)
        at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:106)
        at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:400)
        at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:333)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:483)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:135)

Initially I had a problem also with connecting for the first time on Android 10, but I found this article, and adding the connectTimeout helped, but now the connection still fails when I try to connect for the second (and every next) time. The only thing that helps is restaring the entire app (which is no real solution).

What may be the problem, that the next connections fail despite I always execute the same code?

Upvotes: 1

Views: 779

Answers (1)

Mohru
Mohru

Reputation: 743

After a few days I finally found an answer to my question. It turns out that on Android 10 when you connect to the Access Point that does not offer the internet (eg. my external device) the standard API calls (using Retrofit) works fine, but when trying to use HttpURLConnection the system tries to use some network with internet connection, and as there is none, the connection fails.

The only way for the connection to work is to force the system to use our network by using ConnectivityManager.bindProcessToNetwork(network). This solution was proposed here and I've got no idea why someone downvoted that answer. It's correct.

What's interesting is that if we connect to the no-internet network via device settings, the connection works just fine even without binding.

Upvotes: 2

Related Questions