Send Request Over Wifi (Without Connection) Even If Mobile Data Is on (With Connection) on Android M

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

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 ""
}

Send data through wifi (no internet) when mobile data on

After a lot of research and time trying to understand it all I found out that this isn't possible in versions previous to Android 5.0 (Lollipop), the OS only keeps one network interface up at a time and the apps don't have control over this.

So to do this on versions greater or equal to the Lollipop I did the following:

  1. Before doing your socket connection check if android greater or equal to Lollipop, in case it isn't you just do whatever you have to do normally;
  2. In case the version is equal or greater to Lollipop you need to do something like this:

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    final ConnectivityManager manager = (ConnectivityManager) context
    .getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkRequest.Builder builder;
    builder = new NetworkRequest.Builder();
    //set the transport type do WIFI
    builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
    manager.requestNetwork(builder.build(), new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    manager.bindProcessToNetwork(network);
    } else {
    //This method was deprecated in API level 23
    ConnectivityManager.setProcessDefaultNetwork(network);
    }
    try {
    //do a callback or something else to alert your code that it's ok to send the message through socket now
    } catch (Exception e) {
    e.printStackTrace();
    }
    manager.unregisterNetworkCallback(this);
    }
    });
    }
  3. After you finish you should stop binding the process with this:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    ConnectivityManager manager = (ConnectivityManager) InovePlugApplication.getContext()
    .getSystemService(Context.CONNECTIVITY_SERVICE);
    manager.bindProcessToNetwork(null);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    ConnectivityManager.setProcessDefaultNetwork(null);
    }

This answer helped me unsderstand how to do it https://stackoverflow.com/a/29837637/2550932.

Send request over Mobile data when WIFI is ON.(Android L)

Well finally found solution for this. Trick was to use capability as NET_CAPABILITY_INTERNET. Which is same as startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_HIPRI);

See the Firmware design here

builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);

After this I am able to get onAvailable callback from system and later I set my process default network as mobile data.

Hence all the request goes over mobile data even if wifi is on. WOW!

Note: This was not working in initial releases of Preview L.

Edit 19-10-2015: setProcessDefaultNetwork is now depcreated use bindProcessToNetwork

Send a POST request only through WiFi interface

Since Android 5 (API 21), you can force connections to use the WiFi even if it's not the default network.

One solution is to find the corresponding network, for example with ConnectivityManager.getAllNetworks() and ConnectivityManager.getNetworkInfo():

Once you have the Network, you can either :

  • Open a connection on this network using Network.openConnection().

  • Bind the application to the network with ConnectivityManager.setProcessDefaultNetwork or ConnectivityManager.bindProcessToNetwork() (API 23+)

See Connecting your App to a Wi-Fi Device (especially Routing network requests) for more details.



Related Topics



Leave a reply



Submit