Following Section 1 on forcing Mainland China DNS carrier upon Hong Kong WiFi users, this phase analyzes how the periodic DNS queries on qq.com, as seen by users taking pictures the DNS queries, are generated from the recent Samsung telephones firmware (as at Sept 2020) when WiFi is used.

tl;dr: These queries are sent simultaneously to all DNS servers registered for the WiFi connection, collectively with 114.114.114.114 when it is added below circumstances as examined in Section 1. They are generated every 20 to 60 seconds (depending on settings and hyperlink stipulations) when the next pre-stipulations are delighted, i.e. WiFi is linked (with the exception of with some identified devices) with mCurrentMode no longer equals 0 (elaborated at the bottom of this article), and when show cloak cloak is on.  The queries are sent straight the utilization of the WiFi connection hyperlink, without going by the VPN / non-public DNS configured, presumably as they are supposed for testing the WiFi network.  The IP addresses returned by the queries are no longer additional used, with the exception of for checking within the event that they are in a non-public IP fluctuate (whereby case the checking is deemed failed).

The following is a technical (be taught: very lifeless) sage of the findings. Soar to the tip for hints to disable the DNS inquire to your private home WiFi.

For better illustration of the availability, a recent Github repository showing the decompiled model of the same wifi-carrier.jar as in Section I has been created. It uses a assorted mode of the decompiler (JADX) to are trying and decompile more code to Java, even when there are points in correctness. The repository is at SM-N9750-TGY-1.

The principle avid gamers

The principle controlling class for the logic is again below com.android.server.wifi.WifiConnectivityMonitor, within the file WifiConnectivityMonitor.java.

As briefly mentioned within the closing phase, this class implements a narrate machine which represents the difficulty of the WiFi connection being monitored. The states procure messages and respond to them by taking actions and / or transition into one other narrate.

Separately, a subclass, NetworkStatsAnalyzer, is accountable for the monitoring of the connectivity and stamp energy. The analyzer is an Android Handler which consumes messages in an event loop. These messages would be sent by the guardian narrate machine, from inside of the analyzer itself, or from assorted subclasses, to modify the actions of the analyzer.

The most relevant messages in our context are defined as follows (until in any other case specified, the street numbers are those of the WifiConnectivityMonitor.java file within the decompiled code):

Messages sent to the Analyzer to modify the lifecycle of the checking loop (lines 115-117, 254):

    non-public static final int ACTIVITY_CHECK_POLL = 135221;
    non-public static final int ACTIVITY_CHECK_START = 135219;
    non-public static final int ACTIVITY_CHECK_STOP = 135220;
     ...
    non-public static final int NETWORK_STAT_CHECK_DNS = 135223;

Messages representing external events sent to the WifiConnectivityMonitor narrate machine (lines 205-206).

    non-public static final int EVENT_SCREEN_OFF = 135177;
    non-public static final int EVENT_SCREEN_ON = 135176;

Let the inquire loop originate

When the cellular telephone show cloak cloak is grew to become on, Android will send the android.intent.motion.SCREEN_ON intent to the network receiver of this class (line 1149), which will in turn send EVENT_SCREEN_ON to the WifiConnectivityMonitor narrate machine. If the narrate is below EvaluatedState [Note 1], the processMessage() blueprint, spherical line 2138, handles the message (for brevity, some reference to WifiConnectivityMonitor within the code is disregarded):

    
case EVENT_SCREEN_ON:  //135176 
...
if (mCurrentMode != 0) {
    sendMessage(obtainMessage(135188 /CMD_RSSI_FETCH */, mRssiFetchToken, 0) ;
    if (isValidState() && getCurrentState() != mLevel2State) {
        if (mNetworkStatsAnalyzer != null) {
            mNetworkStatsAnalyzer.sendEmptyMessage(ACTIVITY_CHECK_START); // 135219
        }
        startEleCheck();
    }
    if (mCurrentMode == 1 || mLinkDetectMode == 1) {
        sendMessage(obtainMessage(CMD_TRAFFIC_POLL/*135193*/, mTrafficPollToken, 0);
    }
}
...

This implies that, when mCurrentMode is non-zero, WiFi is linked, and the connection is no longer with some identified devices, this would presumably well send the ACTIVITY_CHECK_START message to the NetworkStatsAnalyzer. (This can moreover manufacture assorted actions, be pleased calling startEleCheck() to sight devices, however these are no longer relevant to our context.)

The message ACTIVITY_CHECK_START is moreover sent to the NetworkStatsAnalyzer from a couple of assorted areas. Right here is an extract of the enter() blueprint of the Edifying narrate, introduced about when WifiConnectivityMonitor enters Edifying narrate (i.e. upon WiFi connection) (line 3077-).

if (mCurrentMode != 0) {
    ...
    mNetworkStatsAnalyzer.sendEmptyMessage(ACTIVITY_CHECK_START);
    mNetworkStatsAnalyzer.sendEmptyMessage(NETWORK_STAT_CHECK_DNS);
}

ACTIVITY_CHECK_START is moreover sent when mCurrentMode switches from 0 to assorted values below Edifying narrate (line 3112).  Collectively, it is ensured that everyone paths arriving at the difficulty, i.e. Wifi is linked, show cloak cloak is on, and mCurrentMode is non-zero, will trigger ACTIVITY_CHECK_START.

New that upon coming into the Edifying narrate, NETWORK_STAT_CHECK_DNS is moreover sent to the NetworkStatsAnalyzer. Let’s watch at what this message does first.

The NetworkStatsAnalyzer

The “loop” structure

The under shows a skeleton of the NetworkStatsAnalyzer handler loop handling the above messages, with bodies of ACTIVITY_CHECK_POLL and ACTIVITY_CHECK_STOP disregarded for the 2nd. (lines 4157-, 4477-, 5107-)

non-public class NetworkStatsAnalyzer extends Handler
{
    ...
    non-public boolean mDnsInterrupted = unsuitable;
    non-public boolean mDnsQueried = unsuitable;
    non-public prolonged mLastDnsCheckTime = 0;
    non-public boolean mPollingStarted = unsuitable;
    non-public boolean mPublicDnsCheckProcess = unsuitable;
    non-public boolean mSkipRemainingDnsResults = unsuitable;
    
    ...
    public void handleMessage(Message string) {
        prolonged now = SystemClock.elapsedRealtime();
        final prolonged elapsedRealtime = SystemClock.elapsedRealtime();
        final WifiInfo wifiInfo = WifiConnectivityMonitor.this.syncGetCurrentWifiInfo();
        final int what = string.what;
        swap (what) {
        ...
            case ACTIVITY_CHECK_START:
                if (this.mPollingStarted) ruin;
                if (WifiConnectivityMonitor.this.isMobileHotspot()) ruin;
                if (this.isBackhaulDetectionEnabled()) {
                    this.sendEmptyMessage(TCP_BACKHAUL_DETECTION_START); // 135226
                }
                this.sendEmptyMessage(ACTIVITY_CHECK_POLL);
                WifiConnectivityMonitor.this.initNetworkStatHistory();
                this.mLastRssi = wifiInfo.getRssi();
                this.mPollingStarted = appropriate;
                ruin;
            case ACTIVITY_CHECK_POLL:
                ...
                ruin;
            case ACTIVITY_CHECK_STOP:
                ...
                ruin;
            case NETWORK_STAT_CHECK_DNS:
                if (!WifiConnectivityMonitor.this.isMobileHotspot()) {
                    checkPublicDns();
                }
                ruin;
        }
    }
}

The message NETWORK_STAT_CHECK_DNS, sent upon WiFi connection, will invoke the manner checkPublicDns() if the WiFi is no longer a cellular hotspot. The blueprint is as follows (line 4262-).

public void checkPublicDns() {
    if (WifiConnectivityMonitor.this.inChinaNetwork()) {
        mPublicDnsCheckProcess = unsuitable;
        return;
    }
    mPublicDnsCheckProcess = appropriate;
    mNsaQcStep = 1;
    WifiConnectivityMonitor wifiConnectivityMonitor = WifiConnectivityMonitor.this;
    String str = wifiConnectivityMonitor.mParam.DEFAULT_URL_STRING;
    DnsThread mDnsThread = recent DnsThread(appropriate, str, this, 10000);
    mDnsThread.originate();
    WifiConnectivityMonitor.this.mDnsThreadID = mDnsThread.getId();
    if (WifiConnectivityMonitor.DBG) {
        Log.d(TAG, "wait publicDnsThread outcomes [" + WifiConnectivityMonitor.this.mDnsThreadID + "]");
    }
}

We sight the problematic inChinaNetwork() blueprint as discussed in Section I getting used. It will likely be seen that the above code handles the “fashioned” case commence air China. Upon WiFi connection, this would presumably well originate an asynchronous DNS inquire in a separate thread known as DnsThread, the utilization of the address within the constant DEFAULT_URL_STRING (hardcoded to “www.google.com” as shown in Section I). Right here’s performed as soon as at the originate of every WiFi connection. However, if inChinaNetwork() returns appropriate, e.g. within the case of a cellular telephone in Hong Kong, it would appropriate discipline the flag mPublicDnsCheckProcess to unsuitable and return, skipping the DNS inquire for google.

Then we watch at ACTIVITY_CHECK_START. On receipt of ACTIVITY_CHECK_START, the NetworkStatsAnalyzer, among assorted actions, sends the ACTIVITY_CHECK_POLL message to itself, and sets the flag mPollingStarted to appropriate. This would kick originate the checking loop, as to be explained under. Earlier than taking these actions, it would build obvious that that the loop is no longer already started by checking mPollingStarted, and that the WiFi connection is no longer acting as a hotspot.

The principle hurry in conjunction with the circulation controlling logic of the physique of ACTIVITY_CHECK_POLL polling loop is as follows (line 4509-).

case ACTIVITY_CHECK_POLL:
if (WifiConnectivityMonitor.this.SMARTCM_DBG) {
    Log.i(TAG, "mPollingStarted : " + mPollingStarted);    
}
if (!mPollingStarted) {
    ruin;
}
if ((WifiConnectivityMonitor.this.mCurrentBssid != null) &&
    (WifiConnectivityMonitor.this.mCurrentBssid != WifiConnectivityMonitor.this.mEmptyBssid)) {
    WifiConnectivityMonitor.this.mIWCChannel.sendMessage(CMD_IWC_ACTIVITY_CHECK_POLL); // 135376
    int rssi2 = wifiInfo.getRssi();
    if (rssi2 < -90) {
        if (!WifiConnectivityMonitor.this.mClientModeImpl.isConnected()) {
            Log.i(TAG, "already disconnected : " + rssi2);
            removeMessages(ACTIVITY_CHECK_POLL);
            sendEmptyMessage(ACTIVITY_CHECK_STOP);
            ruin;
        }
        if (rssi2 < -95) {
            if (rssi2 == -127) break;
            rssi2 = -95;
        }
    }
    /Checking body omitted for the time being
     */
    removeMessages(ACTIVITY_CHECK_POLL);
    sendEmptyMessageDelayed(ACTIVITY_CHECK_POLL, 1000L);
    ...
    break;
}
Log.e(TAG, "currentBssid is null.");
this.removeMessages(ACTIVITY_CHECK_POLL);
this.sendEmptyMessage(ACTIVITY_CHECK_STOP);
break;

The code above ensures that the mPollingStarted flag remains on and WiFi remains connected (by checking if BSSID is empty, or signal is weak and status returned from ClientModeImpl indicates disconnection), before proceeding with various checking (details below).

After each run of the checking code, it resends the ACTIVITY_CHECK_POLL message to itself with a delay of 1000 ms (i.e. 1 second), essentially forming a loop running once per second. In other words, Samsung phones keep running this checking loop, which is not found on standard Android phones, once every second when both screen and WiFi is on! (Furthermore, it also runs another similar loop for “backhaul detection” if mCurrentMode >= 2, no longer lined here).

On the quite lots of hand, if WiFi has been disconnected, the loop will close – ACTIVITY_CHECK_POLL is no longer sent again, and in its assign ACTIVITY_CHECK_STOP is shipped to itself.

For completeness, here is the physique of code handling the ACTIVITY_CHECK_STOP message (line 4492-):

case ACTIVITY_CHECK_STOP:
removeMessages(ACTIVITY_CHECK_POLL);
removeMessages(TCP_BACKHAUL_DETECTION_START); // 135226
mPollingStarted = unsuitable;
mPublicDnsCheckProcess = unsuitable;
...
mDnsQueried = unsuitable;
mDnsInterrupted = unsuitable;
...
ruin;

The above code gets rid of any pending ACTIVITY_CHECK_POLL messages and resets the mPollingStarted flag and assorted flags.

The principle checking – preconditions

Motivate to the fundamental checking physique disregarded above, after performing some statistics relating to packet throughput and stamp energy (no longer lined here), the code checks for the must always close a DNS inquire test (line 4618-, please pronounce that the decompiled code has some points in expanding the nested structure. The code extract under has corrected the difficulty).

if (!WifiConnectivityMonitor.this.mIsScanning && !WifiConnectivityMonitor.this.mIsInRoamSession
        && !WifiConnectivityMonitor.this.mIsInDhcpSession && WifiConnectivityMonitor.this.mIsScreenOn)
{
    if (!this.mPublicDnsCheckProcess && WifiConnectivityMonitor.this.mCurrentMode != 0){
        if (this.mDnsQueried) { // If DNS inquire is ongoing
            ....
            if (WifiConnectivityMonitor.SMARTCM_DBG) {
                Log.i(TAG, "ready dns responses or the fantastic result now!");
            }
            boolean stopQC = unsuitable;
            if (WifiConnectivityMonitor.this.mCurrentMode == 3) {
                if (WifiConnectivityMonitor.this.mInAggGoodStateNow) {
                    stopQC = appropriate;
                }
            } else if (diffRx >= ((prolonged) WifiConnectivityMonitor.this.mParam.mGoodRxPacketsBase) && rxBytesPerPacket > 500) {
                stopQC = appropriate;
            } else if (diffTxBytes >= 100000) {
                stopQC = appropriate;
            }
            if (stopQC) {
                if (WifiConnectivityMonitor.SMARTCM_DBG) {
                    Log.i(TAG, "Appropriate Rx!, don't must always retain evaluating fantastic!");
                }
                if (mDnsQueried) {
                    mSkipRemainingDnsResults = appropriate;
                    mDnsQueried = unsuitable;
                    mDnsInterrupted = unsuitable;
                }
            }
        } else {
            /Additional code to examine DNS, sight under
             */
        }
    }
}

We sight some additional preconditions for performing the DNS inquire test here:

  • WiFi scanning is no longer in progress;
  • WiFi roaming is no longer in progress;
  • DHCP inquire is no longer in progress;
  • Camouflage cloak is grew to become on;
  • mPublicDnsCheckProcess is unsuitable, i.e. no longer doing the preliminary network test above; and
  • mCurrentMode != 0

The first 3 stipulations is for keeping off the DNS inquire below some transient stipulations when the network would possibly presumably well switch. Item 5 avoids doing the preliminary test and the periodic test at the same time. Varied stipulations are for revalidation of the stipulations.

As correctly as, if mDnsQueried is appropriate, the outdated DNS inquire is still ongoing, and a recent inquire would possibly presumably well no longer be even handed as.

As every other, if it is obvious that the connection is in a correct narrate (the utilization of several metrics which would be no longer elaborated here), this would presumably well discipline the flag mSkipRemainingDnsResults to appropriate. From a literal studying of the log messages and variable names, interestingly the blueprint is to manufacture DNS inquire easiest as soon as if the network is in correct situation. However, it is famend that nothing within the code basically reads the mSkipRemainingDnsResults flag! The DNS checking queries would continue to be issued within the loop, even when the connection is correct and real. It is miles no longer identified whether here's a worm, or is deliberate despite the feedback, disclose, as a result of a subsequent build switch.

The core checking allotment

The core phase of the checking loop is as follows (lines 4720-, 4896-)

if (WifiConnectivityMonitor.this.mCurrentMode != 3 || !WifiConnectivityMonitor.this.mInAggGoodStateNow) {
    if (diffRx > 0 || diffTx > 0) {
        if (now - mLastDnsCheckTime > ((prolonged) (WifiConnectivityMonitor.this.mCurrentMode == 3 ? 30000 : 60000))) {
            if (WifiConnectivityMonitor.SMARTCM_DBG) {
                Log.d(TAG, "PERIODIC DNS CHECK TRIGGER (SIMPLE CONNECTION TEST) - Final DNS test turned into as soon as " +
                        ((now4 - mLastDnsCheckTime) / 1000) + " seconds within the past.");
            }
            mNsaQcTrigger = 44;
            needCheckInternetIsAlive = appropriate;
        }
    }

    // Varied stipulations setting the flag needCheckInternetIsAlive;
    
    if (needCheckInternetIsAlive) {
        if (now - mLastDnsCheckTime >= 20000) {
            mCumulativePoorRx.particular();
            mSkipRemainingDnsResults = unsuitable;
            mDnsQueried = appropriate;
            mNsaQcStep = 1;
            int timeoutMS = 10000;
            if (WifiConnectivityMonitor.this.mCurrentMode == 3) {
                timeoutMS = 5000;
            }
            String dnsTargetUrl2 = WifiConnectivityMonitor.this.mParam.DEFAULT_URL_STRING;
            if (WifiConnectivityMonitor.this.inChinaNetwork()) {
                dnsTargetUrl = "www.qq.com";
            } else {
                dnsTargetUrl = dnsTargetUrl2;
            }
            DnsThread mDnsThread = recent DnsThread(appropriate, dnsTargetUrl, this, (prolonged) timeoutMS);
            mDnsThread.originate();
            mLastDnsCheckTime = now;
            WifiConnectivityMonitor.this.mDnsThreadID = mDnsThread.getId();
            if (WifiConnectivityMonitor.DBG) {
                Log.d(TAG, "wait needCheck DnsThread outcomes [" + WifiConnectivityMonitor.this.mDnsThreadID + "]");
            }
        }
    }
}

The first phase determines the “minimal” frequency to inquire the DNS on qq.com, as prolonged as there are any WiFi actions. It would possibly probably presumably per chance discipline the flag needCheckInternetIsAlive to appropriate if it is bigger than 60 seconds since the closing DNS test usually, however if mCurrentMode == 3 meaning “Aggressive WiFi to cellular handover” mode (i.e. requiring a excessive WiFi fantastic or else swap to cellular files, thus needs more aggressive checks), it would shorten the interval to 30 seconds. (But in Aggressive mode, this would easiest be introduced about if connection is no longer in “correct” narrate defined for Aggressive mode). Be wide awake that this loop is traipse as soon as per 2nd when the show cloak cloak is on, meaning that the checking would be traipse approximately every 60 (or 30) seconds, by setting the needCheckInternetIsAlive flag to indicate that a test is wanted.

There are some more criteria for switching on the needCheckInternetIsAlive flag within the subsequent code (disregarded), in response to network metrics gathered. The close is to construct bigger checking when a miserable problem of the WiFi network is detected. This would possibly presumably well build bigger the frequency of DNS to qq.com in some cases. But the frequency is small to at most as soon as per 20 seconds by a later checking.

Lastly, the code sets dnsTargetUrl to www.qq.com (assuming inChinaNetwork() is appropriate in our context. In any other case it would inquire for DEFAULT_URL_STRING, i.e. google), and performs the inquire asynchronously the utilization of the DnsThread class, as with the “fashioned” case below checkPublicDns(). It moreover records basically the latest time for future calculation of time lapsed.

Drilling into DnsThread

Right here is an extract of the DnsThread class. (Most attention-grabbing the case mForce == appropriate below traipse() is shown) (line 6855-)

public final class DnsThread extends Thread {
    ...
    non-public final CountDownLatch latch = recent CountDownLatch(1);

    public DnsThread(boolean power, String url, Handler handler, prolonged timeout) {
        mCallBackHandler = handler;
        if (timeout >= 1000) {
            mTimeout = timeout;
        }
        mForce = power;
        mUrl = url;
    }

    public void traipse() {
        WifiConnectivityMonitor.this.mAnalyticsDisconnectReason = 0;
        if (mForce) {
            HandlerThread dnsPingerThread = recent HandlerThread("dnsPingerThread");
            dnsPingerThread.originate();
            are trying {
                mDnsPingerHandler = recent DnsPingerHandler(dnsPingerThread.getLooper(), mCallBackHandler, getId());
                mDnsPingerHandler.sendDnsPing(this.mUrl, this.mTimeout);
                if (!latch.await(mTimeout, TimeUnit.MILLISECONDS)) {
                    if (WifiConnectivityMonitor.DBG) {
                        Log.d(TAG, "DNS_CHECK_TIMEOUT [" + getId() + "-F] - latch timeout");
                    }
                    mCallBackHandler.sendMessage(WifiConnectivityMonitor.this.obtainMessage(WifiConnectivityMonitor.RESULT_DNS_CHECK, 3, -1, null));
                } else {
                    mCallBackHandler.sendMessage(WifiConnectivityMonitor.this.obtainMessage(WifiConnectivityMonitor.RESULT_DNS_CHECK, mForcedCheckResult, mForcedCheckRtt, mForcedCheckAddress));
                }
            } rep (Exception e) {
                if (WifiConnectivityMonitor.DBG) {
                    Log.d(TAG, "DNS_CHECK_TIMEOUT [" + getId() + "-F] " + e);
                }
                mCallBackHandler.sendMessage(WifiConnectivityMonitor.this.obtainMessage(WifiConnectivityMonitor.RESULT_DNS_CHECK, 3, -1, null));
            }
        } else {
        ...
        }
    }
}

In actuality, when the traipse() blueprint of DnsThread is named, it creates an Android HandlerThread, uses its looper to uncover a recent DnsPingerHandler, and calls sendDnsPing() of the DnsPingerHandler to position an instruct to it to manufacture the DNS inquire. It then waits for the inquire to total on this thread the utilization of a CountDownLatch with the specified timeout, and returns the result by the RESULT_DNS_CHECK message.

The relevant code below DnsPingerHandler(line 7087-):

non-public class DnsPingerHandler extends Handler {
    Handler mCallbackHandler;
    non-public DnsCheck mDnsPingerCheck;
    prolonged mId;

    public DnsPingerHandler(Looper looper, Handler callbackHandler, prolonged identification) {
        clear(looper);
        mDnsPingerCheck = recent DnsCheck(this, "WifiConnectivityMonitor.DnsPingerHandler");
        mCallbackHandler = callbackHandler;
        mId = identification;
    }

    public void sendDnsPing(String url, prolonged timeout) {
        if (!mDnsPingerCheck.requestDnsQuerying(1, (int) timeout, url)) {
            if (WifiConnectivityMonitor.DBG) {
                Log.e(DnsThread.TAG, "DNS List is empty, must always test fantastic");
            }
            if (DnsThread.this.mCallBackHandler != null) {
                DnsThread.this.mCallBackHandler.sendMessage(obtainMessage(WifiConnectivityMonitor.RESULT_DNS_CHECK, 3, -1, null));
                DnsThread.this.latch.countDown();
            }
        }
    }
}

DnsPingerHandler in turn calls requestDnsQuerying() of the class DnsCheck. The blueprint requestDnsQuerying() below DnsCheck class is listed under. (line 7150-)

public class DnsCheck {
    non-public List mDnsServerList = null;
    non-public List mDnsList;
    non-public DnsPinger mDnsPinger;
    non-public HashMap mIdDnsMap = recent HashMap<>();

    public DnsCheck(Handler handler, String designate) {
        mDnsPinger = recent DnsPinger(WifiConnectivityMonitor.this.mContext, designate, handler.getLooper(), handler, 1);
        mDnsCheckTAG = designate;
        mDnsPinger.setCurrentLinkProperties(WifiConnectivityMonitor.this.mLinkProperties);
    }

    public boolean requestDnsQuerying(int num, int timeoutMS, String url) {
        List dnses;
        boolean requested = unsuitable;
        mDnsList = recent ArrayList();
        if (!(WifiConnectivityMonitor.this.mLinkProperties == null || (dnses = WifiConnectivityMonitor.this.mLinkProperties.getDnsServers()) == null || dnses.size() == 0)) {
            mDnsServerList = recent ArrayList(dnses);
        }
        List dnses2 = mDnsServerList;
        if (dnses2 != null) {
            mDnsList.addAll(dnses2);
        }
        int numDnses = mDnsList.size();
        
        ...
        mIdDnsMap.particular();
        for (int i2 = 0; i2 < num; i2++) {
            for (int j = 0; j < numDnses; j++) {
                are trying {
                    if (mDnsList.get(j) == null || mDnsList.get(j).isLoopbackAddress()) {
                        Log.d(DnsThread.TAG, "Loopback address (::1) is detected at DNS" + j);
                    } else {
                        if (url == null) {
                            mIdDnsMap.put(Integer.valueOf(mDnsPinger.pingDnsAsync(mDnsList.get(j), timeoutMS, (i2 0) + 100)), Integer.valueOf(j));
                        } else {
                            mIdDnsMap.put(Integer.valueOf(mDnsPinger.pingDnsAsyncSpecificForce(mDnsList.get(j), timeoutMS, (i2 0) + 100, url)), Integer.valueOf(j));
                        }
                        requested = appropriate;
                    }
                } rep (IndexOutOfBoundsException e2) {
                    if (WifiConnectivityMonitor.DBG) {
                        Log.i(DnsThread.TAG, "IndexOutOfBoundsException");
                    }
                }
            }
        }
        if (WifiConnectivityMonitor.SMARTCM_DBG) {
            Log.i(DnsThread.TAG, "[REQUEST] " + this.mDnsCheckTAG + " : " + this.mIdDnsMap);
        }
        return requested;
    }
}

This retrieves all the DNS servers configured for the connection from LinkProperties (a outmoded Android class). Then it loops by ALL DNS servers, calls the manner pingDnsAsyncSpecificForce(this.mDnsList.get(j), timeoutMS, (i2 0) + 100, url) for every of them. The blueprint is “asynchronous” because it sends a message inside of the DnsPinger class and straight returns. In assorted words, DNS queries for the url are sent simutaneously to all DNS servers configured.

The blueprint pingDnsAsyncSpecificForce() of sophistication DnsPinger is located below a separate Java file, DnsPinger.java. It performs the real DNS inquire with UDP. We're no longer going into its particulars here, alternatively it is valuable that within the constructor of DnsCheck, it is equipped with a assert WiFi connection by the utilization of DnsPinger.setCurrentLinkProperties(), and this would presumably well send its DNS inquire straight to the Wifi interface. In assorted words these DNS queries will disregard the VPN or non-public DNS settings within the cellular machine.

The checking outcomes

For the reason that job is asynchronous, after DnsPinger performed its job (outcomes of DNS obtained or error occurred), this would presumably well send the result the utilization of the message DNS_PING_RESULT_SPECIFIC (593925). The cease result sent help is basically the time wanted for the DNS inquire, or an error code in case of error.  If a non-public network address is returned, it would be handled as an error (mark = 2) [2].

The DNS_PING_RESULT_SPECIFIC is handled by handleMessage of DnsPingerHandler (line 7087-):

if (WifiConnectivityMonitor.SMARTCM_DBG) {
    Log.i(DnsThread.TAG, "[DNS_PING_RESULT_SPECIFIC]");
}
DnsCheck dnsCheck = mDnsPingerCheck;
if (dnsCheck != null) {
    are trying {
        int dnsResult = dnsCheck.checkDnsResult(msg.arg1, msg.arg2, 1);
        if (dnsResult != 10) {
            if (WifiConnectivityMonitor.DBG) {
                Log.d(DnsThread.TAG, "send DNS CHECK End result [" + this.mId + "]");
            }
            DnsThread.this.mForcedCheckResult = dnsResult;
            DnsThread.this.mForcedCheckRtt = msg.arg2;
            DnsThread.this.mForcedCheckAddress = (InetAddress) msg.obj;
            DnsThread.this.latch.countDown();
        } else if (WifiConnectivityMonitor.SMARTCM_DBG) {
            Log.d(DnsThread.TAG, "wait till the responses about remained DNS Query arrive!");
        }
    } rep (NullPointerException ne) {
        Log.e(DnsThread.TAG, "DnsPingerHandler - " + ne);
    }
}

The error stipulations are checked by DnsCheck.checkDnsResult() (no longer elaborated here). It is miles valuable that the outcomes of the DNS inquire (i.e. the IP address of qq.com)is no longer basically used, besides the validity test (to construct obvious that that it is no longer a non-public IP address) below DnsPinger above. Lastly it points countDown() to the CountDownLatch below DnsThread, releasing the CountDownLatch there as mentioned above. This would in fashioned cases trigger DnsThread to send a RESULT_DNS_CHECK message, with the test outcomes, help to the NetworkStatsAnalyzer.

The phase of NetworkStatsAnalyzer handling the message is as follows (line 4426-):

case RESULT_DNS_CHECK:
int mDnsResult = WifiConnectivityMonitor.this.checkDnsThreadResult(message.arg1, message.arg2);
mDnsQueried = unsuitable;
if (mDnsInterrupted) {
    this.mDnsInterrupted = unsuitable;
    if (WifiConnectivityMonitor.DBG) {
        Log.d(TAG, "End result: " + mDnsResult + " - This DNS inquire is interrupted.");
    }
} else if (WifiConnectivityMonitor.this.mIsInDhcpSession || WifiConnectivityMonitor.this.mIsScanning || WifiConnectivityMonitor.this.mIsInRoamSession) {
    if (WifiConnectivityMonitor.DBG) {
        Log.d(TAG, "End result: " + mDnsResult + " - This DNS inquire is interrupted by DHCP session or Scanning.");
    }
} else if (mDnsResult != 0) {
    if (WifiConnectivityMonitor.DBG) {
        Log.e(TAG, "single DNS Checking FAILURE");
    }
    if (WifiConnectivityMonitor.this.mCurrentMode != 3 || !WifiConnectivityMonitor.this.mInAggGoodStateNow) {
        ...
    } else {
        if (WifiConnectivityMonitor.DBG) {
            Log.e(TAG, "But, close no longer test the fantastic in AGG correct rx narrate");
        }
        mSkipRemainingDnsResults = appropriate;
    }
}
mPublicDnsCheckProcess = unsuitable;
ruin;

Processing of the outcomes is basically performed below checkDnsThreadResult(), which finally ends in BssidStatistics.updateBssidLatestDnsResultType() to listing the result for the connection (line 8021). Afterwards, it housekeeps the relevant flags (mDnsQueried and masses others). It moreover sets the unnecessary mSkipRemainingDnsResults flag. (This phase moreover handles the “fashioned” checkPublicDns() inquire and thus moreover clears the mPublicDnsCheckProcess flag).

Ending the DNS queries loop

Lastly, help to the cease degree narrate machine. When the show cloak cloak turns off, the EvaluatedState would cease the loop by sending ACTIVITY_CHECK_STOP to the NetworkStatsAnalyzer (line 2177-).

case EVENT_SCREEN_OFF:
...
screenOffEleInitialize();
removeMessages(WifiConnectivityMonitor.CMD_RSSI_FETCH);
removeMessages(WifiConnectivityMonitor.CMD_TRAFFIC_POLL);
mRssiFetchToken++;
if (WifiConnectivityMonitor.this.mNetworkStatsAnalyzer == null) {
    ruin;
}
mNetworkStatsAnalyzer.sendEmptyMessage(WifiConnectivityMonitor.ACTIVITY_CHECK_STOP);
ruin;

The Latest Mode flag (or how trick the Cell phone to cease the extra DNS checking)

All alongside we sight the reference to mCurrentMode, which when discipline to 0 would disable the DNS checking. Right here's discipline by the determineMode() blueprint (line 3614-):

non-public void determineMode() {
    int i;
    String ssid = mWifiInfo.getSSID();
    if (mCurrentMode != 0) {
        if (isIgnorableNetwork(ssid)) {
            setCurrentMode(0);
        } else if (!mPoorNetworkDetectionEnabled) {
            setCurrentMode(1);
        } else if (isQCExceptionOnly()) {
            if (SMARTCM_DBG) {
                logi("isQCExceptionOnly");
            }
            setCurrentMode(1);
        } else if (isAggressiveModeEnabled()) {
            if (SMARTCM_DBG) {
                logi("mAggressiveModeEnabled");
            }
            setCurrentMode(3);
        } else {
            setCurrentMode(2);
        }
    }
    ...
}

A watch at isIgnorableNetwork() would as a result of this fact present hints on the finest technique to disable the DNS checking loop (line 9611-):

public boolean isIgnorableNetwork(String _ssid) {
    int reason = -1;
    String ssid = null;
    int networkId = -1;
    WifiInfo wifiInfo = this.mWifiInfo;
    if (wifiInfo != null && _ssid == null) {
        ssid = wifiInfo.getSSID();
        networkId = this.mWifiInfo.getNetworkId();
    }
    if (ssid == null && _ssid != null) {
        ssid = _ssid;
    }
    WifiConfiguration wifiConfiguration = null;
    if (networkId != -1) {
        wifiConfiguration = getWifiConfiguration(networkId);
    }
    if ("ATT".equals(SemCscFeature.getInstance().getString(CscFeatureTagWifi.TAG_CSCFEATURE_WIFI_CAPTIVEPORTALEXCEPTION)) && isPackageRunning(this.mContext, "com.synchronoss.dcs.att.r2g")) {
        reason = 1;
    } else if (ssid != null && ssid.comprises("DIRECT-") && ssid.comprises(":NEX-")) {
        reason = 2;
    } else if (isPackageRunning(this.mContext, "de.telekom.hotspotlogin")) {
        reason = 3;
    } else if (isPackageRunning(this.mContext, "com.belgacom.fon")) {
        reason = 4;
    } else if ("CHM".equals(SemCscFeature.getInstance().getString(CscFeatureTagWifi.TAG_CSCFEATURE_WIFI_CAPTIVEPORTALEXCEPTION)) && (isPackageRunning(this.mContext, "com.chinamobile.cmccwifi") || isPackageRunning(this.mContext, "com.chinamobile.cmccwifi.WelcomeActivity") || isPackageRunning(this.mContext, "com.chinamobile.cmccwifi.MainActivity") || isPackageRunning(this.mContext, "com.android.settings.wifi.CMCCChargeWarningDialog"))) {
        reason = 6;
    } else if ((""au_Wi-Fi"".equals(ssid) || ""Wi2"".equals(ssid) || ""Wi2premium"".equals(ssid) || ""Wi2premium_club"".equals(ssid) || ""UQ_Wi-Fi"".equals(ssid) || ""wifi_square"".equals(ssid)) && (isPackageExists("com.kddi.android.au_wifi_connect") || isPackageExists("com.kddi.android.au_wifi_connect2"))) {
        reason = 7;
    } else if (FactoryTest.isFactoryBinary()) {
        reason = 8;
    } else if (""mailsky"".equals(ssid) && this.mIsUsingProxy) {
        reason = 9;
    } else if (""COPconnect"".equals(ssid) && wifiConfiguration.allowedKeyManagement.get(2)) {
        reason = 10;
    } else if (""SpirentATTEVSAP"".equals(ssid)) {
        reason = 11;
    }
    if (reason == -1) {
        return unsuitable;
    }
    Log.d(TAG, "isIgnorableNetwork - No must always test connectivity: " + ssid + ", reason: " + reason);
    return appropriate;
}

Presumably, these are hardcoded guidelines looking out for signs that the network is no longer linked to the Web (e.g. captive portal), and thus there's no longer any must always test for connectivity.  Among the many criteria above, basically the most perfect technique to trigger a WiFi network to be excluded from DNS checking (i.e. making guidelines on how to return appropriate) is to discipline the SSID to have both strings DIRECT- and :NEX-, e.g. DIRECT-:NEX-Dwelling [3].

(Substitute on 17/10/2020: It turned into as soon as found that, for Samsung telephones equipped in Hong Kong or telephones linked to Hong Kong network (as correctly as Macau / Mainland network), the Samsung firmware would possibly presumably well originate DNS queries for, as correctly as connections with, the Mainland internet sites http://www.qq.com, http://www.baidu.com, http://m.taobao.com and http://m.hao123.com, and the peculiar connectivity test modified to http://connectivity.samsung.com.cn/generate_204 in its assign of the default google aim, as phase of captive portal detection individually. The DNS loop above would possibly presumably well trigger such detection.  Critical sides will be discussed in Section 3.)


[1] The narrate machine for WifiConnectivityMonitor is hierarchial. Evaluated narrate covers invalid and legit linked narrate, and Edifying narrate covers several states collectively with Level1 and Level2. It seems that Level2 narrate corresponds to some of narrate the assign the WiFi connection is with some devices (e.g. pedometer) and thus network connectivity checking is no longer wanted.

[2] Right here's obvious by isDnsResponsePrivateAddress() of DnsPinger.

[3] The blueprint isPackageRunning() exams for the Apps working because the foreground process, no longer appropriate for the existence of the equipment. So making a equipment named com.belgacom.fon and placing in it on the machine has no close.