I have a wifi LAN setup which does not have internet access. Just various other local wifi devices connected to it. The DHCP is configured to not return a gateway or dns ser
Google added some useful methods in Android SDK 21 for this purpose.
You can create NetworkRequest
:
NetworkRequest networkRequest = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
And then you can request such network using ConnectivityManager
. For example, you want to be sure that all HTTP requests will be passed through the network with internet access. You can build your Retrofit API in this way:
ApiConfig apiConfig;
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
apiConfig = new Retrofit.Builder()
.baseUrl("https://api.imatrix.io/")
.client(new OkHttpClient.Builder()
.socketFactory(network.getSocketFactory())
.build())
.build()
.create(ApiConfig.class);
}
@Override
public void onLost(Network network) {
apiConfig = null;
}
});
Please, mind the thread-safety when you're using such snippet of code.
In addition, I suggest check ConnectivityManager#bindProcessToNetwork and this blog.
ConnectivityManager.NetworkCallback
is an empty class and it has several methods.
After a bit of coding and testing I have merged Squonk and this solution. This is the class I have created:
package it.helian.exampleprj.network;
import java.net.InetAddress;
import java.net.UnknownHostException;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.State;
import android.net.wifi.WifiManager;
import android.text.TextUtils;
import android.util.Log;
public class NetworkUtils {
private static final String TAG_LOG = "ExamplePrj";
Context context;
WifiManager wifiMan = null;
WifiManager.WifiLock wifiLock = null;
public NetworkUtils(Context context) {
super();
this.context = context;
}
/**
* Enable mobile connection for a specific address
* @param context a Context (application or activity)
* @param address the address to enable
* @return true for success, else false
*/
public boolean forceMobileConnectionForAddress(Context context, String address) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == connectivityManager) {
Log.d(TAG_LOG, "ConnectivityManager is null, cannot try to force a mobile connection");
return false;
}
//check if mobile connection is available and connected
State state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.d(TAG_LOG, "TYPE_MOBILE_HIPRI network state: " + state);
if (0 == state.compareTo(State.CONNECTED) || 0 == state.compareTo(State.CONNECTING)) {
return true;
}
//activate mobile connection in addition to other connection already activated
int resultInt = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
Log.d(TAG_LOG, "startUsingNetworkFeature for enableHIPRI result: " + resultInt);
//-1 means errors
// 0 means already enabled
// 1 means enabled
// other values can be returned, because this method is vendor specific
if (-1 == resultInt) {
Log.e(TAG_LOG, "Wrong result of startUsingNetworkFeature, maybe problems");
return false;
}
if (0 == resultInt) {
Log.d(TAG_LOG, "No need to perform additional network settings");
return true;
}
//find the host name to route
String hostName = extractAddressFromUrl(address);
Log.d(TAG_LOG, "Source address: " + address);
Log.d(TAG_LOG, "Destination host address to route: " + hostName);
if (TextUtils.isEmpty(hostName)) hostName = address;
//create a route for the specified address
int hostAddress = lookupHost(hostName);
if (-1 == hostAddress) {
Log.e(TAG_LOG, "Wrong host address transformation, result was -1");
return false;
}
//wait some time needed to connection manager for waking up
try {
for (int counter=0; counter<30; counter++) {
State checkState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
if (0 == checkState.compareTo(State.CONNECTED))
break;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
//nothing to do
}
boolean resultBool = connectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
Log.d(TAG_LOG, "requestRouteToHost result: " + resultBool);
if (!resultBool)
Log.e(TAG_LOG, "Wrong requestRouteToHost result: expected true, but was false");
state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.d(TAG_LOG, "TYPE_MOBILE_HIPRI network state after routing: " + state);
return resultBool;
}
/**
* This method extracts from address the hostname
* @param url eg. http://some.where.com:8080/sync
* @return some.where.com
*/
public String extractAddressFromUrl(String url) {
String urlToProcess = null;
//find protocol
int protocolEndIndex = url.indexOf("://");
if(protocolEndIndex>0) {
urlToProcess = url.substring(protocolEndIndex + 3);
} else {
urlToProcess = url;
}
// If we have port number in the address we strip everything
// after the port number
int pos = urlToProcess.indexOf(':');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
// If we have resource location in the address then we strip
// everything after the '/'
pos = urlToProcess.indexOf('/');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
// If we have ? in the address then we strip
// everything after the '?'
pos = urlToProcess.indexOf('?');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
return urlToProcess;
}
/**
* Transform host name in int value used by {@link ConnectivityManager.requestRouteToHost}
* method
*
* @param hostname
* @return -1 if the host doesn't exists, elsewhere its translation
* to an integer
*/
private int lookupHost(String hostname) {
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
return -1;
}
byte[] addrBytes;
int addr;
addrBytes = inetAddress.getAddress();
addr = ((addrBytes[3] & 0xff) << 24)
| ((addrBytes[2] & 0xff) << 16)
| ((addrBytes[1] & 0xff) << 8 )
| (addrBytes[0] & 0xff);
return addr;
}
@SuppressWarnings("unused")
private int lookupHost2(String hostname) {
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
return -1;
}
byte[] addrBytes;
int addr;
addrBytes = inetAddress.getAddress();
addr = ((addrBytes[3] & 0xff) << 24)
| ((addrBytes[2] & 0xff) << 16)
| ((addrBytes[1] & 0xff) << 8 )
| (addrBytes[0] & 0xff);
return addr;
}
public Boolean disableWifi() {
wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
if (wifiMan != null) {
wifiLock = wifiMan.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, "HelianRCAWifiLock");
}
return wifiMan.setWifiEnabled(false);
}
public Boolean enableWifi() {
Boolean success = false;
if (wifiLock != null && wifiLock.isHeld())
wifiLock.release();
if (wifiMan != null)
success = wifiMan.setWifiEnabled(true);
return success;
}
}
This is the usage:
boolean mobileRoutingEnabled = checkMobileInternetRouting();
if(!mobileRoutingEnabled) {
networkUtils.disableWifi();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
networkUtils.forceMobileConnectionForAddress(context, RCA_URL);
if(!mobileRoutingEnabled) {
networkUtils.enableWifi();
}
// This second check is for testing purpose
checkMobileInternetRouting();
return callWebService(RCA_COMPLETE_URL, _plate);
where checkMobileInternetRouting is:
private boolean checkMobileInternetRouting() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
State state = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
return 0 == state.compareTo(State.CONNECTED) || 0 == state.compareTo(State.CONNECTING);
}
This is a bit hacky but works properly. The only problem is that this routing has got a timeout of few seconds (like 20-30) that forces you to execute the entire above procedure once more. Setting this timeout to a higher value would be very good.
From code, when you detect there is no connectivity, you could switch off WiFi...
As for a setting, there is none (no good way to check if there really is connectivity universally and reliably). But some phones do just what you describe automatically, like for example my LG P-970.
(Note: Android disconnects from mobile networks when it connects to a WiFi, so there is no way to still be connected to a WiFi but route internet access through mobile, even though Linux can do it (with the ip route ...
suite of tools))
you don't need to code anything. i found an app that do exactly this thing. you can configure to disconnect automatically from the wifi if there is no internet from this connection.
https://play.google.com/store/apps/details?id=com.nLabs.internetconnectivity&hl=en
I can't guarantee this will work as it's something I only experimented with some time ago. I had a similar need to use 3G (or other mobile network) when the wifi-connected network had no route to the outside world.
The following code should drop the wifi connection in order to allow the mobile network to come in to play. You'll need to do various tests along the way and re-establish the wifi connection again afterwards...
WifiManager wifiMan = null;
WifiManager.WifiLock wifiLock = null;
private Boolean disableWifi() {
wifiMan = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (wifiMan != null) {
wifiLock = wifiMan.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, "MyWifiLock");
}
return wifiMan.setWifiEnabled(false);
}
private Boolean enableWifi() {
Boolean success;
if (wifiLock != null)
wifiLock.release();
if (wifiMan != null)
success = wifiMan.setWifiEnabled(true);
return success;
}