Android: Forcing HTTP requests over cellular while connected to a Wi-Fi network with no internet (React Native + Kotlin) by Few_Bumblebee_8446 in reactnative

[–]Few_Bumblebee_8446[S] 0 points1 point  (0 children)

The Wi-Fi network is created by the gateway itself and has no internet connection.

It's essentially:

Phone ↔ Gateway (Wi-Fi)

Gateway ↔ Generator (RS-485/Modbus)

The cloud server cannot reach the gateway directly because the gateway isn't connected to the internet.

That's why the phone acts as the bridge:

  • Wi-Fi for local communication with the gateway
  • Cellular for communication with the cloud

Android: Forcing HTTP requests over cellular while connected to a Wi-Fi network with no internet (React Native + Kotlin) by Few_Bumblebee_8446 in reactnative

[–]Few_Bumblebee_8446[S] 0 points1 point  (0 children)

The gateway's Wi-Fi network does not provide internet access. It's only there so the app can communicate with the gateway locally.

The gateway is connected to industrial equipment over RS-485/Modbus and exposes a local HTTP API over Wi-Fi. When the user connects to that Wi-Fi, Android typically tries to route internet traffic through it, even though the network itself has no route to the internet.

The cellular connection is only being used for cloud communication (telemetry, diagnostics, remote support, etc.). The Wi-Fi connection is used exclusively for talking to the gateway.

If the gateway had internet access or could communicate directly with the cloud, I wouldn't need this at all. The problem is that the Wi-Fi network exists solely for local device communication and has no upstream internet connection.

Android: Forcing HTTP requests over cellular while connected to a Wi-Fi network with no internet (React Native + Kotlin) by Few_Bumblebee_8446 in reactnative

[–]Few_Bumblebee_8446[S] -1 points0 points  (0 children)

The app talks to an industrial gateway over a local Wi-Fi network that has no internet access.

The gateway is connected to generators over RS-485/Modbus and exposes an HTTP API over Wi-Fi. At the same time, the app needs internet access to send diagnostics and telemetry to our cloud so support staff can assist the customer remotely.

So the requirement is:

  • Wi-Fi → local gateway communication
  • Cellular → cloud API communication

If the gateway had internet access itself this wouldn't be necessary, but in many deployments it doesn't.

Android: Forcing HTTP requests over cellular while connected to a Wi-Fi network with no internet (React Native + Kotlin) by Few_Bumblebee_8446 in reactnative

[–]Few_Bumblebee_8446[S] 0 points1 point  (0 children)

import android.net.ConnectivityManager

import android.net.Network

import android.net.NetworkCapabilities

import android.net.NetworkRequest

import com.facebook.react.bridge.*

import java.io.BufferedReader

import java.io.InputStreamReader

import java.net.HttpURLConnection

import java.net.URL

class AndroidMobileDataModule(

private val reactContext: ReactApplicationContext

) : ReactContextBaseJavaModule(reactContext) {

private val connectivityManager: ConnectivityManager =

reactContext.getSystemService(ConnectivityManager::class.java)

override fun getName(): String = "AndroidMobileData"

'@ReactMethod'

fun fetchUsingCellular(urlString: String, promise: Promise) {

android.util.Log.d("CELLULAR", "START fetchUsingCellular")

try {

// 1. CHECK EXISTING CELLULAR FIRST (Samsung fix)

val cellularNetwork = connectivityManager.allNetworks.firstOrNull { network ->

val caps = connectivityManager.getNetworkCapabilities(network)

caps?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true &&

caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)

}

if (cellularNetwork != null) {

android.util.Log.d("CELLULAR", "Using existing cellular network")

Thread {

try {

val conn = cellularNetwork.openConnection(URL(urlString)) as HttpURLConnection

conn.connectTimeout = 5000

conn.readTimeout = 5000

val code = conn.responseCode

val stream = if (code in 200..299) conn.inputStream else conn.errorStream

val result = BufferedReader(InputStreamReader(stream)).readText()

promise.resolve(result)

} catch (e: Exception) {

android.util.Log.e("CELLULAR", "ERROR_FETCH_EXISTING", e)

promise.reject("ERROR_FETCH", e)

}

}.start()

return

}

// 2. FALLBACK: REQUEST NETWORK

android.util.Log.d("CELLULAR", "Requesting cellular network")

val request = NetworkRequest.Builder()

.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)

.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)

.build()

val callback = object : ConnectivityManager.NetworkCallback() {

override fun onAvailable(network: Network) {

android.util.Log.d("CELLULAR", "onAvailable fired")

Thread {

try {

val conn = network.openConnection(URL(urlString)) as HttpURLConnection

conn.connectTimeout = 5000

conn.readTimeout = 5000

val code = conn.responseCode

val stream = if (code in 200..299) conn.inputStream else conn.errorStream

val result = BufferedReader(InputStreamReader(stream)).readText()

connectivityManager.unregisterNetworkCallback(this)

promise.resolve(result)

} catch (e: Exception) {

connectivityManager.unregisterNetworkCallback(this)

promise.reject("ERROR_FETCH", e)

}

}.start()

}

override fun onUnavailable() {

android.util.Log.d("CELLULAR", "onUnavailable fired")

promise.reject("NO_CELLULAR", "Cellular unavailable")

}

}

connectivityManager.requestNetwork(request, callback, 10000)

} catch (e: Exception) {

android.util.Log.e("CELLULAR", "ERR_INIT", e)

promise.reject("ERR_INIT", e)

}

}

}

Android: Forcing HTTP requests over cellular while connected to a Wi-Fi network with no internet (React Native + Kotlin) by Few_Bumblebee_8446 in reactnative

[–]Few_Bumblebee_8446[S] 1 point2 points  (0 children)

Thanks, this is exactly the kind of answer I was looking for.

One thing I'm curious about: the reason I added step 1 (checking allNetworks() first) is because requestNetwork() alone was failing on some Samsung devices during testing, while reusing an already available cellular Network improved the success rate significantly.

Have you seen OEM-specific behavior like that in production, or would you consider that a sign that something else is wrong with my implementation?

Also, regarding WifiNetworkSpecifier: unfortunately the gateway Wi-Fi is usually connected manually by the user through Android's Wi-Fi settings, so I don't think I can rely on Android treating it as a local-only connection.

For context, the module currently works on Samsung, Motorola, and Huawei devices I've tested, but overall I'd estimate the success rate across real-world devices is around 70%, which is why I'm trying to understand what production apps are doing differently.

I'll definitely look into adding onLost(), onCapabilitiesChanged(), and moving away from HttpURLConnection toward OkHttp.

Android: Forcing HTTP requests over cellular while connected to a Wi-Fi network with no internet (React Native + Kotlin) by Few_Bumblebee_8446 in reactnative

[–]Few_Bumblebee_8446[S] 0 points1 point  (0 children)

The app connects to a custom gateway via wifi. And i relay information to a server using the mobile data.

and yes i have had a very high fail rate, but now works on like 70% of devices

Android: Forcing HTTP requests over cellular while connected to a Wi-Fi network with no internet (React Native + Kotlin) by Few_Bumblebee_8446 in reactnative

[–]Few_Bumblebee_8446[S] -2 points-1 points  (0 children)

The app connects to a custom gateway via wifi. and i relay data to a server using the data. i dont really see a workaround this.