Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video transferring on Android: cellular network affects socket read_buffer_size of WiFi connections #605

Open
pavlosharhan2 opened this issue Sep 19, 2024 · 0 comments
Labels
bug Something isn't working triage Needs to be reviewed and assigned

Comments

@pavlosharhan2
Copy link

pavlosharhan2 commented Sep 19, 2024

I think It's more an android related question, but is still, I would like to try to get some help if anyone has ideas

I am investigating an issue where Wi-Fi transfers between my android app and GoPro are inconsistent across different cellular network conditions. Initially, I noticed that GoPro Quik was able to transfer data efficiently even on 2G networks which wasn’t the case for my app. After some attempts digging into the Quik source code, I realized that Quik overrides OkHttp’s read_buffer_size to 512kb instead of the value okhttp uses depending on different quality of Cellular connection. Once I made the same adjustment in my app, it started transferring data well on 2G, which improved performance in those cases. However, this solution wasn’t perfect. Sometimes, transfers slowed down even on 4G, and I noticed that GoPro Quik also experienced slow transfer speeds in those instances. Interestingly, the speeds improved significantly once the cellular connection was disabled, suggesting there might be some interaction between the cellular and Wi-Fi networks affecting performance. I’m looking for ways of logging buffer sizes to investigate further and exploring how Android or the underlying Linux kernel might be influencing these behaviors.

Any suggestions or theories or where exactly the quality of cellular network may contribute are appreciated.

Here is my code for creating Okhttp client with a custom socket factory that overrides read_buffer_size:


class CustomBufferSocketFactory(
    private val sendBufferSizeBytes: Int,
    private val receiveBufferSizeBytes: Int,
    private val tcpNoDelayEnabled: Boolean = false,
    private val socketFactory: SocketFactory = SocketFactory.getDefault()
) : SocketFactory() {

    override fun createSocket(): Socket {
        return configureSocket(socketFactory.createSocket())
    }

    override fun createSocket(host: String, port: Int): Socket {
        return configureSocket(socketFactory.createSocket(host, port))
    }

    override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
        return configureSocket(socketFactory.createSocket(host, port, localHost, localPort))
    }

    override fun createSocket(address: InetAddress, port: Int): Socket {
        return configureSocket(socketFactory.createSocket(address, port))
    }

    override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
        return configureSocket(socketFactory.createSocket(address, port, localAddress, localPort))
    }

    private fun configureSocket(socket: Socket): Socket {
        socket.sendBufferSize = sendBufferSizeBytes
        socket.receiveBufferSize = receiveBufferSizeBytes
        socket.tcpNoDelay = tcpNoDelayEnabled
        return socket
    }
}



suspend fun connectToLocalNetwork(
    context: Context,
    ssid: String,
    password: String
): OkHttpClient? {
    return withTimeoutOrNull(15000L) { // Timeout after 15 seconds
        suspendCancellableCoroutine { continuation ->
            var isResumed = false

            val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

            // Use WifiNetworkSpecifier to build a request for the desired Wi-Fi network
            val wifiSpecifier = WifiNetworkSpecifier.Builder()
                .setSsid(ssid)
                .setWpa2Passphrase(password)
                .build()

            // Build the network request
            val networkRequest = NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .removeTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
                .setNetworkSpecifier(wifiSpecifier)
                .build()

            // Define the network callback
            val networkCallback = object : ConnectivityManager.NetworkCallback() {
                override fun onAvailable(network: Network) {
                    if (!isResumed && !continuation.isCancelled) {
                        isResumed = true

                        // Bind your app's network traffic to the specific network
                        connectivityManager.bindProcessToNetwork(network)

                        Logger.getLogger(OkHttpClient::class.java.name).setLevel(Level.FINE)

                        // Use OkHttpClientFactory for large file downloads
                        val client =
                            OkHttpClient.Builder()
                                .connectTimeout(60, TimeUnit.SECONDS)
                                .readTimeout(60, TimeUnit.SECONDS)
                                .writeTimeout(60, TimeUnit.SECONDS)
                                .socketFactory(CustomBufferSocketFactory(
                                    524288, 524288, true, network.socketFactory))
                                .build()

                        continuation.resume(client)
                    }
                }

                override fun onUnavailable() {
                    if (!isResumed && !continuation.isCancelled) {
                        isResumed = true
                        continuation.resume(null)
                    }
                }

                override fun onLost(network: Network) {
                    if (!isResumed && !continuation.isCancelled) {
                        isResumed = true
                        continuation.resume(null)
                    }
                }
            }

            connectivityManager.requestNetwork(networkRequest, networkCallback)
        }
    }
}

@pavlosharhan2 pavlosharhan2 added the bug Something isn't working label Sep 19, 2024
@github-actions github-actions bot added the triage Needs to be reviewed and assigned label Sep 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working triage Needs to be reviewed and assigned
Projects
None yet
Development

No branches or pull requests

1 participant