I've been trying to get the DNS resolution time using the next code:
val url = URL(dataExperienceTestResult.pingUrl)
val host: String = url.host
val currentTime: Long = SystemClock.elapsedRealtime()
val address: InetAddress = InetAddress.getByName(host)
val dnsTime: Long = SystemClock.elapsedRealtime() - currentTime
Which works as expected providing me with a reasonable resolution time (100ms using Data), however, this is just the case of the first try because the next resolution times are too low (0-2ms using Data). After reading the documentation, I could find the reason for this is because it is cached for 10 mins if successful.
I tried to call the hidden method clearDnsCache()
of the class InerAddress
using reflection having slightly higher results (2-4ms using Data) so the cache doesn't seem to be cleared completely:
//The position 0 method of the list is clearDnsCache()
val method = Class.forName(InetAddress::class.java.name).methods[0]
method.invoke(Class.forName(InetAddress::class.java.name))
I also tried a solution that I read in other StackOverflow questions which consist of eating a security property of the JVM machine. It didn't work, I guess this is because it would require root.
Security.setProperty("networkaddress.cache.ttl", "0")
The last option that I'm currently working on consist of sending a query using the DnsResolver class but I'm getting to high results (300ms - first true, 200ms next tries both using Data).
private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
long currentTime;
@RequiresApi(api = Build.VERSION_CODES.Q)
public void method(Context context){
URL url;
Executor executor = new Handler(Looper.getMainLooper())::post;
try {
url = new URL("https://ee-uk.metricelltestcloud.com/SpeedTest/latency.txt");
//
String host = url.getHost();
final String msg = "RawQuery " + host;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
Network[] networks = connectivityManager.getAllNetworks();
currentTime = SystemClock.elapsedRealtime();
for (Network network : networks){
final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
DnsResolver resolver = DnsResolver.getInstance();
resolver.rawQuery(network, host, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, executor, null, callback);
}
}
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
private static String byteArrayToHexString(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; ++i) {
int b = bytes[i] & 0xFF;
hexChars[i * 2] = HEX_CHARS[b >>> 4];
hexChars[i * 2 + 1] = HEX_CHARS[b & 0x0F];
}
return new String(hexChars);
}
@RequiresApi(api = Build.VERSION_CODES.Q)
class VerifyCancelCallback implements DnsResolver.Callback<byte[]> {
private String mMsg;
VerifyCancelCallback(@NonNull String msg) {
this.mMsg = msg;
// this(msg, null);
}
@Override
public void onAnswer(@NonNull byte[] answer, int rcode) {
long dnsTime = SystemClock.elapsedRealtime() - currentTime;
Log.v("Kanto_Resolver", "Answer " + dnsTime + " ms");
Log.v("Kanto_resolver", answer.toString());
Log.d("Kanto_resolver", "Reported rcode: " + rcode);
Log.d("Kanto_resolver", "Reported blob: " + byteArrayToHexString(answer));
}
@Override
public void onError(@NonNull DnsResolver.DnsException error) {
Log.v("Kanto_Resolver", "Error");
}
}
Question: Do you know a way to resolve the DNS without using "InetAddress.getByName()" or a way to clear completely the DNS cache?
I need: Get the real (not cached) DNS resolution time every time I check it without considering when I did the last check.
I'm aware that there are already some questions about same topic in StackOverflow but most of them are too old and none of them could solve my question at all.