1

Basically, what I need is check if my Wifi connection has Internet access. The most effective way I know is with sock.connect() but I need to be sure that the connection will be done through the Wifi network and this is my main issue, I was searching for a few days about this and there is not a good response to this.

There are a lot of solutions like How do I see if Wi-Fi is connected on Android? but they only check if the device is connected to a router. I need to know if the router as internet access

My best approximation was this:

        Socket sock = new Socket();
        ConnectivityManager
                cm = (ConnectivityManager) mContext.getApplicationContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            Network net = cm.getActiveNetwork();
            net.bindSocket(sock);
        }
        sock.connect(new InetSocketAddress("8.8.8.8", 53), 1500);
        sock.close();

But this has some limitations. The biggest limitation is that only works for API >= 23. Also, if I am in China I think that hostname "8.8.8.8" will not work, right?

And finally, cm.getActiveNetwork() will only be the WIFI network if my device is connected to a wifi network, and this is not totally true because it is possible to modify the default active network.

What I need to know is:

  • Is there any working alternative for API >= 16?
  • Is there a good hostname that will work fine in China?

Any help will be appreciated, Thanks in advance!

Daniel S.
  • 2,629
  • 2
  • 21
  • 19
  • Possible duplicate of [How do I see if Wi-Fi is connected on Android?](https://stackoverflow.com/questions/3841317/how-do-i-see-if-wi-fi-is-connected-on-android) – owl777 Sep 13 '18 at 19:36
  • No, there are a lot of solutions like that but they only check if the device is connected to a router. I need to know if the router as internet access – Daniel S. Sep 13 '18 at 19:46

2 Answers2

0

Yes you have to check with a remote server in order to be sure.

A common way would be like this:

NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
if (isConnected) {
    try {
        InetAddress ipAddr = InetAddress.getByName("google.com");
        if (ipAddr.isReachable(5000)) {
            // Internet access OK!
        }
    } catch (Exception e) {
        // Error handling...
    }
}

Prefer domain names when calling getByName rather than IPs (https://docs.oracle.com/javase/7/docs/api/java/net/InetAddress.html#getByName(java.lang.String))

If you want to avoid the connectivity manager you can register a BroadcastReceiver for WifiManager.NETWORK_STATE_CHANGED_ACTION events and you will know if you are connected to a WiFi (the current state is received almost immediately).

As regards the region issue I am out of ideas, maybe use NTP servers instead of google (much more innocent servers) or try Baidu!?

Regulus
  • 377
  • 3
  • 8
  • Yes, I did something like this but I want to check the internet access through the wifi connection. With your solution, I can not guarantee this. Also isReachable() does not work properly, I was using it for another purpose and you can have internet access and isReachable() could return false anyway :( – Daniel S. Sep 13 '18 at 20:35
  • Indeed you cannot be 100% sure. You can rely on the fact that if the WiFi is connected that's the most probable means to perform network operations instead of through mobile data or so (but you cannot be sure ofc). And indeed isReachable is not perfect. There is the issue of DNS caching (many culprits, local, router, ISP) so life is hard. You could try this instead of isReachable: if (!ipAddr.equals(""))... – Regulus Sep 13 '18 at 20:46
0

Finally, I came to a solution:

    public interface Consumer {
    void accept(Boolean internet);
}

class InternetCheck extends AsyncTask<Void, Void, Boolean> {

    private Consumer mConsumer;

    public InternetCheck(Consumer consumer) {
        mConsumer = consumer;
        execute();
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
        Socket socket = null;
        try {
            WifiManager wifiManager = (WifiManager) mContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
            if (wifiManager != null) {
                socket = new Socket();
                socket.setKeepAlive(false);
                String localIpAddress = getIpAddress(wifiManager);
                socket.bind(new InetSocketAddress(localIpAddress, 0));
                socket.connect(new InetSocketAddress("8.8.8.8", 53), 1500);
                return true;
            }
            return false;
        } catch (IOException e) {
            //unbind();
            return false;
        }finally {
            if(socket != null && !socket.isClosed()) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    protected void onPostExecute(Boolean internet) {
        mConsumer.accept(internet);
    }
}

public static String getIpAddress(WifiManager wifiManager) {
    WifiInfo wifiInfo = wifiManager.getConnectionInfo();
    int ipAddress = wifiInfo.getIpAddress();
    return String.format(Locale.getDefault(), "%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff),
            (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));
}

I came to a solution after saw this question. With this, I obtained the IP address of my wifi connection and with this, I was able to bind the socket (socket.bind(...)) to the wifi connection and be check if my router had internet access.

I hope this solution helps somebody in the future :)

Daniel S.
  • 2,629
  • 2
  • 21
  • 19