Send request over WiFi (without connection) even if Mobile data is ON (with connection) on Android M
Asked Answered
I

2

22

I have to send UDP packets to a WiFi module (provided with own AP) with no internet connection but when I connect the mobile with the AP, Android redirects my packets on the mobile data interface because it has got internet connection.

I've used the code below to do my job but it seems not working on Android M.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setWifiInterfaceAsDefault() {
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkRequest.Builder builder = new NetworkRequest.Builder();
    NetworkRequest networkRequest= builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .build();

    connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback());
}

I've also added

<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />

on my AndroidManifest.xml and I ensured myself that Settings.System.canWrite(this) returns true but still nothing.

Thanks in advance.

Infralapsarian answered 15/4, 2016 at 17:12 Comment(11)
My friend did you figure out a solution? I have the exact requirement.Carpathoukraine
I have done a workaround simulating a connection on the IoT device to let Android think that the IoT device had a connection. You can do that placing a void file called generate_204 on the server root and adding some urls to your server dns. I know it's a weird workaround but for me worked. Hope I've helped you.Infralapsarian
Thanks, but unfortunately that is not an option for me. IoT device is not in my control.Carpathoukraine
If you'll find a real solution, please let us know ✌Infralapsarian
@AleMuzzi I have a similar question would you mind giving more information on your generate_204 workaround as i can make changes to the device https://mcmap.net/q/385200/-force-android-to-use-wifi-network-with-no-internet/1685748Surplice
@Surplice Different OS (it also depends on OS version) checks internet connections in different ways; Android asks some google servers a void file called "generate_204" to ensure that the current net has internet connection. If you can provide that file to your device, it will show you that there is an internet connection. Checked urls are: - "connectivitycheck.gstatic.com" - "clients3.google.com" Anyway, you can see what effectively your device does tracking its packets with "tPacketCapture" app and WireSharkInfralapsarian
@AleMuzzi so how do i point connectivitycheck.gstatic.com - clients3.google.com to the web root of my iot device where i can place the generate_204 fileSurplice
@Surplice generate_204 has to be put in the root of your http server in the IoT device and you have to have a dos server on the device that provides those urls, for example if you are using openwrt as IoT OS you will have a file named "hosts" under /etc, add the following lines: 10.1.1.1 connectivitycheck.gstatic.com 10.1.1.1 clients3.google.com. Where 10.1.1.1 is your "127.0.0.1" IoT addressInfralapsarian
I know it's really ugly written like this, sorryInfralapsarian
@AleMuzzi Thank you very much I dont know a lot on the IOT Device side but will editing the hosts be sufficient as we would have push it in an update to many devices. or is the DNS Server a requirement?Surplice
@Surplice Sorry, I think I've not understood your question but if I have understood, I can tell you that OpenWRT provides itself a DNS server, that's why you have an hosts file containing the well known hosts (with static IP) so, hoping that google will not change the internet check system, adding those addresses will be enoughInfralapsarian
G
21

Stanislav's answer is correct but incomplete because only works in Lollipop.

I've wrote a complete solution for Lollipop and Marshmallow onwards for you to route all network requests through WiFi when connected to a specific network of your choice.


Kotlin

In your Activity,

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class RoutingActivity : Activity() {

    private var mConnectivityManager: ConnectivityManager? = null
    private var mNetworkCallback: ConnectivityManager.NetworkCallback? = null
    //...

    override fun onCreate(savedInstanceState: Bundle?) {
        //...
        routeNetworkRequestsThroughWifi("Access-Point-SSID-You-Want-To-Route-Your-Requests")
    }

Route future network requests from application through WiFi (even if given WiFi network is without internet and mobile data has internet connection)

/**
 * This method sets a network callback that is listening for network changes and once is
 * connected to the desired WiFi network with the given SSID it will bind to that network.
 *
 * Note: requires android.permission.INTERNET and android.permission.CHANGE_NETWORK_STATE in
 * the manifest.
 *
 * @param ssid The name of the WiFi network you want to route your requests
 */
private fun routeNetworkRequestsThroughWifi(ssid: String) {
    mConnectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    // ensure prior network callback is invalidated
    unregisterNetworkCallback(mNetworkCallback)

    // new NetworkRequest with WiFi transport type
    val request = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .build()

    // network callback to listen for network changes
    mNetworkCallback = object : ConnectivityManager.NetworkCallback() {

        // on new network ready to use
        override fun onAvailable(network: Network) {

            if (getNetworkSsid(this@RoutingActivity).equals(ssid, ignoreCase = false)) {
                releaseNetworkRoute()
                createNetworkRoute(network)

            } else {
                releaseNetworkRoute()
            }
        }
    }
    mConnectivityManager?.requestNetwork(request, mNetworkCallback)
}

Unregister network callback

private fun unregisterNetworkCallback(networkCallback: ConnectivityManager.NetworkCallback?) {
    if (networkCallback != null) {
        try {
            mConnectivityManager?.unregisterNetworkCallback(networkCallback)

        } catch (ignore: Exception) {
        } finally {
            mNetworkCallback = null
        }
    }
}

Create network route

private fun createNetworkRoute(network: Network): Boolean? {
    var processBoundToNetwork: Boolean? = false
    when {
    // 23 = Marshmallow
        Build.VERSION.SDK_INT >= 23 -> {
            processBoundToNetwork = mConnectivityManager?.bindProcessToNetwork(network)
        }

    // 21..22 = Lollipop
        Build.VERSION.SDK_INT in 21..22 -> {
            processBoundToNetwork = ConnectivityManager.setProcessDefaultNetwork(network)
        }
    }
    return processBoundToNetwork
}

 Release network route

private fun releaseNetworkRoute(): Boolean? {
    var processBoundToNetwork: Boolean? = false
    when {
    // 23 = Marshmallow
        Build.VERSION.SDK_INT >= 23 -> {
            processBoundToNetwork = mConnectivityManager?.bindProcessToNetwork(null)
        }

    // 21..22 = Lollipop
        Build.VERSION.SDK_INT in 21..22 -> {
            processBoundToNetwork = ConnectivityManager.setProcessDefaultNetwork(null)
        }
    }
    return processBoundToNetwork
}

Helper

private fun getNetworkSsid(context: Context?): String {
    // WiFiManager must use application context (not activity context) otherwise a memory leak can occur
    val mWifiManager = context?.applicationContext?.getSystemService(Context.WIFI_SERVICE) as WifiManager
    val wifiInfo: WifiInfo? = mWifiManager.connectionInfo
    if (wifiInfo?.supplicantState == SupplicantState.COMPLETED) {
        return wifiInfo.ssid.removeSurrounding("\"")
    }
    return ""
}
Gnu answered 22/7, 2018 at 22:24 Comment(10)
More information about this can be found here: android-developers.googleblog.com/2016/07/…Gnu
You should consider putting up your own blog post about this; this remains an under-documented topic with a steady stream of SO questions, and your writeup here and at https://mcmap.net/q/392185/-using-a-wifi-without-internet-connection provide a much more complete and useful picture than the googleblog postDabchick
Much appreciated for your positive feedback @mikeh! Feel free to write an article yourself with this content and I kindly volunteer to review it before you publish it.Gnu
Thanks @ryan-amaral, that is indeed very kind. Let me see what I can do!Dabchick
mConnectivityManager?.bindProcessToNetwork(null) - this line did it for me.Chink
Important - it seems the getNetworkSsid() function MUST instantiate a fresh WifiManager as shown (i.e. not use an existing copy declared at the top of a class, etc) or it will likely return the WRONG SSID. Maybe something to do with the connectionInfo or supplicantState getters?Yogi
I am connecting a device without internet. In some devices this code is working but in some devices android automatic disconnects wifi if it is not having internet. In same mobile, if we connect wifi manually instead of programatically then it is asking to keep it or not. Is there any solution to remain connected with the wifi which is not having internet ?Unaccomplished
@RyanAmaral Thanks its really nice post can you please help me which method we have to call what like onResume and onStar method what we have to call i am getting alway unknow ssid so if (getNetworkSsid(this@Rove3MainActivity).equals(ssid, ignoreCase = false)) { this condition failAbscissa
Thanks, @MARSH. As from Android 8 is necessary to request location permission for us to be able to retrieve the SSID. That might be the reason why you're getting <unknown ssid>.Gnu
@KhushbuShah is required to connect to the wifi network programmatically, otherwise will not work as we don't have permission to change the route of a wifi network set up by the user, i.e. manually connected.Gnu
A
13

Bind the network using ConnectivityManager.setProcessDefaultNetwork() prevents roaming and allows for full TCP access. Thus, within the onAvailable() callback you could bind the application process to that network rather than opening a connection to a particular URL.

ConnectivityManager connection_manager = 
(ConnectivityManager) activity.getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);

connection_manager.registerNetworkCallback(request.build(), new NetworkCallback() {

    @Override
    public void onAvailable(Network network) {
        ConnectivityManager.setProcessDefaultNetwork(network);
    }
}

Original answer

Arras answered 20/3, 2017 at 14:36 Comment(2)
thanks @Stanislav, great solutions. Its working for mashmallow and Naugat as well.Dorcus
You really saved my day. thanks a lot. this works in Android P also. I will later try implement Ryan's code also if required.Cobbett

© 2022 - 2024 — McMap. All rights reserved.