Android check internet connection when 3g is available but not accessible - android

I've been looking for help on the other questions, but can not find something, while in a review activity need internet connection, even with activated 3G but can not connect (have exceeded data use or firewall) tutorials or help I have found do not include that.

I use this little function to detect Public Wifi redirects to sign on page. If this function returns false, it means you could not connect to the intended page despite having your 3G or Wifi on. With this you can show the user any page, for example a "No Internet Connection" page.
public boolean networkSignOn() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL("http://clients3.google.com/generate_204");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(10000);
urlConnection.setReadTimeout(10000);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
return urlConnection.getResponseCode() == 204;
} catch (IOException e) {
Log.v("Walled garden check - probably not a portal: exception " + e, "");
return false;
} finally {
if (urlConnection != null) urlConnection.disconnect();
}
}

Related

URL Connection close hangs sometimes

I have a function that gets the size of a file on a server. I recognize that closing the connection can take a lot of time, like 10 secs or more, sometimes.
Now I had the situation, that in the Android emulator it hanged forever, but starting the same app on a real device it went through normally.
Can someone explain this behavior or is there a better way to close the connection?
public static int getFileSizeFromURL(String sUrl) {
URL url;
URLConnection conn;
int size=0;
try {
url = new URL(sUrl);
conn = url.openConnection();
size = conn.getContentLength();
if(size < 0){
} else {
conn.getInputStream().close(); <----- hangs here in Simulator.
}
}
catch(Exception e) {
e.printStackTrace();
}
return size;
}
When size is zero then connection should be disconnect. and when size is more than zero then connection should get input stream working. Try below code.
public static int getFileSizeFromURL(String sUrl) {
URL url;
URLConnection conn;
int size=0;
try {
url = new URL(sUrl);
conn = url.openConnection();
size = conn.getContentLength();
if(size == 0){
conn.disconnect();
}
else
conn.getInputStream(); <----- hangs here in Simulator.
}
catch(Exception e) {
e.printStackTrace();
}
return size;
}
I think this might be related to your code making a GET request when you should really do a HEAD request:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
I am not sure whether this will fix the problem, but the docs say
Calling the close() methods on the InputStream or OutputStream of an HttpURLConnection after a request may free network resources associated with this instance
and a GET request will definitely use more resources than a HEAD request. Unless a GET request is strictly required, you should avoid it. If you are not sure whether the server supports HEAD requests, try HEAD first and fall back to GET if the first attempt fails.

HttpUrlConnect set Host header

I need to set the Host header for a very specific request and this is not being taken into account.
I know this is a restricted header, but how is it possible to do it?
Here's what I'm trying.
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(10000);
urlConnection.setRequestProperty("Host", "test.com");
urlConnection.setRequestMethod("GET");
int responseCode = urlConnection.getResponseCode();
Log.d(TAG, String.format("response code: %d", responseCode));
} catch (IOException e) {
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
If I'm not misunderstanding your question perhaps you can do something like this instead of trying to set the host header under your try statement:
URL url = new URL("http://www.test.com/");
The host header will take on the information from whichever URL you supply.
Source:
https://developer.android.com/reference/java/net/HttpURLConnection.html

HttpURLConnection getResponseCode() deos not return if there is no Internet connection

I am using a HttpURLConnection to check whether the server URL is available or not by using the following code:
try {
boolean connectionFailed = false;
URL knownURL = new URL("http://www.google.com");
httpConnection = (HttpURLConnection) knownURL.openConnection();
httpConnection.setConnectTimeout(5000);
responseCode = httpConnection.getResponseCode();
if (responseCode != 200) {
status = ConnectionStatus.NOT_CONNECTED;
}
}
catch(Exception e) {
connctionFailed = true;
}
This code is working fine under normal conditions. But when there is no Internet connection (because either the router is disconnected or not the hotspot), httpConnection.getResponseCode() is not executed (the function does not return). How can I fix this?
httpConnection.setConnectTimeout(5000)
is a timeout for connection.
This is not a timeout for httpConnection.getResponseCode().
If you add httpConnection.setReadTimeout(2000), httpConnection.getResponseCode()should throw an exception when no connection is available.
You may be having a try catch block at higher layer which is catching the sockettimeout exception.

How to check for unrestricted Internet access? (captive portal detection)

I need to reliably detect if a device has full internet access, i.e. that the user is not confined to a captive portal (also called walled garden), i.e. a limited subnet which forces users to submit their credentials on a form in order to get full access.
My app is automating the authentication process, and therefore it is important to know that full internet access is not available before starting the logon activity.
The question is not about how to check that the network interface is up and in a connected state. It is about making sure the device has unrestricted internet access as opposed to a sandboxed intranet segment.
All the approaches I have tried so far are failing, because connecting to any well-known host would not throw an exception but return a valid HTTP 200 response code because all requests are routed to the login page.
Here are all the approaches I tried but they all return true instead of false for the reasons explained above:
1:
InetAddress.getByName(host).isReachable(TIMEOUT_IN_MILLISECONDS);
isConnected = true; <exception not thrown>
2:
Socket socket = new Socket();
SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(host), 80);
socket.connect(sockaddr, pingTimeout);
isConnected = socket.isConnected();
3:
URL url = new URL(hostUrl));
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setRequestMethod("GET");
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
So, how do I make sure I connected to an actual host instead of the login redirection page? Obviously, I could check the actual response body from the 'ping' host I use but it does not look like a proper solution.
For reference, here is the 'official' method from the Android 4.0.1 AOSP code base:
WifiWatchdogStateMachine.isWalledGardenConnection(). I am including the code below just in case the link breaks in the future.
private static final String mWalledGardenUrl = "http://clients3.google.com/generate_204";
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
private boolean isWalledGardenConnection() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(mWalledGardenUrl); // "http://clients3.google.com/generate_204"
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// We got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) {
log("Walled garden check - probably not a portal: exception "
+ e);
}
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
This approach relies on a specific URL, mWalledGardenUrl = "http://clients3.google.com/generate_204" always returning a 204 response code. This will work even if DNS has been interfered with since in that case a 200 code will be returned instead of the expected 204. I have seen some captive portals spoofing requests to this specific URL in order to prevent the Internet not accessible message on Android devices.
Google has a variation of this theme: fetching http://www.google.com/blank.html will return a 200 code with a zero-length response body. So if you get a non-empty body this would be another way to figure out that you are behind a walled garden.
Apple has its own URLs for detecting captive portals: when network is up IOS and MacOS devices would connect to an URL like http://www.apple.com/library/test/success.html, http://attwifi.apple.com/library/test/success.html, or http://captive.apple.com/hotspot-detect.html which must return an HTTP status code of 200 and a body containing Success.
NOTE:
This approach will not work in areas with regionally restricted Internet access such as China where the whole country is a walled garden, and where some Google/Apple services might be blocked. Some of these might not be blocked: http://www.google.cn/generate_204, http://g.cn/generate_204, http://gstatic.com/generate_204 or http://connectivitycheck.gstatic.com/generate_204 — yet these all belong to google so not guaranteed to work.
Another possible solution might be to connect via HTTPS and inspect the target certificate. Not sure if walled gardens actually serve the login page via HTTPS or just drop the connections. In either case, you should be able to see that your destination is not the one you expected.
Of course, you also have the overhead of TLS and certificate checks. Such is the price of authenticated connections, unfortunately.
I believe preventing redirection for your connection will work.
URL url = new URL(hostUrl));
HttpURLConnection httpConn = (HttpURLConnection)url.openConnection();
/* This line prevents redirects */
httpConn.setInstanceFollowRedirects( false );
httpConn.setAllowUserInteraction( false );
httpConn.setRequestMethod( "GET" );
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
If that doesn't work, then I think the only way to do it is to check the body of the response.
This has been implemented on Android 4.2.2+ version - I find their approach fast and interesting :
CaptivePortalTracker.java detects walled garden as follows
- Try to connect to www.google.com/generate_204
- Check that the HTTP response is 204
If the check fails, we are in a walled garden.
private boolean isCaptivePortal(InetAddress server) {
HttpURLConnection urlConnection = null;
if (!mIsCaptivePortalCheckEnabled) return false;
mUrl = "http://" + server.getHostAddress() + "/generate_204";
if (DBG) log("Checking " + mUrl);
try {
URL url = new URL(mUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// we got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) log("Probably not a portal: exception " + e);
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
if you are already using retrofit you can do it by retrofit. just make a ping.html page and send an head request to it using retrofit and make sure your http client is configured like below: (followRedirects(false) part is the most important part)
private OkHttpClient getCheckInternetOkHttpClient() {
return new OkHttpClient.Builder()
.readTimeout(2L, TimeUnit.SECONDS)
.connectTimeout(2L, TimeUnit.SECONDS)
.followRedirects(false)
.build();
}
then build your retrofit like below:
private InternetCheckApi getCheckInternetRetrofitApi() {
return (new Retrofit.Builder())
.baseUrl("[base url of your ping.html page]")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.client(getCheckInternetOkHttpClient())
.build().create(InternetCheckApi.class);
}
your InternetCheckApi.class would be like:
public interface InternetCheckApi {
#Headers({"Content-Typel: application/json"})
#HEAD("ping.html")
Call<Void> checkInternetConnectivity();
}
then you can use it like below:
getCheckInternetOkHttpClient().checkInternetConnectivity().enqueue(new Callback<Void>() {
public void onResponse(Call<Void> call, Response<Void> response) {
if(response.code() == 200) {
//internet is available
} else {
//internet is not available
}
}
public void onFailure(Call<Void> call, Throwable t) {
//internet is not available
}
}
);
note that your internet check http client must be separate from your main http client.
This is best done here as in AOSP :
https://github.com/aosp-mirror/platform_frameworks_base/blob/6bebb8418ceecf44d2af40033870f3aabacfe36e/core/java/android/net/captiveportal/CaptivePortalProbeResult.java#L61
https://github.com/aosp-mirror/platform_frameworks_base/blob/e3a0f42e8e8678f6d90ddf104d485858fbb2e35b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
private static final String GOOGLE_PING_URL = "http://google.com/generate_204";
private static final int SOCKET_TIMEOUT_MS = 10000;
public boolean isCaptivePortal () {
try {
URL url = new URL(GOOGLE_PING_URL);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
return (urlConnection.getResponseCode() != 204)
&& (urlConnection.getResponseCode() >= 200)
&& (urlConnection.getResponseCode() <= 399);
} catch (Exception e) {
// for any exception throw an exception saying check was unsuccesful
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
Please note this will probably not work on a proxy network and something more advanced as in the AOSP url needs to be done

HttpURLConnection responsecode is randomly -1

Hi I'm using following code to establish a url connection. But randomly I get the responseCode -1 (which is the default value of responseCode):
try {
URL url = new URL(urlString);
HttpURLConnection httpconn = (HttpURLConnection) url.openConnection();
if (httpconn.getResponseCode() == HttpURLConnection.HTTP_OK) {
handleData(new DataInputStream(httpconn.getInputStream()), requestCode);
} else {
Log.e(TAG, "HttpConnection not OK: " + httpconn.getResponseCode());
ActivityHelper.httpError(this);
}
httpconn.disconnect();
} catch (Exception e) {
Log.e(TAG, "handleHttpConnection", e);
ActivityHelper.httpError(this);
}
Am I doing something wrong? Because it works perfectly in estimated 9 of 10 attempts.
UrlConnection is buggy.
See this blog post from the official Android Developer's blog for a pre-Gingerbread workaround for one problem.
My advice, don't use it. It was still being flaky for me on 3.2. I switched to HttpClient and things have been less bad.

Categories

Resources