I have a big issue with users using my Android application on Samsung Galaxy S3/S4/Note2.
My application sends a GET request to retrieve content of "http://www.msftncsi.com/ncsi.txt" to check if Internet connection is working fine before opening a web form in a WebView. It works very fine most devices, anyway it has a weird behaviour on Samsung devices because it looks like it stores the request response for 10 minutes.
So, if I send this request when Internet access is down then I'll get same results during 10 minutes (false negatives) even if Internet access has been repaired. The contrary is true : if I send the request when Internet access is OK then I'll receive same response during 10 minutes (false positives) even if I disconnect my router from Internet.
I tried solutions from this thread (none worked) : How to prevent Android from returning a cached response to my HTTP Request?
Important note : I can't modify server's response headers.
Here is the code failing on Samsung devices only :
Logger.d(LOG_TAG, "Starting the thread checking internet access.");
boolean isInternetAccessAvailable = false;
boolean tryAgain = false;
do
{
try
{
HttpClient httpClient = new DefaultHttpClient();
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParams, mRequestTimeout);
HttpConnectionParams.setSoTimeout(httpParams, mRequestTimeout);
HttpClientParams.setRedirecting(httpParams, false);
HttpGet httpGet = new HttpGet(CHECK_INTERNET_URL);
HttpResponse httpResponse = httpClient.execute(httpGet);
if (!mCheckInternetTask.isCancelled())
{
Logger.d(LOG_TAG, "Received server response. Start response content parsing.");
StatusLine statusLine = httpResponse.getStatusLine();
int statuscode = statusLine.getStatusCode();
if (statuscode == HttpStatus.SC_OK)
{
HttpEntity entity = httpResponse.getEntity();
if (entity != null)
{
String resultString = null;
try
{
InputStream inputStream = null;
inputStream = entity.getContent();
resultString = convertStreamToString(inputStream);
}
catch (IllegalStateException e)
{
Logger.w(LOG_TAG, "IllegalStateException during HTTP entity conversion into String", e);
}
catch (IOException e)
{
Logger.w(LOG_TAG, "IOException during HTTP entity conversion into String", e);
}
if (resultString != null && resultString.equals("Microsoft NCSI"))
{
Logger.i(LOG_TAG, "Microsoft NCSI web content reachable: Internet OK.");
isInternetAccessAvailable = true;
}
else
{
Logger.i(LOG_TAG, "Microsoft NCSI web content not reachable: Internet KO.");
}
}
else
{
Logger.e(LOG_TAG, "getInternetStatusFromOS() HttpEntity is null !!");
}
// Do no try again
tryAgain = false;
}
else if (statuscode == HttpStatus.SC_MOVED_TEMPORARILY)
{
Logger.i(LOG_TAG, "Captive portal detected, Internet KO");
}
else if (!tryAgain)
{
Logger.w(LOG_TAG, "HTTP response code is " + statuscode + ", not 200, try to clear the DNS cache and try again");
clearDnsCache();
tryAgain = true;
}
else
{
Logger.i(LOG_TAG, "HTTP response code is " + statuscode + ", not 200: Internet KO.");
// Do no try again
tryAgain = false;
}
}
}
catch (ConnectTimeoutException e)
{
Logger.d(LOG_TAG, "ConnectTimeoutException caught: Internet KO.", e);
}
catch (SocketTimeoutException e)
{
tryAgain = true;
Logger.d(LOG_TAG, "SocketTimeoutException caught: Internet KO.", e);
}
catch (ClientProtocolException e)
{
Logger.d(LOG_TAG, "ClientProtocolException caught: Internet KO.", e);
}
catch (IOException e)
{
Logger.d(LOG_TAG, "IOException caught: Internet KO.", e);
}
}
while (tryAgain && !mCheckInternetTask.isCancelled());
if (mCheckInternetTask.isCancelled())
{
Logger.d(LOG_TAG, "Thread interrupted (timed out).");
}
else
{
Logger.d(LOG_TAG, "Check Internet access runnable finished, with result: " + isInternetAccessAvailable);
setCheckInternetAccessResult(isInternetAccessAvailable);
}
I already sorted this problem on the Samsung Galaxy S2 using a hidden function to clear dns cache but it doesn't work anymore on Galaxy S3/S4/Note2 (and probably other Galaxy devices) :
/**
* Try to clear the DNS cache, if necessary.<br/>
* When starting the application, if the {@value #CHECK_INTERNET_URL} is not reachable, the DNS cache will
* remember this status for at least 10 minutes. Any request to this URL will return with a 404 error
* during this period.<br>
* Two solutions can be found:
* <ul>
* <li>Using the <a
* href="http://docs.oracle.com/javase/6/docs/technotes/guides/net/properties.html#nct">networkaddress
* .cache.ttl</a> and the <a
* href="http://docs.oracle.com/javase/6/docs/technotes/guides/net/properties.html#ncnt"
* >networkaddress.cache.negative.ttl</a> constants.</li>
* <li>Using the clearDnsCache from the <a href=
* "https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/java/net/InetAddress.java"
* >INetAddress</a> class source code</li>
* </ul>
* The first solution doesn't work on the Galaxy SII (the device on which the Internet access detection
* problem appeared first). The second solutions needs to use a method that is not visible from the code:
* we will have to use Reflection.
*
* @return true if the clearDnsCache method could be found, and was called.
*/
private boolean clearDnsCache()
{
boolean success = false;
try
{
Method method = null;
method = InetAddress.class.getMethod("clearDnsCache");
if (method != null)
{
method.invoke(null);
success = true;
}
}
catch (SecurityException e)
{
Logger.d(LOG_TAG, "SecurityException when trying to call INetAddress#clearDnsCache()", e);
}
catch (NoSuchMethodException e)
{
Logger.d(LOG_TAG, "NoSuchMethodException when trying to call INetAddress#clearDnsCache()", e);
}
catch (IllegalArgumentException e)
{
Logger.d(LOG_TAG, "IllegalArgumentException when trying to call INetAddress#clearDnsCache()", e);
}
catch (IllegalAccessException e)
{
Logger.d(LOG_TAG, "IllegalAccessException when trying to call INetAddress#clearDnsCache()", e);
}
catch (InvocationTargetException e)
{
Logger.d(LOG_TAG, "InvocationTargetException when trying to call INetAddress#clearDnsCache()", e);
}
return success;
}
Thanks for reading. Thanks in advance for your help.