Snap for 6198741 from 731dc404a45c92211c926f3e4dff1fdd06603342 to sdk-release

Change-Id: Ia7156f1e2cfe11e090f39276ca4b12e260f7de78
diff --git a/Android.bp b/Android.bp
index d05881f..a79b14b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,11 +3,8 @@
     export_include_dirs: ["include"],
 }
 
-//For the test to use
-//TODO: 1. Need to Refactor ResolverStats related code(ex: ResolverController::getDnsInfo).
-//         Then removing this library header file.
-//      2. Change the imports path in netd/TEST_MAPPING after migrating from
-//         system/netd/resolv to packages/modules/resolv.
+// Used only by dns_responder_client_ndk.cpp for ResolverStats.h
+// TODO: refactor stats to use a Parcel and eliminate this dependency
 cc_library_headers {
     name: "libnetd_resolv_internal_headers",
     export_include_dirs: ["."],
@@ -18,6 +15,8 @@
     local_include_dir: "binder",
     srcs: [
         "binder/android/net/IDnsResolver.aidl",
+        "binder/android/net/ResolverHostsParcel.aidl",
+        "binder/android/net/ResolverExperimentalOptionsParcel.aidl",
         "binder/android/net/ResolverParamsParcel.aidl",
     ],
     imports: [
@@ -28,16 +27,22 @@
             gen_log: true,
         },
     },
-    api_dir: "aidl/dnsresolver",
     versions: [
         "1",
         "2",
+        "3",
     ],
 }
 
 cc_library {
     name: "libnetd_resolv",
     version_script: "libnetd_resolv.map.txt",
+    stubs: {
+        versions: [
+            "1",
+        ],
+        symbol_file: "libnetd_resolv.map.txt",
+    },
     defaults: ["netd_defaults"],
     srcs: [
         "getaddrinfo.cpp",
@@ -50,12 +55,14 @@
         "res_mkquery.cpp",
         "res_query.cpp",
         "res_send.cpp",
-        "res_state.cpp",
         "res_stats.cpp",
+        "util.cpp",
         "Dns64Configuration.cpp",
         "DnsProxyListener.cpp",
+        "DnsQueryLog.cpp",
         "DnsResolver.cpp",
         "DnsResolverService.cpp",
+        "DnsStats.cpp",
         "DnsTlsDispatcher.cpp",
         "DnsTlsQueryMap.cpp",
         "DnsTlsTransport.cpp",
@@ -69,7 +76,7 @@
     // Link most things statically to minimize our dependence on system ABIs.
     stl: "libc++_static",
     static_libs: [
-        "dnsresolver_aidl_interface-ndk_platform",
+        "dnsresolver_aidl_interface-unstable-ndk_platform",
         "libbase",
         "libcutils",
         "libjsoncpp",
@@ -77,10 +84,10 @@
         "libnetdutils",
         "libprotobuf-cpp-lite",
         "libstatslog_resolv",
-        "libstatssocket",
+        "libstatspush_compat",
         "libsysutils",
-        "libutils",  // Used by libstatslog_resolv
-        "netd_event_listener_interface-V1-ndk_platform",
+        "libutils", // Used by libstatslog_resolv
+        "netd_event_listener_interface-ndk_platform",
         "server_configurable_flags",
         "stats_proto",
     ],
@@ -106,6 +113,16 @@
             ],
         },
     },
+    header_abi_checker: {
+        enabled: true,
+        symbol_file: "libnetd_resolv.map.txt",
+    },
+    sanitize: {
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
 }
 
 cc_library_static {
@@ -132,7 +149,7 @@
 genrule {
     name: "statslog_resolv.cpp",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_resolv.cpp --module resolv --namespace android,net,stats --importHeader statslog_resolv.h",
+    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_resolv.cpp --module resolv --namespace android,net,stats --importHeader statslog_resolv.h --supportQ",
     out: [
         "statslog_resolv.cpp",
     ],
@@ -146,63 +163,25 @@
     export_generated_headers: ["statslog_resolv.h"],
     static_libs: [
         "libcutils",
-        "libstatssocket",
+        "libgtest_prod", // Used by libstatspush_compat
+        "libstatspush_compat",
     ],
 }
 
-cc_test {
-    name: "resolv_integration_test",
-    test_suites: ["device-tests"],
-    require_root: true,
-    defaults: ["netd_defaults"],
-    tidy: false,  // cuts test build time by > 1m30s
-    srcs: [
-        "tests/dns_responder/dns_responder.cpp",
-        "dnsresolver_binder_test.cpp",
-        "resolv_integration_test.cpp",
-    ],
-    header_libs: [
-        "libnetd_resolv_headers",
-    ],
-    shared_libs: [
-        "libbpf_android",
-        "libbinder",
-        "libcrypto",
-        "liblog",
-        "libnetd_client",
-        "libssl",
-        "libutils",
-    ],
-    static_libs: [
-        "dnsresolver_aidl_interface-cpp",
-        "libbase",
-        "libgmock",
-        "libnetd_test_dnsresponder",
-        "libnetd_test_metrics_listener",
-        "libnetd_test_resolv_utils",
-        "libnetd_test_tun_interface",
-        "libnetd_test_utils",
-        "libnetdutils",
-        "netd_aidl_interface-V2-cpp",
-        "netd_event_listener_interface-V1-cpp",
-    ],
-    compile_multilib: "both",
-    sanitize: {
-        address: true,
-        recover: ["all"],
-    },
-}
-
+// TODO: Move this test to tests/
 cc_test {
     name: "resolv_unit_test",
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "mts"],
     require_root: true,
     //TODO:  drop root privileges and make it be an real unit test.
     defaults: ["netd_defaults"],
     srcs: [
         "resolv_cache_unit_test.cpp",
+        "resolv_callback_unit_test.cpp",
         "resolv_tls_unit_test.cpp",
         "resolv_unit_test.cpp",
+        "DnsStatsTest.cpp",
+        "DnsQueryLogTest.cpp",
     ],
     shared_libs: [
         "libbase",
@@ -212,9 +191,8 @@
         "libbinder_ndk",
     ],
     static_libs: [
-        "dnsresolver_aidl_interface-V2-cpp",
-        "dnsresolver_aidl_interface-V2-ndk_platform",
-        "netd_event_listener_interface-V1-ndk_platform",
+        "dnsresolver_aidl_interface-unstable-ndk_platform",
+        "netd_event_listener_interface-ndk_platform",
         "libgmock",
         "liblog",
         "libnetd_resolv",
@@ -222,7 +200,21 @@
         "libnetd_test_resolv_utils",
         "libnetdutils",
         "libprotobuf-cpp-lite",
+        "libstatslog_resolv",
+        "libstatspush_compat",
+        "libsysutils",
+        "libutils",
+        "resolv_stats_test_utils",
         "server_configurable_flags",
         "stats_proto",
     ],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
 }
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index ce6081d..2046c34 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -24,6 +24,7 @@
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <resolv.h>  // b64_pton()
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -54,10 +55,10 @@
 #include "ResolverEventReporter.h"
 #include "getaddrinfo.h"
 #include "gethnamaddr.h"
-#include "netd_resolv/stats.h"  // RCODE_TIMEOUT
 #include "res_send.h"
 #include "resolv_cache.h"
 #include "resolv_private.h"
+#include "stats.h"  // RCODE_TIMEOUT
 #include "stats.pb.h"
 
 using aidl::android::net::metrics::INetdEventListener;
@@ -74,10 +75,6 @@
 // Limits the number of outstanding DNS queries by client UID.
 constexpr int MAX_QUERIES_PER_UID = 256;
 
-// Max packet size for answer, sync with getaddrinfo.c
-// TODO: switch to dynamically allocated buffers with std::vector
-constexpr int MAXPACKET = 8 * 1024;
-
 android::netdutils::OperationLimiter<uid_t> queryLimiter(MAX_QUERIES_PER_UID);
 
 void logArguments(int argc, char** argv) {
@@ -147,7 +144,7 @@
     return false;
 }
 
-void maybeFixupNetContext(android_net_context* ctx) {
+void maybeFixupNetContext(android_net_context* ctx, pid_t pid) {
     if (requestingUseLocalNameservers(ctx->flags) && !hasPermissionToBypassPrivateDns(ctx->uid)) {
         // Not permitted; clear the flag.
         ctx->flags &= ~NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
@@ -161,6 +158,7 @@
             ctx->flags |= NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS;
         }
     }
+    ctx->pid = pid;
 }
 
 void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
@@ -293,7 +291,23 @@
     return true;
 }
 
-void initDnsEvent(NetworkDnsEventReported* event) {
+// Note: Even if it returns PDM_OFF, it doesn't mean there's no DoT stats in the message
+// because Private DNS mode can change at any time.
+PrivateDnsModes getPrivateDnsModeForMetrics(uint32_t netId) {
+    switch (gPrivateDnsConfiguration.getStatus(netId).mode) {
+        case PrivateDnsMode::OFF:
+            // It can also be due to netId not found.
+            return PrivateDnsModes::PDM_OFF;
+        case PrivateDnsMode::OPPORTUNISTIC:
+            return PrivateDnsModes::PDM_OPPORTUNISTIC;
+        case PrivateDnsMode::STRICT:
+            return PrivateDnsModes::PDM_STRICT;
+        default:
+            return PrivateDnsModes::PDM_UNKNOWN;
+    }
+}
+
+void initDnsEvent(NetworkDnsEventReported* event, const android_net_context& netContext) {
     // The value 0 has the special meaning of unset/unknown in Westworld atoms. So, we set both
     // flags to -1 as default value.
     //  1. The hints flag is only used in resolv_getaddrinfo. When user set it to -1 in
@@ -304,6 +318,7 @@
     //     resolv_res_nsend,res_nsend will do nothing special by the setting.
     event->set_hints_ai_flags(-1);
     event->set_res_nsend_flags(-1);
+    event->set_private_dns_modes(getPrivateDnsModeForMetrics(netContext.dns_netid));
 }
 
 // Return 0 if the event should not be logged.
@@ -315,6 +330,24 @@
     return (arc4random_uniform(subsampling_denom) == 0) ? subsampling_denom : 0;
 }
 
+void maybeLogQuery(int eventType, const android_net_context& netContext,
+                   const NetworkDnsEventReported& event, const std::string& query_name,
+                   const std::vector<std::string>& ip_addrs) {
+    // Skip reverse queries.
+    if (eventType == INetdEventListener::EVENT_GETHOSTBYADDR) return;
+
+    for (const auto& query : event.dns_query_events().dns_query_event()) {
+        // Log it when the cache misses.
+        if (query.cache_hit() != CS_FOUND) {
+            const int timeTakenMs = event.latency_micros() / 1000;
+            DnsQueryLog::Record record(netContext.dns_netid, netContext.uid, netContext.pid,
+                                       query_name, ip_addrs, timeTakenMs);
+            gDnsResolv->dnsQueryLog().push(std::move(record));
+            return;
+        }
+    }
+}
+
 void reportDnsEvent(int eventType, const android_net_context& netContext, int latencyUs,
                     int returnCode, NetworkDnsEventReported& event, const std::string& query_name,
                     const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) {
@@ -329,6 +362,8 @@
                                          event.private_dns_modes(), dnsQueryBytesField, rate);
     }
 
+    maybeLogQuery(eventType, netContext, event, query_name, ip_addrs);
+
     const auto& listeners = ResolverEventReporter::getInstance().getListeners();
     if (listeners.size() == 0) {
         LOG(ERROR) << __func__
@@ -538,6 +573,13 @@
     free(mHints);
 }
 
+static bool evaluate_domain_name(const android_net_context &netcontext,
+                                 const char *host) {
+    if (!gResNetdCallbacks.evaluate_domain_name)
+        return true;
+    return gResNetdCallbacks.evaluate_domain_name(netcontext, host);
+}
+
 static bool sendBE32(SocketClient* c, uint32_t data) {
     uint32_t be_data = htonl(data);
     return c->sendData(&be_data, sizeof(be_data)) == 0;
@@ -669,13 +711,18 @@
 
     addrinfo* result = nullptr;
     Stopwatch s;
-    maybeFixupNetContext(&mNetContext);
+    maybeFixupNetContext(&mNetContext, mClient->getPid());
     const uid_t uid = mClient->getUid();
     int32_t rv = 0;
     NetworkDnsEventReported event;
-    initDnsEvent(&event);
+    initDnsEvent(&event, mNetContext);
     if (queryLimiter.start(uid)) {
-        rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, &result, &event);
+        if (evaluate_domain_name(mNetContext, mHost)) {
+            rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, &result,
+                                    &event);
+        } else {
+            rv = EAI_SYSTEM;
+        }
         queryLimiter.finish(uid);
     } else {
         // Note that this error code is currently not passed down to the client.
@@ -856,7 +903,7 @@
                << mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}";
 
     Stopwatch s;
-    maybeFixupNetContext(&mNetContext);
+    maybeFixupNetContext(&mNetContext, mClient->getPid());
 
     // Decode
     std::vector<uint8_t> msg(MAXPACKET, 0);
@@ -885,12 +932,17 @@
 
     // Send DNS query
     std::vector<uint8_t> ansBuf(MAXPACKET, 0);
-    int arcode, nsendAns = -1;
+    int rcode = ns_r_noerror;
+    int nsendAns = -1;
     NetworkDnsEventReported event;
-    initDnsEvent(&event);
+    initDnsEvent(&event, mNetContext);
     if (queryLimiter.start(uid)) {
-        nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
-                                    &arcode, static_cast<ResNsendFlags>(mFlags), &event);
+        if (evaluate_domain_name(mNetContext, rr_name.c_str())) {
+            nsendAns = resolv_res_nsend(&mNetContext, msg.data(), msgLen, ansBuf.data(), MAXPACKET,
+                                        &rcode, static_cast<ResNsendFlags>(mFlags), &event);
+        } else {
+            nsendAns = -EAI_SYSTEM;
+        }
         queryLimiter.finish(uid);
     } else {
         LOG(WARNING) << "ResNSendHandler::run: resnsend: from UID " << uid
@@ -908,13 +960,13 @@
         sendBE32(mClient, nsendAns);
         if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
             reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
-                           resNSendToAiError(nsendAns, arcode), event, rr_name);
+                           resNSendToAiError(nsendAns, rcode), event, rr_name);
         }
         return;
     }
 
     // Send rcode
-    if (!sendBE32(mClient, arcode)) {
+    if (!sendBE32(mClient, rcode)) {
         PLOG(WARNING) << "ResNSendHandler::run: resnsend: failed to send rcode to uid " << uid;
         return;
     }
@@ -931,7 +983,7 @@
         const int total_ip_addr_count =
                 extractResNsendAnswers((uint8_t*) ansBuf.data(), nsendAns, rr_type, &ip_addrs);
         reportDnsEvent(INetdEventListener::EVENT_RES_NSEND, mNetContext, latencyUs,
-                       resNSendToAiError(nsendAns, arcode), event, rr_name, ip_addrs,
+                       resNSendToAiError(nsendAns, rcode), event, rr_name, ip_addrs,
                        total_ip_addr_count);
     }
 }
@@ -1073,17 +1125,21 @@
 
 void DnsProxyListener::GetHostByNameHandler::run() {
     Stopwatch s;
-    maybeFixupNetContext(&mNetContext);
+    maybeFixupNetContext(&mNetContext, mClient->getPid());
     const uid_t uid = mClient->getUid();
     hostent* hp = nullptr;
     hostent hbuf;
     char tmpbuf[MAXPACKET];
     int32_t rv = 0;
     NetworkDnsEventReported event;
-    initDnsEvent(&event);
+    initDnsEvent(&event, mNetContext);
     if (queryLimiter.start(uid)) {
-        rv = resolv_gethostbyname(mName, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp,
-                                  &event);
+        if (evaluate_domain_name(mNetContext, mName)) {
+            rv = resolv_gethostbyname(mName, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp,
+                                      &event);
+        } else {
+            rv = EAI_SYSTEM;
+        }
         queryLimiter.finish(uid);
     } else {
         rv = EAI_MEMORY;
@@ -1236,14 +1292,14 @@
 
 void DnsProxyListener::GetHostByAddrHandler::run() {
     Stopwatch s;
-    maybeFixupNetContext(&mNetContext);
+    maybeFixupNetContext(&mNetContext, mClient->getPid());
     const uid_t uid = mClient->getUid();
     hostent* hp = nullptr;
     hostent hbuf;
     char tmpbuf[MAXPACKET];
     int32_t rv = 0;
     NetworkDnsEventReported event;
-    initDnsEvent(&event);
+    initDnsEvent(&event, mNetContext);
     if (queryLimiter.start(uid)) {
         rv = resolv_gethostbyaddr(mAddress, mAddressLen, mAddressFamily, &hbuf, tmpbuf,
                                   sizeof tmpbuf, &mNetContext, &hp, &event);
diff --git a/DnsQueryLog.cpp b/DnsQueryLog.cpp
new file mode 100644
index 0000000..6f0e179
--- /dev/null
+++ b/DnsQueryLog.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "DnsQueryLog.h"
+
+#include <android-base/stringprintf.h>
+
+namespace android::net {
+
+namespace {
+
+std::string maskHostname(const std::string& hostname) {
+    // Boundary issue is handled in substr().
+    return hostname.substr(0, 1) + "***";
+}
+
+// Return the string of masked addresses of the first v4 address and the first v6 address.
+std::string maskIps(const std::vector<std::string>& ips) {
+    std::string ret;
+    bool v4Found = false, v6Found = false;
+    for (const auto& ip : ips) {
+        if (auto pos = ip.find_first_of(':'); pos != ip.npos && !v6Found) {
+            ret += ip.substr(0, pos + 1) + "***, ";
+            v6Found = true;
+        } else if (auto pos = ip.find_first_of('.'); pos != ip.npos && !v4Found) {
+            ret += ip.substr(0, pos + 1) + "***, ";
+            v4Found = true;
+        }
+        if (v6Found && v4Found) break;
+    }
+    return ret.empty() ? "" : ret.substr(0, ret.length() - 2);
+}
+
+// Return the readable string format "hr:min:sec.ms".
+std::string timestampToString(const std::chrono::system_clock::time_point& ts) {
+    using std::chrono::duration_cast;
+    using std::chrono::milliseconds;
+    const auto time_sec = std::chrono::system_clock::to_time_t(ts);
+    char buf[32];
+    std::strftime(buf, sizeof(buf), "%H:%M:%S", std::localtime(&time_sec));
+    int ms = duration_cast<milliseconds>(ts.time_since_epoch()).count() % 1000;
+    return android::base::StringPrintf("%s.%03d", buf, ms);
+}
+
+}  // namespace
+
+void DnsQueryLog::push(Record&& record) {
+    std::lock_guard guard(mLock);
+    mQueue.push_back(std::move(record));
+    if (mQueue.size() > mCapacity) {
+        mQueue.pop_front();
+    }
+}
+
+void DnsQueryLog::dump(netdutils::DumpWriter& dw) const {
+    dw.println("DNS query log (last %lld minutes):", (mValidityTimeMs / 60000).count());
+    netdutils::ScopedIndent indentStats(dw);
+    const auto now = std::chrono::system_clock::now();
+
+    std::lock_guard guard(mLock);
+    for (const auto& record : mQueue) {
+        if (now - record.timestamp > mValidityTimeMs) continue;
+
+        const std::string maskedHostname = maskHostname(record.hostname);
+        const std::string maskedIpsStr = maskIps(record.addrs);
+        const std::string time = timestampToString(record.timestamp);
+        dw.println("time=%s netId=%u uid=%u pid=%d hostname=%s answer=[%s] (%dms)", time.c_str(),
+                   record.netId, record.uid, record.pid, maskedHostname.c_str(),
+                   maskedIpsStr.c_str(), record.timeTaken);
+    }
+}
+
+}  // namespace android::net
diff --git a/DnsQueryLog.h b/DnsQueryLog.h
new file mode 100644
index 0000000..c19f8db
--- /dev/null
+++ b/DnsQueryLog.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/DumpWriter.h>
+
+namespace android::net {
+
+// A circular buffer based class used for query logging. It's thread-safe for concurrent access.
+class DnsQueryLog {
+  public:
+    static constexpr std::string_view DUMP_KEYWORD = "querylog";
+
+    struct Record {
+        Record(uint32_t netId, uid_t uid, pid_t pid, const std::string& hostname,
+               const std::vector<std::string>& addrs, int timeTaken)
+            : netId(netId),
+              uid(uid),
+              pid(pid),
+              hostname(hostname),
+              addrs(addrs),
+              timeTaken(timeTaken) {}
+        const uint32_t netId;
+        const uid_t uid;
+        const pid_t pid;
+        const std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
+        const std::string hostname;
+        const std::vector<std::string> addrs;
+        const int timeTaken;
+    };
+
+    // Allow the tests to set the capacity and the validaty time in milliseconds.
+    DnsQueryLog(size_t size = kDefaultLogSize,
+                std::chrono::milliseconds time = kDefaultValidityMinutes)
+        : mCapacity(size), mValidityTimeMs(time) {}
+
+    void push(Record&& record) EXCLUDES(mLock);
+    void dump(netdutils::DumpWriter& dw) const EXCLUDES(mLock);
+
+  private:
+    mutable std::mutex mLock;
+    std::deque<Record> mQueue GUARDED_BY(mLock);
+    const size_t mCapacity;
+    const std::chrono::milliseconds mValidityTimeMs;
+
+    // The capacity of the circular buffer.
+    static constexpr size_t kDefaultLogSize = 200;
+
+    // Limit to dump the queries within last |kDefaultValidityMinutes| minutes.
+    static constexpr std::chrono::minutes kDefaultValidityMinutes{60};
+};
+
+}  // namespace android::net
diff --git a/DnsQueryLogTest.cpp b/DnsQueryLogTest.cpp
new file mode 100644
index 0000000..b652412
--- /dev/null
+++ b/DnsQueryLogTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <regex>
+#include <thread>
+
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "DnsQueryLog.h"
+
+using namespace std::chrono_literals;
+
+namespace android::net {
+
+namespace {
+
+// Dump the log to STDOUT and capture it.
+std::string captureDumpOutput(const DnsQueryLog& queryLog) {
+    netdutils::DumpWriter dw(STDOUT_FILENO);
+    CapturedStdout captured;
+    queryLog.dump(dw);
+    return captured.str();
+}
+
+// A simple check for the dump result by checking the netIds one by one.
+void verifyDumpOutput(const std::string& dumpLog, const std::vector<int>& expectedNetIds) {
+    // Capture three matches: netId, hostname, and answer (empty allowed).
+    static const std::regex pattern(
+            R"(netId=(\d+).* hostname=([\w\*]+) answer=\[([\w:,\.\*\s]*)\])");
+
+    std::string str(dumpLog);
+    std::smatch sm;
+    for (const auto& netId : expectedNetIds) {
+        SCOPED_TRACE(netId);
+        EXPECT_TRUE(std::regex_search(str, sm, pattern));
+        EXPECT_EQ(sm[1], std::to_string(netId));
+        str = sm.suffix();
+    }
+
+    // Ensure the dumpLog is exactly as expected.
+    EXPECT_FALSE(std::regex_search(str, sm, pattern));
+}
+
+}  // namespace
+
+class DnsQueryLogTest : public ::testing::Test {
+  protected:
+    const std::vector<std::string> serversV4 = {"127.0.0.1", "1.2.3.4"};
+    const std::vector<std::string> serversV4V6 = {"127.0.0.1", "1.2.3.4", "2001:db8::1",
+                                                  "fe80:1::2%testnet"};
+};
+
+TEST_F(DnsQueryLogTest, Push) {
+    std::vector<DnsQueryLog::Record> records = {
+            DnsQueryLog::Record(30, 1000, 1000, "example.com", serversV4, 10),
+            DnsQueryLog::Record(31, 1000, 1000, "", serversV4, 10),      // Empty hostname.
+            DnsQueryLog::Record(32, 1000, 1000, "example.com", {}, 10),  // No answer.
+            DnsQueryLog::Record(33, 1000, 1000, "example.com", serversV4V6, 10),
+    };
+    DnsQueryLog queryLog;
+    for (auto& r : records) {
+        queryLog.push(std::move(r));
+    }
+
+    std::string output = captureDumpOutput(queryLog);
+    verifyDumpOutput(output, {30, 31, 32, 33});
+}
+
+TEST_F(DnsQueryLogTest, PushStressTest) {
+    const int threadNum = 100;
+    const int pushNum = 1000;
+    const size_t size = 500;
+    DnsQueryLog queryLog(size);
+    std::vector<std::thread> threads(threadNum);
+
+    // Launch 'threadNum' threads to push the same queryLog 'pushNum' times.
+    for (auto& thread : threads) {
+        thread = std::thread([&]() {
+            for (int i = 0; i < pushNum; i++) {
+                DnsQueryLog::Record record(30, 1000, 1000, "www.example.com", serversV4, 10);
+                queryLog.push(std::move(record));
+            }
+        });
+    }
+    for (auto& thread : threads) {
+        thread.join();
+    }
+
+    // Verify there are exact 'size' records in queryLog.
+    std::string output = captureDumpOutput(queryLog);
+    verifyDumpOutput(output, std::vector(size, 30));
+}
+
+TEST_F(DnsQueryLogTest, ZeroSize) {
+    const size_t size = 0;
+    DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10);
+    DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10);
+    DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10);
+
+    DnsQueryLog queryLog(size);
+    queryLog.push(std::move(r1));
+    queryLog.push(std::move(r2));
+    queryLog.push(std::move(r3));
+
+    std::string output = captureDumpOutput(queryLog);
+    verifyDumpOutput(output, {});
+}
+
+TEST_F(DnsQueryLogTest, CapacityFull) {
+    const size_t size = 3;
+    DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10);
+    DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10);
+    DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10);
+    DnsQueryLog::Record r4(33, 1000, 1000, "www.example4.com", serversV4V6, 10);
+    const std::vector<int> expectedNetIds = {31, 32, 33};
+
+    DnsQueryLog queryLog(size);
+    queryLog.push(std::move(r1));
+    queryLog.push(std::move(r2));
+    queryLog.push(std::move(r3));
+    queryLog.push(std::move(r4));
+
+    std::string output = captureDumpOutput(queryLog);
+    verifyDumpOutput(output, expectedNetIds);
+}
+
+TEST_F(DnsQueryLogTest, ValidityTime) {
+    DnsQueryLog::Record r1(30, 1000, 1000, "www.example.com", serversV4, 10);
+    DnsQueryLog queryLog(3, 100ms);
+    queryLog.push(std::move(r1));
+
+    // Dump the output and verify the correctness by checking netId.
+    std::string output = captureDumpOutput(queryLog);
+    verifyDumpOutput(output, {30});
+
+    std::this_thread::sleep_for(150ms);
+
+    // The record is expired thus not shown in the output.
+    output = captureDumpOutput(queryLog);
+    verifyDumpOutput(output, {});
+
+    // Push another record to ensure it still works.
+    DnsQueryLog::Record r2(31, 1000, 1000, "example.com", serversV4V6, 10);
+    queryLog.push(std::move(r2));
+    output = captureDumpOutput(queryLog);
+    verifyDumpOutput(output, {31});
+}
+
+}  // namespace android::net
diff --git a/DnsResolver.cpp b/DnsResolver.cpp
index 70ecc7e..9dba693 100644
--- a/DnsResolver.cpp
+++ b/DnsResolver.cpp
@@ -38,6 +38,7 @@
     gResNetdCallbacks.log = callbacks->log;
     if (gApiLevel >= 30) {
         gResNetdCallbacks.tagSocket = callbacks->tagSocket;
+        gResNetdCallbacks.evaluate_domain_name = callbacks->evaluate_domain_name;
     }
     android::net::gDnsResolv = android::net::DnsResolver::getInstance();
     return android::net::gDnsResolv->start();
diff --git a/DnsResolver.h b/DnsResolver.h
index 2efa341..863b096 100644
--- a/DnsResolver.h
+++ b/DnsResolver.h
@@ -18,6 +18,7 @@
 #define _DNS_RESOLVER_H_
 
 #include "DnsProxyListener.h"
+#include "DnsQueryLog.h"
 #include "ResolverController.h"
 #include "netd_resolv/resolv.h"
 #include "netdutils/Log.h"
@@ -34,11 +35,14 @@
     DnsResolver(DnsResolver const&) = delete;
     void operator=(DnsResolver const&) = delete;
 
+    DnsQueryLog& dnsQueryLog() { return mQueryLog; }
+
     ResolverController resolverCtrl;
 
   private:
     DnsResolver() {}
     DnsProxyListener mDnsProxyListener;
+    DnsQueryLog mQueryLog;
 };
 
 extern DnsResolver* gDnsResolv;
diff --git a/DnsResolverService.cpp b/DnsResolverService.cpp
index 3b83381..c4b42bc 100644
--- a/DnsResolverService.cpp
+++ b/DnsResolverService.cpp
@@ -92,7 +92,7 @@
     return STATUS_OK;
 }
 
-binder_status_t DnsResolverService::dump(int fd, const char**, uint32_t) {
+binder_status_t DnsResolverService::dump(int fd, const char** args, uint32_t numArgs) {
     auto dump_permission = checkAnyPermission({PERM_DUMP});
     if (!dump_permission.isOk()) {
         return STATUS_PERMISSION_DENIED;
@@ -101,6 +101,14 @@
     // This method does not grab any locks. If individual classes need locking
     // their dump() methods MUST handle locking appropriately.
     DumpWriter dw(fd);
+
+    if (numArgs == 1 && std::string(args[0]) == DnsQueryLog::DUMP_KEYWORD) {
+        dw.blankline();
+        gDnsResolv->dnsQueryLog().dump(dw);
+        dw.blankline();
+        return STATUS_OK;
+    }
+
     for (auto netId : resolv_list_caches()) {
         dw.println("NetId: %u", netId);
         gDnsResolv->resolverCtrl.dump(dw, netId);
@@ -166,6 +174,13 @@
         const ResolverParamsParcel& resolverParams) {
     // Locking happens in PrivateDnsConfiguration and res_* functions.
     ENFORCE_INTERNAL_PERMISSIONS();
+
+    uid_t uid = AIBinder_getCallingUid();
+    if (resolverParams.caCertificate.size() != 0 && uid == AID_SYSTEM) {
+        auto err = StringPrintf("UID %d is not authorized to set a non-empty CA certificate", uid);
+        return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(EX_SECURITY, err.c_str()));
+    }
+
     // TODO: Remove this log after AIDL gen_log supporting more types, b/129732660
     auto entry =
             gDnsResolverLog.newEntry()
@@ -249,5 +264,14 @@
     return statusFromErrcode(res);
 }
 
+::ndk::ScopedAStatus DnsResolverService::flushNetworkCache(int netId) {
+    // Locking happens in res_cache.cpp functions.
+    ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+    int res = gDnsResolv->resolverCtrl.flushNetworkCache(netId);
+
+    return statusFromErrcode(res);
+}
+
 }  // namespace net
 }  // namespace android
diff --git a/DnsResolverService.h b/DnsResolverService.h
index 1a1c9e7..e484b3c 100644
--- a/DnsResolverService.h
+++ b/DnsResolverService.h
@@ -50,6 +50,7 @@
             std::vector<int32_t>* wait_for_pending_req_timeout_count) override;
     ::ndk::ScopedAStatus destroyNetworkCache(int32_t netId) override;
     ::ndk::ScopedAStatus createNetworkCache(int32_t netId) override;
+    ::ndk::ScopedAStatus flushNetworkCache(int32_t netId) override;
 
     // DNS64-related commands
     ::ndk::ScopedAStatus startPrefix64Discovery(int32_t netId) override;
diff --git a/DnsStats.cpp b/DnsStats.cpp
new file mode 100644
index 0000000..970e30e
--- /dev/null
+++ b/DnsStats.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "resolv"
+
+#include "DnsStats.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android::net {
+
+using base::StringPrintf;
+using netdutils::DumpWriter;
+using netdutils::IPAddress;
+using netdutils::IPSockAddr;
+using netdutils::ScopedIndent;
+using std::chrono::duration_cast;
+using std::chrono::microseconds;
+using std::chrono::milliseconds;
+using std::chrono::seconds;
+
+namespace {
+
+static constexpr IPAddress INVALID_IPADDRESS = IPAddress();
+
+std::string rcodeToName(int rcode) {
+    // clang-format off
+    switch (rcode) {
+        case NS_R_NO_ERROR: return "NOERROR";
+        case NS_R_FORMERR: return "FORMERR";
+        case NS_R_SERVFAIL: return "SERVFAIL";
+        case NS_R_NXDOMAIN: return "NXDOMAIN";
+        case NS_R_NOTIMPL: return "NOTIMP";
+        case NS_R_REFUSED: return "REFUSED";
+        case NS_R_YXDOMAIN: return "YXDOMAIN";
+        case NS_R_YXRRSET: return "YXRRSET";
+        case NS_R_NXRRSET: return "NXRRSET";
+        case NS_R_NOTAUTH: return "NOTAUTH";
+        case NS_R_NOTZONE: return "NOTZONE";
+        case NS_R_INTERNAL_ERROR: return "INTERNAL_ERROR";
+        case NS_R_TIMEOUT: return "TIMEOUT";
+        default: return StringPrintf("UNKNOWN(%d)", rcode);
+    }
+    // clang-format on
+}
+
+bool ensureNoInvalidIp(const std::vector<IPSockAddr>& servers) {
+    for (const auto& server : servers) {
+        if (server.ip() == INVALID_IPADDRESS || server.port() == 0) {
+            LOG(WARNING) << "Invalid server: " << server;
+            return false;
+        }
+    }
+    return true;
+}
+
+}  // namespace
+
+// The comparison ignores the last update time.
+bool StatsData::operator==(const StatsData& o) const {
+    return std::tie(serverSockAddr, total, rcodeCounts, latencyUs) ==
+           std::tie(o.serverSockAddr, o.total, o.rcodeCounts, o.latencyUs);
+}
+
+std::string StatsData::toString() const {
+    if (total == 0) return StringPrintf("%s <no data>", serverSockAddr.ip().toString().c_str());
+
+    const auto now = std::chrono::steady_clock::now();
+    const int meanLatencyMs = duration_cast<milliseconds>(latencyUs).count() / total;
+    const int lastUpdateSec = duration_cast<seconds>(now - lastUpdate).count();
+    std::string buf;
+    for (const auto& [rcode, counts] : rcodeCounts) {
+        if (counts != 0) {
+            buf += StringPrintf("%s:%d ", rcodeToName(rcode).c_str(), counts);
+        }
+    }
+    return StringPrintf("%s (%d, %dms, [%s], %ds)", serverSockAddr.ip().toString().c_str(), total,
+                        meanLatencyMs, buf.c_str(), lastUpdateSec);
+}
+
+StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size)
+    : mCapacity(size), mStatsData(ipSockAddr) {}
+
+void StatsRecords::push(const Record& record) {
+    updateStatsData(record, true);
+    mRecords.push_back(record);
+
+    if (mRecords.size() > mCapacity) {
+        updateStatsData(mRecords.front(), false);
+        mRecords.pop_front();
+    }
+}
+
+void StatsRecords::updateStatsData(const Record& record, const bool add) {
+    const int rcode = record.rcode;
+    if (add) {
+        mStatsData.total += 1;
+        mStatsData.rcodeCounts[rcode] += 1;
+        mStatsData.latencyUs += record.latencyUs;
+    } else {
+        mStatsData.total -= 1;
+        mStatsData.rcodeCounts[rcode] -= 1;
+        mStatsData.latencyUs -= record.latencyUs;
+    }
+    mStatsData.lastUpdate = std::chrono::steady_clock::now();
+}
+
+bool DnsStats::setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol) {
+    if (!ensureNoInvalidIp(servers)) return false;
+
+    ServerStatsMap& statsMap = mStats[protocol];
+    for (const auto& server : servers) {
+        statsMap.try_emplace(server, StatsRecords(server, kLogSize));
+    }
+
+    // Clean up the map to eliminate the nodes not belonging to the given list of servers.
+    const auto cleanup = [&](ServerStatsMap* statsMap) {
+        ServerStatsMap tmp;
+        for (const auto& server : servers) {
+            if (statsMap->find(server) != statsMap->end()) {
+                tmp.insert(statsMap->extract(server));
+            }
+        }
+        statsMap->swap(tmp);
+    };
+
+    cleanup(&statsMap);
+
+    return true;
+}
+
+bool DnsStats::addStats(const IPSockAddr& ipSockAddr, const DnsQueryEvent& record) {
+    if (ipSockAddr.ip() == INVALID_IPADDRESS) return false;
+
+    for (auto& [serverSockAddr, statsRecords] : mStats[record.protocol()]) {
+        if (serverSockAddr == ipSockAddr) {
+            const StatsRecords::Record rec = {
+                    .rcode = record.rcode(),
+                    .latencyUs = microseconds(record.latency_micros()),
+            };
+            statsRecords.push(rec);
+            return true;
+        }
+    }
+    return false;
+}
+
+std::vector<StatsData> DnsStats::getStats(Protocol protocol) const {
+    std::vector<StatsData> ret;
+
+    if (mStats.find(protocol) != mStats.end()) {
+        for (const auto& [_, statsRecords] : mStats.at(protocol)) {
+            ret.push_back(statsRecords.getStatsData());
+        }
+    }
+    return ret;
+}
+
+void DnsStats::dump(DumpWriter& dw) {
+    const auto dumpStatsMap = [&](ServerStatsMap& statsMap) {
+        ScopedIndent indentLog(dw);
+        if (statsMap.size() == 0) {
+            dw.println("<no server>");
+            return;
+        }
+        for (const auto& [_, statsRecords] : statsMap) {
+            dw.println("%s", statsRecords.getStatsData().toString().c_str());
+        }
+    };
+
+    dw.println("Server statistics: (total, RTT avg, {rcode:counts}, last update)");
+    ScopedIndent indentStats(dw);
+
+    dw.println("over UDP");
+    dumpStatsMap(mStats[PROTO_UDP]);
+
+    dw.println("over TLS");
+    dumpStatsMap(mStats[PROTO_DOT]);
+
+    dw.println("over TCP");
+    dumpStatsMap(mStats[PROTO_TCP]);
+}
+
+}  // namespace android::net
diff --git a/DnsStats.h b/DnsStats.h
new file mode 100644
index 0000000..40dad94
--- /dev/null
+++ b/DnsStats.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <chrono>
+#include <deque>
+#include <map>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/DumpWriter.h>
+#include <netdutils/InternetAddresses.h>
+
+#include "ResolverStats.h"
+#include "stats.pb.h"
+
+namespace android::net {
+
+// The overall information of a StatsRecords.
+struct StatsData {
+    StatsData(const netdutils::IPSockAddr& ipSockAddr) : serverSockAddr(ipSockAddr) {
+        lastUpdate = std::chrono::steady_clock::now();
+    };
+
+    // Server socket address.
+    netdutils::IPSockAddr serverSockAddr;
+
+    // The most recent number of records being accumulated.
+    int total = 0;
+
+    // The map used to store the number of each rcode.
+    std::map<int, int> rcodeCounts;
+
+    // The aggregated RTT in microseconds.
+    // For DNS-over-TCP, it includes TCP handshake.
+    // For DNS-over-TLS, it might include TCP handshake plus SSL handshake.
+    std::chrono::microseconds latencyUs = {};
+
+    // The last update timestamp.
+    std::chrono::time_point<std::chrono::steady_clock> lastUpdate;
+
+    std::string toString() const;
+
+    // For testing.
+    bool operator==(const StatsData& o) const;
+    friend std::ostream& operator<<(std::ostream& os, const StatsData& data) {
+        return os << data.toString();
+    }
+};
+
+// A circular buffer based class used to store the statistics for a server with a protocol.
+class StatsRecords {
+  public:
+    struct Record {
+        int rcode;
+        std::chrono::microseconds latencyUs;
+    };
+
+    StatsRecords(const netdutils::IPSockAddr& ipSockAddr, size_t size);
+
+    void push(const Record& record);
+
+    const StatsData& getStatsData() const { return mStatsData; }
+
+  private:
+    void updateStatsData(const Record& record, const bool add);
+
+    std::deque<Record> mRecords;
+    size_t mCapacity;
+    StatsData mStatsData;
+};
+
+// DnsStats class manages the statistics of DNS servers per netId.
+// The class itself is not thread-safe.
+class DnsStats {
+  public:
+    using ServerStatsMap = std::map<netdutils::IPSockAddr, StatsRecords>;
+
+    // Add |servers| to the map, and remove no-longer-used servers.
+    // Return true if they are successfully added; otherwise, return false.
+    bool setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol);
+
+    // Return true if |record| is successfully added into |server|'s stats; otherwise, return false.
+    bool addStats(const netdutils::IPSockAddr& server, const DnsQueryEvent& record);
+
+    void dump(netdutils::DumpWriter& dw);
+
+    // For testing.
+    std::vector<StatsData> getStats(Protocol protocol) const;
+
+    // TODO: Compatible support for getResolverInfo().
+    // TODO: Support getSortedServers().
+
+    static constexpr size_t kLogSize = 128;
+
+  private:
+    std::map<Protocol, ServerStatsMap> mStats;
+};
+
+}  // namespace android::net
diff --git a/DnsStatsTest.cpp b/DnsStatsTest.cpp
new file mode 100644
index 0000000..419e6db
--- /dev/null
+++ b/DnsStatsTest.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DnsStats.h"
+
+namespace android::net {
+
+using namespace std::chrono_literals;
+using android::netdutils::IPSockAddr;
+using std::chrono::milliseconds;
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAreArray;
+
+namespace {
+
+DnsQueryEvent makeDnsQueryEvent(const Protocol protocol, const NsRcode rcode,
+                                const milliseconds& latency) {
+    DnsQueryEvent event;
+    event.set_protocol(protocol);
+    event.set_rcode(rcode);
+    event.set_latency_micros(latency.count() * 1000);
+    return event;
+}
+
+StatsData makeStatsData(const IPSockAddr& server, const int total, const milliseconds& latencyMs,
+                        const std::map<int, int>& rcodeCounts) {
+    StatsData ret(server);
+    ret.total = total;
+    ret.latencyUs = latencyMs;
+    ret.rcodeCounts = rcodeCounts;
+    return ret;
+}
+
+}  // namespace
+
+class StatsRecordsTest : public ::testing::Test {};
+
+TEST_F(StatsRecordsTest, PushRecord) {
+    const IPSockAddr server = IPSockAddr::toIPSockAddr("127.0.0.2", 53);
+    constexpr size_t size = 3;
+    const StatsRecords::Record recordNoError = {NS_R_NO_ERROR, 10ms};
+    const StatsRecords::Record recordTimeout = {NS_R_TIMEOUT, 250ms};
+
+    StatsRecords sr(server, size);
+    EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 0, 0ms, {}));
+
+    sr.push(recordNoError);
+    EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 1, 10ms, {{NS_R_NO_ERROR, 1}}));
+
+    sr.push(recordNoError);
+    EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 2, 20ms, {{NS_R_NO_ERROR, 2}}));
+
+    sr.push(recordTimeout);
+    EXPECT_EQ(sr.getStatsData(),
+              makeStatsData(server, 3, 270ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 1}}));
+
+    sr.push(recordTimeout);
+    EXPECT_EQ(sr.getStatsData(),
+              makeStatsData(server, 3, 510ms, {{NS_R_NO_ERROR, 1}, {NS_R_TIMEOUT, 2}}));
+
+    sr.push(recordTimeout);
+    EXPECT_EQ(sr.getStatsData(),
+              makeStatsData(server, 3, 750ms, {{NS_R_NO_ERROR, 0}, {NS_R_TIMEOUT, 3}}));
+}
+
+class DnsStatsTest : public ::testing::Test {
+  protected:
+    std::string captureDumpOutput() {
+        netdutils::DumpWriter dw(STDOUT_FILENO);
+        CapturedStdout captured;
+        mDnsStats.dump(dw);
+        return captured.str();
+    }
+
+    // Get the output string from dump() and check the content.
+    void verifyDumpOutput(const std::vector<StatsData>& tcpData,
+                          const std::vector<StatsData>& udpData,
+                          const std::vector<StatsData>& dotData) {
+        // A simple pattern to capture two matches:
+        //     server address (empty allowed) and its statistics.
+        const std::regex pattern(R"(\s{4,}([0-9a-fA-F:\.]*) ([<(].*[>)]))");
+        std::string dumpString = captureDumpOutput();
+
+        const auto check = [&](const std::vector<StatsData>& statsData, const std::string& protocol,
+                               std::string* dumpString) {
+            SCOPED_TRACE(protocol);
+            ASSERT_NE(dumpString->find(protocol), std::string::npos);
+            std::smatch sm;
+
+            // Expect to show something even if none of servers is set.
+            if (statsData.empty()) {
+                ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern));
+                EXPECT_TRUE(sm[1].str().empty());
+                EXPECT_EQ(sm[2], "<no server>");
+                *dumpString = sm.suffix();
+                return;
+            }
+
+            for (const auto& stats : statsData) {
+                ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern));
+                EXPECT_EQ(sm[1], stats.serverSockAddr.ip().toString());
+                EXPECT_FALSE(sm[2].str().empty());
+                *dumpString = sm.suffix();
+            }
+        };
+
+        check(udpData, "UDP", &dumpString);
+        check(dotData, "TLS", &dumpString);
+        check(tcpData, "TCP", &dumpString);
+
+        // Ensure the whole string has been checked.
+        EXPECT_EQ(dumpString, "\n");
+    }
+
+    DnsStats mDnsStats;
+};
+
+TEST_F(DnsStatsTest, SetServers) {
+    // Check before any operation to mDnsStats.
+    verifyDumpOutput({}, {}, {});
+
+    static const struct {
+        std::vector<std::string> servers;
+        std::vector<std::string> expectation;
+        bool isSuccess;
+    } tests[] = {
+            // Normal case.
+            {
+                    {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"},
+                    {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"},
+                    true,
+            },
+            // Duplicate servers.
+            {
+                    {"127.0.0.1", "2001:db8::2", "127.0.0.1", "2001:db8::2"},
+                    {"127.0.0.1", "2001:db8::2"},
+                    true,
+            },
+            // Invalid server addresses. The state remains in previous state.
+            {
+                    {"not_an_ip", "127.0.0.3", "127.a.b.2"},
+                    {"127.0.0.1", "2001:db8::2"},
+                    false,
+            },
+            // Clean up the old servers 127.0.0.1 and 127.0.0.2.
+            {
+                    {"127.0.0.4", "2001:db8::5"},
+                    {"127.0.0.4", "2001:db8::5"},
+                    true,
+            },
+            // Empty list.
+            {{}, {}, true},
+    };
+
+    for (const auto& [servers, expectation, isSuccess] : tests) {
+        std::vector<IPSockAddr> ipSockAddrs;
+        ipSockAddrs.reserve(servers.size());
+        for (const auto& server : servers) {
+            ipSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 53));
+        }
+
+        EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_TCP) == isSuccess);
+        EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_UDP) == isSuccess);
+        EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_DOT) == isSuccess);
+
+        std::vector<StatsData> expectedStats;
+        expectedStats.reserve(expectation.size());
+        for (const auto& exp : expectation) {
+            expectedStats.push_back(makeStatsData(IPSockAddr::toIPSockAddr(exp, 53), 0, 0ms, {}));
+        }
+
+        EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
+        EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+        EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
+    }
+
+    verifyDumpOutput({}, {}, {});
+}
+
+TEST_F(DnsStatsTest, SetServersDifferentPorts) {
+    const std::vector<IPSockAddr> servers = {
+            IPSockAddr::toIPSockAddr("127.0.0.1", 0),   IPSockAddr::toIPSockAddr("fe80::1", 0),
+            IPSockAddr::toIPSockAddr("127.0.0.1", 53),  IPSockAddr::toIPSockAddr("127.0.0.1", 5353),
+            IPSockAddr::toIPSockAddr("127.0.0.1", 853), IPSockAddr::toIPSockAddr("fe80::1", 53),
+            IPSockAddr::toIPSockAddr("fe80::1", 5353),  IPSockAddr::toIPSockAddr("fe80::1", 853),
+    };
+
+    // Servers setup fails due to port unset.
+    EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_TCP));
+    EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_UDP));
+    EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_DOT));
+
+    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
+    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
+    verifyDumpOutput({}, {}, {});
+
+    EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_TCP));
+    EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_UDP));
+    EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_DOT));
+
+    const std::vector<StatsData> expectedStats = {
+            makeStatsData(servers[2], 0, 0ms, {}), makeStatsData(servers[3], 0, 0ms, {}),
+            makeStatsData(servers[4], 0, 0ms, {}), makeStatsData(servers[5], 0, 0ms, {}),
+            makeStatsData(servers[6], 0, 0ms, {}), makeStatsData(servers[7], 0, 0ms, {}),
+    };
+
+    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
+    verifyDumpOutput(expectedStats, expectedStats, expectedStats);
+}
+
+TEST_F(DnsStatsTest, AddStatsAndClear) {
+    const std::vector<IPSockAddr> servers = {
+            IPSockAddr::toIPSockAddr("127.0.0.1", 53),
+            IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+    };
+    const DnsQueryEvent record = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms);
+
+    EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP));
+    EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+
+    // Fail to add stats because of incorrect arguments.
+    EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.0.0.4", 53), record));
+    EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.a.b.4", 53), record));
+
+    EXPECT_TRUE(mDnsStats.addStats(servers[0], record));
+    EXPECT_TRUE(mDnsStats.addStats(servers[0], record));
+    EXPECT_TRUE(mDnsStats.addStats(servers[1], record));
+
+    const std::vector<StatsData> expectedStatsForTcp = {
+            makeStatsData(servers[0], 0, 0ms, {}),
+            makeStatsData(servers[1], 0, 0ms, {}),
+    };
+    const std::vector<StatsData> expectedStatsForUdp = {
+            makeStatsData(servers[0], 2, 20ms, {{NS_R_NO_ERROR, 2}}),
+            makeStatsData(servers[1], 1, 10ms, {{NS_R_NO_ERROR, 1}}),
+    };
+
+    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStatsForTcp));
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStatsForUdp));
+    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
+    verifyDumpOutput(expectedStatsForTcp, expectedStatsForUdp, {});
+
+    // Clear stats.
+    EXPECT_TRUE(mDnsStats.setServers({}, PROTO_TCP));
+    EXPECT_TRUE(mDnsStats.setServers({}, PROTO_UDP));
+    EXPECT_TRUE(mDnsStats.setServers({}, PROTO_DOT));
+    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
+    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
+    verifyDumpOutput({}, {}, {});
+}
+
+TEST_F(DnsStatsTest, StatsRemainsInExistentServer) {
+    std::vector<IPSockAddr> servers = {
+            IPSockAddr::toIPSockAddr("127.0.0.1", 53),
+            IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+    };
+    const DnsQueryEvent recordNoError = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms);
+    const DnsQueryEvent recordTimeout = makeDnsQueryEvent(PROTO_UDP, NS_R_TIMEOUT, 250ms);
+
+    EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+
+    // Add a record to 127.0.0.1.
+    EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError));
+
+    // Add four records to 127.0.0.2.
+    EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError));
+    EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError));
+    EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout));
+    EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout));
+
+    std::vector<StatsData> expectedStats = {
+            makeStatsData(servers[0], 1, 10ms, {{NS_R_NO_ERROR, 1}}),
+            makeStatsData(servers[1], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}),
+    };
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+    verifyDumpOutput({}, expectedStats, {});
+
+    // Update the server list, the stats of 127.0.0.2 will remain.
+    servers = {
+            IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+            IPSockAddr::toIPSockAddr("127.0.0.3", 53),
+            IPSockAddr::toIPSockAddr("127.0.0.4", 53),
+    };
+    EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+    expectedStats = {
+            makeStatsData(servers[0], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}),
+            makeStatsData(servers[1], 0, 0ms, {}),
+            makeStatsData(servers[2], 0, 0ms, {}),
+    };
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+    verifyDumpOutput({}, expectedStats, {});
+
+    // Let's add a record to 127.0.0.2 again.
+    EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError));
+    expectedStats = {
+            makeStatsData(servers[0], 5, 530ms, {{NS_R_NO_ERROR, 3}, {NS_R_TIMEOUT, 2}}),
+            makeStatsData(servers[1], 0, 0ms, {}),
+            makeStatsData(servers[2], 0, 0ms, {}),
+    };
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+    verifyDumpOutput({}, expectedStats, {});
+}
+
+TEST_F(DnsStatsTest, AddStatsRecords_100000) {
+    constexpr size_t operations = 100000;
+    constexpr size_t logSize = DnsStats::kLogSize;
+    constexpr size_t rcodeNum = 4;  // A value by which kLogSize is divisible.
+    ASSERT_EQ(logSize % rcodeNum, 0U);
+
+    const std::vector<IPSockAddr> servers = {
+            IPSockAddr::toIPSockAddr("127.0.0.1", 53),
+            IPSockAddr::toIPSockAddr("127.0.0.2", 53),
+            IPSockAddr::toIPSockAddr("127.0.0.3", 53),
+            IPSockAddr::toIPSockAddr("127.0.0.4", 53),
+    };
+
+    // To test unknown rcode in rcodeToName(), store the elements as type int.
+    const std::array<int, rcodeNum> rcodes = {
+            NS_R_NO_ERROR,        // NOERROR
+            NS_R_NXDOMAIN,        // NXDOMAIN
+            99,                   // UNKNOWN(99)
+            NS_R_INTERNAL_ERROR,  // INTERNAL_ERROR
+    };
+
+    EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP));
+    EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
+    EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_DOT));
+
+    for (size_t i = 0; i < operations; i++) {
+        const NsRcode rcode = static_cast<NsRcode>(rcodes[i % rcodeNum]);
+        const auto eventTcp = makeDnsQueryEvent(PROTO_TCP, rcode, 10ms);
+        const auto eventUdp = makeDnsQueryEvent(PROTO_UDP, rcode, 10ms);
+        const auto eventDot = makeDnsQueryEvent(PROTO_DOT, rcode, 10ms);
+        for (const auto& server : servers) {
+            SCOPED_TRACE(server.toString() + "-" + std::to_string(i));
+            ASSERT_TRUE(mDnsStats.addStats(server, eventTcp));
+            ASSERT_TRUE(mDnsStats.addStats(server, eventUdp));
+            ASSERT_TRUE(mDnsStats.addStats(server, eventDot));
+        }
+    }
+
+    std::map<int, int> expectedRcodeCounts;
+    for (const auto& rcode : rcodes) {
+        expectedRcodeCounts.try_emplace(rcode, 32);
+    }
+    const std::vector<StatsData> expectedStats = {
+            makeStatsData(servers[0], logSize, logSize * 10ms, expectedRcodeCounts),
+            makeStatsData(servers[1], logSize, logSize * 10ms, expectedRcodeCounts),
+            makeStatsData(servers[2], logSize, logSize * 10ms, expectedRcodeCounts),
+            makeStatsData(servers[3], logSize, logSize * 10ms, expectedRcodeCounts),
+    };
+
+    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
+    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
+    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
+    verifyDumpOutput(expectedStats, expectedStats, expectedStats);
+}
+
+}  // namespace android::net
diff --git a/DnsTlsDispatcher.cpp b/DnsTlsDispatcher.cpp
index 58ef2a7..8747e46 100644
--- a/DnsTlsDispatcher.cpp
+++ b/DnsTlsDispatcher.cpp
@@ -17,8 +17,11 @@
 #define LOG_TAG "resolv"
 
 #include "DnsTlsDispatcher.h"
+
 #include <netdutils/Stopwatch.h>
+
 #include "DnsTlsSocketFactory.h"
+#include "resolv_cache.h"
 #include "resolv_private.h"
 #include "stats.pb.h"
 
@@ -27,6 +30,7 @@
 namespace android {
 namespace net {
 
+using android::netdutils::IPSockAddr;
 using android::netdutils::Stopwatch;
 using netdutils::Slice;
 
@@ -97,14 +101,17 @@
     for (const auto& server : orderedServers) {
         DnsQueryEvent* dnsQueryEvent =
                 statp->event->mutable_dns_query_events()->add_dns_query_event();
+
+        bool connectTriggered = false;
         Stopwatch queryStopwatch;
-        code = this->query(server, statp->_mark, query, ans, resplen);
+        code = this->query(server, statp->_mark, query, ans, resplen, &connectTriggered);
 
         dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
         dnsQueryEvent->set_dns_server_index(serverCount++);
         dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(server.ss.ss_family));
         dnsQueryEvent->set_protocol(PROTO_DOT);
         dnsQueryEvent->set_type(getQueryType(query.base(), query.size()));
+        dnsQueryEvent->set_connected(connectTriggered);
 
         switch (code) {
             // These response codes are valid responses and not expected to
@@ -112,19 +119,23 @@
             case DnsTlsTransport::Response::success:
                 dnsQueryEvent->set_rcode(
                         static_cast<NsRcode>(reinterpret_cast<HEADER*>(ans.base())->rcode));
+                resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
                 return code;
             case DnsTlsTransport::Response::limit_error:
                 dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR);
+                resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
                 return code;
             // These response codes might differ when trying other servers, so
             // keep iterating to see if we can get a different (better) result.
             case DnsTlsTransport::Response::network_error:
                 // Sync from res_tls_send in res_send.cpp
                 dnsQueryEvent->set_rcode(NS_R_TIMEOUT);
-                continue;
+                resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
+                break;
             case DnsTlsTransport::Response::internal_error:
                 dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR);
-                continue;
+                resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent);
+                break;
             // No "default" statement.
         }
     }
@@ -133,8 +144,9 @@
 }
 
 DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, unsigned mark,
-                                                  const Slice query,
-                                                  const Slice ans, int *resplen) {
+                                                  const Slice query, const Slice ans, int* resplen,
+                                                  bool* connectTriggered) {
+    int connectCounter;
     const Key key = std::make_pair(mark, server);
     Transport* xport;
     {
@@ -147,6 +159,7 @@
             xport = it->second.get();
         }
         ++xport->useCount;
+        connectCounter = xport->transport.getConnectCounter();
     }
 
     LOG(DEBUG) << "Sending query of length " << query.size();
@@ -170,6 +183,7 @@
     auto now = std::chrono::steady_clock::now();
     {
         std::lock_guard guard(sLock);
+        *connectTriggered = (xport->transport.getConnectCounter() > connectCounter);
         --xport->useCount;
         xport->lastUsed = now;
         cleanup(now);
diff --git a/DnsTlsDispatcher.h b/DnsTlsDispatcher.h
index b8e9968..9eb6dfe 100644
--- a/DnsTlsDispatcher.h
+++ b/DnsTlsDispatcher.h
@@ -54,11 +54,12 @@
                                     const netdutils::Slice ans, int* _Nonnull resplen);
 
     // Given a |query|, sends it to the server on the network indicated by |mark|,
-    // and writes the response into |ans|,  and indicates
-    // the number of bytes written in |resplen|.  Returns a success or error code.
+    // and writes the response into |ans|, and indicates the number of bytes written in |resplen|.
+    // If the whole procedure above triggers (or experiences) any new connection, |connectTriggered|
+    // is set. Returns a success or error code.
     DnsTlsTransport::Response query(const DnsTlsServer& server, unsigned mark,
                                     const netdutils::Slice query, const netdutils::Slice ans,
-                                    int* _Nonnull resplen);
+                                    int* _Nonnull resplen, bool* _Nonnull connectTriggered);
 
   private:
     // This lock is static so that it can be used to annotate the Transport struct.
diff --git a/DnsTlsQueryMap.h b/DnsTlsQueryMap.h
index c5ab023..a6227f4 100644
--- a/DnsTlsQueryMap.h
+++ b/DnsTlsQueryMap.h
@@ -74,6 +74,9 @@
     // Returns true if there are no pending queries.
     bool empty();
 
+    // The maximum number of times we will send a query before abandoning it.
+    static constexpr int kMaxTries = 3;
+
   private:
     std::mutex mLock;
 
@@ -87,9 +90,6 @@
         std::promise<Result> result;
     };
 
-    // The maximum number of times we will send a query before abandoning it.
-    static constexpr int kMaxTries = 3;
-
     // Outstanding queries by newId.
     std::map<uint16_t, QueryPromise> mQueries GUARDED_BY(mLock);
 
diff --git a/DnsTlsServer.h b/DnsTlsServer.h
index f036b68..82d1a45 100644
--- a/DnsTlsServer.h
+++ b/DnsTlsServer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _DNS_DNSTLSSERVER_H
-#define _DNS_DNSTLSSERVER_H
+#pragma once
 
 #include <chrono>
 #include <set>
@@ -24,7 +23,7 @@
 
 #include <netinet/in.h>
 
-#include <netd_resolv/params.h>
+#include <params.h>
 
 namespace android {
 namespace net {
@@ -62,7 +61,9 @@
     // The time to wait for the attempt on connecting to the server.
     // Set the default value 127 seconds to be consistent with TCP connect timeout.
     // (presume net.ipv4.tcp_syn_retries = 6)
-    std::chrono::milliseconds connectTimeout = std::chrono::milliseconds(127 * 1000);
+    static constexpr std::chrono::milliseconds kDotConnectTimeoutMs =
+            std::chrono::milliseconds(127 * 1000);
+    std::chrono::milliseconds connectTimeout = kDotConnectTimeoutMs;
 
     // Exact comparison of DnsTlsServer objects
     bool operator<(const DnsTlsServer& other) const;
@@ -78,5 +79,3 @@
 
 }  // namespace net
 }  // namespace android
-
-#endif  // _DNS_DNSTLSSERVER_H
diff --git a/DnsTlsSocket.cpp b/DnsTlsSocket.cpp
index bb43ed9..e133ccc 100644
--- a/DnsTlsSocket.cpp
+++ b/DnsTlsSocket.cpp
@@ -38,6 +38,7 @@
 #include <netdutils/SocketOption.h>
 #include <netdutils/ThreadUtil.h>
 
+#include "netd_resolv/resolv.h"
 #include "private/android_filesystem_config.h"  // AID_DNS
 #include "resolv_private.h"
 
@@ -95,7 +96,7 @@
         return Status(errno);
     }
 
-    resolv_tag_socket(mSslFd.get(), AID_DNS);
+    resolv_tag_socket(mSslFd.get(), AID_DNS, NET_CONTEXT_INVALID_PID);
 
     const socklen_t len = sizeof(mMark);
     if (setsockopt(mSslFd.get(), SOL_SOCKET, SO_MARK, &mMark, len) == -1) {
@@ -159,7 +160,9 @@
     // For discussion of alternative, sustainable approaches see b/71909242.
     if (RESOLV_INJECT_CA_CERTIFICATE && !mServer.certificate.empty()) {
         // Inject test CA certs from ResolverParamsParcel.caCertificate for internal testing.
-        LOG(WARNING) << "test CA certificate is valid";
+        // This is only allowed by DnsResolverService if the caller is not AID_SYSTEM, and on
+        // debug builds.
+        LOG(WARNING) << "Setting test CA certificate. This should never happen in production code.";
         if (!setTestCaCertificate()) {
             LOG(ERROR) << "Failed to set test CA certificate";
             return false;
diff --git a/DnsTlsTransport.cpp b/DnsTlsTransport.cpp
index 3453765..9766b65 100644
--- a/DnsTlsTransport.cpp
+++ b/DnsTlsTransport.cpp
@@ -51,6 +51,11 @@
     return std::move(record->result);
 }
 
+int DnsTlsTransport::getConnectCounter() const {
+    std::lock_guard guard(mLock);
+    return mConnectCounter;
+}
+
 bool DnsTlsTransport::sendQuery(const DnsTlsQueryMap::Query q) {
     // Strip off the ID number and send the new ID instead.
     bool sent = mSocket->query(q.newId, netdutils::drop(q.query, 2));
@@ -63,6 +68,7 @@
 void DnsTlsTransport::doConnect() {
     LOG(DEBUG) << "Constructing new socket";
     mSocket = mFactory->createDnsTlsSocket(mServer, mMark, this, &mCache);
+    mConnectCounter++;
 
     if (mSocket) {
         auto queries = mQueries.getAll();
diff --git a/DnsTlsTransport.h b/DnsTlsTransport.h
index 3e43c7e..5f959da 100644
--- a/DnsTlsTransport.h
+++ b/DnsTlsTransport.h
@@ -57,12 +57,14 @@
     // on networks where it doesn't actually work.
     static bool validate(const DnsTlsServer& server, unsigned netid, uint32_t mark);
 
+    int getConnectCounter() const EXCLUDES(mLock);
+
     // Implement IDnsTlsSocketObserver
     void onResponse(std::vector<uint8_t> response) override;
     void onClosed() override EXCLUDES(mLock);
 
   private:
-    std::mutex mLock;
+    mutable std::mutex mLock;
 
     DnsTlsSessionCache mCache;
     DnsTlsQueryMap mQueries;
@@ -85,6 +87,9 @@
 
     // Send a query to the socket.
     bool sendQuery(const DnsTlsQueryMap::Query q) REQUIRES(mLock);
+
+    // The number of times an attempt to connect the nameserver.
+    int mConnectCounter GUARDED_BY(mLock) = 0;
 };
 
 }  // end of namespace net
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c8dbf77..27eac94 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
 [Builtin Hooks]
 clang_format = true
+commit_msg_test_field = false
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp
index 9242222..bfac68a 100644
--- a/PrivateDnsConfiguration.cpp
+++ b/PrivateDnsConfiguration.cpp
@@ -28,12 +28,23 @@
 #include "ResolverEventReporter.h"
 #include "netd_resolv/resolv.h"
 #include "netdutils/BackoffSequence.h"
+#include "resolv_cache.h"
+#include "util.h"
 
 using std::chrono::milliseconds;
 
 namespace android {
 namespace net {
 
+namespace {
+
+milliseconds getExperimentTimeout(const std::string& flagName, const milliseconds defaultValue) {
+    int val = getExperimentFlagInt(flagName, defaultValue.count());
+    return milliseconds((val < 1000) ? 1000 : val);
+}
+
+}  // namespace
+
 std::string addrToString(const sockaddr_storage* addr) {
     char out[INET6_ADDRSTRLEN] = {0};
     getnameinfo((const sockaddr*) addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
@@ -61,30 +72,24 @@
 
 int PrivateDnsConfiguration::set(int32_t netId, uint32_t mark,
                                  const std::vector<std::string>& servers, const std::string& name,
-                                 const std::string& caCert, int32_t connectTimeoutMs) {
+                                 const std::string& caCert) {
     LOG(DEBUG) << "PrivateDnsConfiguration::set(" << netId << ", 0x" << std::hex << mark << std::dec
-               << ", " << servers.size() << ", " << name << ", " << connectTimeoutMs << "ms)";
+               << ", " << servers.size() << ", " << name << ")";
 
     // Parse the list of servers that has been passed in
     std::set<DnsTlsServer> tlsServers;
-    for (size_t i = 0; i < servers.size(); ++i) {
+    for (const auto& s : servers) {
         sockaddr_storage parsed;
-        if (!parseServer(servers[i].c_str(), &parsed)) {
+        if (!parseServer(s.c_str(), &parsed)) {
             return -EINVAL;
         }
         DnsTlsServer server(parsed);
         server.name = name;
         server.certificate = caCert;
-
-        // connectTimeoutMs = 0: use the default timeout value.
-        // connectTimeoutMs < 0: invalid timeout value.
-        if (connectTimeoutMs > 0) {
-            // Set a specific timeout value but limit it to be at least 1 second.
-            server.connectTimeout =
-                    (connectTimeoutMs < 1000) ? milliseconds(1000) : milliseconds(connectTimeoutMs);
-        }
-
+        server.connectTimeout =
+                getExperimentTimeout("dot_connect_timeout_ms", DnsTlsServer::kDotConnectTimeoutMs);
         tlsServers.insert(server);
+        LOG(DEBUG) << "Set DoT connect timeout " << server.connectTimeout.count() << "ms for " << s;
     }
 
     std::lock_guard guard(mPrivateDnsLock);
@@ -95,6 +100,8 @@
     } else {
         mPrivateDnsModes[netId] = PrivateDnsMode::OFF;
         mPrivateDnsTransports.erase(netId);
+        resolv_stats_set_servers_for_dot(netId, {});
+        mPrivateDnsValidateThreads.erase(netId);
         return 0;
     }
 
@@ -126,7 +133,8 @@
             validatePrivateDnsProvider(server, tracker, netId, mark);
         }
     }
-    return 0;
+
+    return resolv_stats_set_servers_for_dot(netId, servers);
 }
 
 PrivateDnsStatus PrivateDnsConfiguration::getStatus(unsigned netId) {
@@ -152,6 +160,7 @@
     std::lock_guard guard(mPrivateDnsLock);
     mPrivateDnsModes.erase(netId);
     mPrivateDnsTransports.erase(netId);
+    mPrivateDnsValidateThreads.erase(netId);
 }
 
 void PrivateDnsConfiguration::validatePrivateDnsProvider(const DnsTlsServer& server,
@@ -160,6 +169,10 @@
     tracker[server] = Validation::in_process;
     LOG(DEBUG) << "Server " << addrToString(&server.ss) << " marked as in_process on netId "
                << netId << ". Tracker now has size " << tracker.size();
+    // This judge must be after "tracker[server] = Validation::in_process;"
+    if (!needValidateThread(server, netId)) {
+        return;
+    }
 
     // Note that capturing |server| and |netId| in this lambda create copies.
     std::thread validate_thread([this, server, netId, mark] {
@@ -202,6 +215,7 @@
                 break;
             }
         }
+        this->cleanValidateThreadTracker(server, netId);
     });
     validate_thread.detach();
 }
@@ -276,6 +290,50 @@
     return reevaluationStatus;
 }
 
+bool PrivateDnsConfiguration::needValidateThread(const DnsTlsServer& server, unsigned netId)
+        REQUIRES(mPrivateDnsLock) {
+    // Create the thread tracker if it was not present
+    auto threadPair = mPrivateDnsValidateThreads.find(netId);
+    if (threadPair == mPrivateDnsValidateThreads.end()) {
+        // No thread tracker yet for this netId.
+        bool added;
+        std::tie(threadPair, added) = mPrivateDnsValidateThreads.emplace(netId, ThreadTracker());
+        if (!added) {
+            LOG(ERROR) << "Memory error while needValidateThread for netId " << netId;
+            return true;
+        }
+    }
+    auto& threadTracker = threadPair->second;
+    if (threadTracker.count(server)) {
+        LOG(DEBUG) << "Server " << addrToString(&(server.ss))
+                   << " validate thread is already running. Thread tracker now has size "
+                   << threadTracker.size();
+        return false;
+    } else {
+        threadTracker.insert(server);
+        LOG(DEBUG) << "Server " << addrToString(&(server.ss))
+                   << " validate thread is not running. Thread tracker now has size "
+                   << threadTracker.size();
+        return true;
+    }
+}
+
+void PrivateDnsConfiguration::cleanValidateThreadTracker(const DnsTlsServer& server,
+                                                         unsigned netId) {
+    std::lock_guard<std::mutex> guard(mPrivateDnsLock);
+    LOG(DEBUG) << "cleanValidateThreadTracker Server " << addrToString(&(server.ss))
+               << " validate thread is stopped.";
+
+    auto threadPair = mPrivateDnsValidateThreads.find(netId);
+    if (threadPair != mPrivateDnsValidateThreads.end()) {
+        auto& threadTracker = threadPair->second;
+        threadTracker.erase(server);
+        LOG(DEBUG) << "Server " << addrToString(&(server.ss))
+                   << " validate thread is stopped. Thread tracker now has size "
+                   << threadTracker.size();
+    }
+}
+
 // Start validation for newly added servers as well as any servers that have
 // landed in Validation::fail state. Note that servers that have failed
 // multiple validation attempts but for which there is still a validating
diff --git a/PrivateDnsConfiguration.h b/PrivateDnsConfiguration.h
index 6c50604..b66859c 100644
--- a/PrivateDnsConfiguration.h
+++ b/PrivateDnsConfiguration.h
@@ -53,8 +53,7 @@
 class PrivateDnsConfiguration {
   public:
     int set(int32_t netId, uint32_t mark, const std::vector<std::string>& servers,
-            const std::string& name, const std::string& caCert, int32_t connectTimeoutMs)
-            EXCLUDES(mPrivateDnsLock);
+            const std::string& name, const std::string& caCert) EXCLUDES(mPrivateDnsLock);
 
     PrivateDnsStatus getStatus(unsigned netId) EXCLUDES(mPrivateDnsLock);
 
@@ -62,12 +61,16 @@
 
   private:
     typedef std::map<DnsTlsServer, Validation, AddressComparator> PrivateDnsTracker;
+    typedef std::set<DnsTlsServer, AddressComparator> ThreadTracker;
 
     void validatePrivateDnsProvider(const DnsTlsServer& server, PrivateDnsTracker& tracker,
                                     unsigned netId, uint32_t mark) REQUIRES(mPrivateDnsLock);
 
     bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success);
 
+    bool needValidateThread(const DnsTlsServer& server, unsigned netId) REQUIRES(mPrivateDnsLock);
+    void cleanValidateThreadTracker(const DnsTlsServer& server, unsigned netId);
+
     // Start validation for newly added servers as well as any servers that have
     // landed in Validation::fail state. Note that servers that have failed
     // multiple validation attempts but for which there is still a validating
@@ -79,6 +82,7 @@
     // Structure for tracking the validation status of servers on a specific netId.
     // Using the AddressComparator ensures at most one entry per IP address.
     std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock);
+    std::map<unsigned, ThreadTracker> mPrivateDnsValidateThreads GUARDED_BY(mPrivateDnsLock);
 };
 
 extern PrivateDnsConfiguration gPrivateDnsConfiguration;
diff --git a/ResolverController.cpp b/ResolverController.cpp
index 6927e5f..c59676a 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -34,8 +34,8 @@
 #include "PrivateDnsConfiguration.h"
 #include "ResolverEventReporter.h"
 #include "ResolverStats.h"
-#include "netd_resolv/stats.h"
 #include "resolv_cache.h"
+#include "stats.h"
 
 using aidl::android::net::ResolverParamsParcel;
 
@@ -194,6 +194,11 @@
     return resolv_create_cache_for_net(netId);
 }
 
+int ResolverController::flushNetworkCache(unsigned netId) {
+    LOG(VERBOSE) << __func__ << ": netId = " << netId;
+    return resolv_flush_cache_for_net(netId);
+}
+
 int ResolverController::setResolverConfiguration(const ResolverParamsParcel& resolverParams) {
     using aidl::android::net::IDnsResolver;
 
@@ -210,9 +215,9 @@
     if (tlsServers.size() > MAXNS) {
         tlsServers.resize(MAXNS);
     }
-    const int err = gPrivateDnsConfiguration.set(
-            resolverParams.netId, fwmark.intValue, tlsServers, resolverParams.tlsName,
-            resolverParams.caCertificate, resolverParams.tlsConnectTimeoutMs);
+    const int err =
+            gPrivateDnsConfiguration.set(resolverParams.netId, fwmark.intValue, tlsServers,
+                                         resolverParams.tlsName, resolverParams.caCertificate);
 
     if (err != 0) {
         return err;
@@ -227,7 +232,8 @@
     res_params.retry_count = resolverParams.retryCount;
 
     return resolv_set_nameservers(resolverParams.netId, resolverParams.servers,
-                                  resolverParams.domains, res_params);
+                                  resolverParams.domains, res_params,
+                                  resolverParams.experimentalOptions);
 }
 
 int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* servers,
@@ -356,6 +362,8 @@
             dw.decIndent();
         }
         dw.println("Concurrent DNS query timeout: %d", wait_for_pending_req_timeout_count[0]);
+        resolv_stats_dump(dw, netId);
+        resolv_oem_options_dump(dw, netId);
     }
     dw.decIndent();
 }
diff --git a/ResolverController.h b/ResolverController.h
index 4ff395b..c1c6f0f 100644
--- a/ResolverController.h
+++ b/ResolverController.h
@@ -38,6 +38,7 @@
 
     void destroyNetworkCache(unsigned netid);
     int createNetworkCache(unsigned netid);
+    int flushNetworkCache(unsigned netid);
 
     int getPrefix64(unsigned netId, netdutils::IPPrefix* prefix);
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 382bfc3..10533ed 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,7 +1,9 @@
 {
     "presubmit": [
         { "name": "resolv_integration_test" },
+        { "name": "resolv_gold_test" },
         { "name": "resolv_unit_test" },
-        { "name": "resolv_stress_test" }
+        { "name": "resolv_stress_test" },
+        { "name": "resolv_stats_test_utils_test" }
     ]
 }
diff --git a/aidl/dnsresolver/1/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/1/android/net/IDnsResolver.aidl
similarity index 100%
rename from aidl/dnsresolver/1/android/net/IDnsResolver.aidl
rename to aidl_api/dnsresolver_aidl_interface/1/android/net/IDnsResolver.aidl
diff --git a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/1/android/net/ResolverParamsParcel.aidl
similarity index 87%
rename from aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
rename to aidl_api/dnsresolver_aidl_interface/1/android/net/ResolverParamsParcel.aidl
index b808bae..5333f31 100644
--- a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/1/android/net/ResolverParamsParcel.aidl
@@ -11,5 +11,5 @@
   @utf8InCpp String[] domains;
   @utf8InCpp String tlsName;
   @utf8InCpp String[] tlsServers;
-  @utf8InCpp String[] tlsFingerprints;
+  @utf8InCpp String[] tlsFingerprints = {};
 }
diff --git a/aidl/dnsresolver/2/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/2/android/net/IDnsResolver.aidl
similarity index 100%
rename from aidl/dnsresolver/2/android/net/IDnsResolver.aidl
rename to aidl_api/dnsresolver_aidl_interface/2/android/net/IDnsResolver.aidl
diff --git a/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/2/android/net/ResolverParamsParcel.aidl
similarity index 96%
rename from aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl
rename to aidl_api/dnsresolver_aidl_interface/2/android/net/ResolverParamsParcel.aidl
index a37f938..5c81616 100644
--- a/aidl/dnsresolver/2/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/2/android/net/ResolverParamsParcel.aidl
@@ -28,5 +28,5 @@
   @utf8InCpp String[] domains;
   @utf8InCpp String tlsName;
   @utf8InCpp String[] tlsServers;
-  @utf8InCpp String[] tlsFingerprints;
+  @utf8InCpp String[] tlsFingerprints = {};
 }
diff --git a/aidl_api/dnsresolver_aidl_interface/3/.hash b/aidl_api/dnsresolver_aidl_interface/3/.hash
new file mode 100644
index 0000000..c6a5015
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/3/.hash
@@ -0,0 +1 @@
+e3cbaaa75f8ec9ff0ee35c2fa9b928121aeb9956  -
diff --git a/aidl_api/dnsresolver_aidl_interface/3/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/3/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..7ab7003
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/3/android/net/IDnsResolver.aidl
@@ -0,0 +1,33 @@
+package android.net;
+interface IDnsResolver {
+  boolean isAlive();
+  void registerEventListener(android.net.metrics.INetdEventListener listener);
+  void setResolverConfiguration(in android.net.ResolverParamsParcel resolverParams);
+  void getResolverInfo(int netId, out @utf8InCpp String[] servers, out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params, out int[] stats, out int[] wait_for_pending_req_timeout_count);
+  void startPrefix64Discovery(int netId);
+  void stopPrefix64Discovery(int netId);
+  @utf8InCpp String getPrefix64(int netId);
+  void createNetworkCache(int netId);
+  void destroyNetworkCache(int netId);
+  void setLogSeverity(int logSeverity);
+  const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
+  const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
+  const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
+  const int RESOLVER_PARAMS_MAX_SAMPLES = 3;
+  const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4;
+  const int RESOLVER_PARAMS_RETRY_COUNT = 5;
+  const int RESOLVER_PARAMS_COUNT = 6;
+  const int RESOLVER_STATS_SUCCESSES = 0;
+  const int RESOLVER_STATS_ERRORS = 1;
+  const int RESOLVER_STATS_TIMEOUTS = 2;
+  const int RESOLVER_STATS_INTERNAL_ERRORS = 3;
+  const int RESOLVER_STATS_RTT_AVG = 4;
+  const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5;
+  const int RESOLVER_STATS_USABLE = 6;
+  const int RESOLVER_STATS_COUNT = 7;
+  const int DNS_RESOLVER_LOG_VERBOSE = 0;
+  const int DNS_RESOLVER_LOG_DEBUG = 1;
+  const int DNS_RESOLVER_LOG_INFO = 2;
+  const int DNS_RESOLVER_LOG_WARNING = 3;
+  const int DNS_RESOLVER_LOG_ERROR = 4;
+}
diff --git a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/3/android/net/ResolverParamsParcel.aidl
similarity index 73%
copy from aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
copy to aidl_api/dnsresolver_aidl_interface/3/android/net/ResolverParamsParcel.aidl
index b808bae..ce31f1a 100644
--- a/aidl/dnsresolver/1/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/3/android/net/ResolverParamsParcel.aidl
@@ -11,5 +11,7 @@
   @utf8InCpp String[] domains;
   @utf8InCpp String tlsName;
   @utf8InCpp String[] tlsServers;
-  @utf8InCpp String[] tlsFingerprints;
+  @utf8InCpp String[] tlsFingerprints = {};
+  @utf8InCpp String caCertificate = "";
+  int tlsConnectTimeoutMs = 0;
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index c1d33e1..3191b0a 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -26,6 +26,8 @@
 
     // Use a custom AndroidManifest.xml used for API targeting.
     androidManifest: "AndroidManifest.xml",
+
+    legacy_android10_support: true,
 }
 
 apex_key {
diff --git a/apex/manifest.json b/apex/manifest.json
index 6739a4d..f3f5b02 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.resolv",
-  "version": 290000000
+  "version": 299900000
 }
diff --git a/binder/android/net/IDnsResolver.aidl b/binder/android/net/IDnsResolver.aidl
index f6f4092..c85e5cf 100644
--- a/binder/android/net/IDnsResolver.aidl
+++ b/binder/android/net/IDnsResolver.aidl
@@ -161,4 +161,25 @@
      *         POSIX errno.
      */
     void setLogSeverity(int logSeverity);
+
+    /**
+     * Flush cache for the given network.
+     *
+     * @param netId the network ID of the network to flush.
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         POSIX errno.
+     */
+    void flushNetworkCache(int netId);
+
+    /**
+     * Values for {@code tcMode} of {@code ResolverExperimentalOptionsParcel}. It controls the way DNS
+     * resolver handles truncated DNS response from UDP connection.
+     *
+     * TC_MODE_DEFAULT: resolver retries on TCP-only on each name server.
+     * TC_MODE_UDP_TCP: resolver retries on TCP on the same server, falls back to UDP from next.
+     * TC_MODE_MAX: any value smaller than TC_MODE_DEFAULT or greater than TC_MODE_MAX is invalid.
+     */
+    const int TC_MODE_DEFAULT = 0;
+    const int TC_MODE_UDP_TCP = 1;
+    const int TC_MODE_MAX = 2;
 }
diff --git a/binder/android/net/ResolverExperimentalOptionsParcel.aidl b/binder/android/net/ResolverExperimentalOptionsParcel.aidl
new file mode 100644
index 0000000..f8fd589
--- /dev/null
+++ b/binder/android/net/ResolverExperimentalOptionsParcel.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.ResolverHostsParcel;
+
+/**
+ * Knobs for OEM to control alternative behavior.
+ *
+ * {@hide}
+ */
+parcelable ResolverExperimentalOptionsParcel {
+    /**
+     * An IP/hostname mapping table for DNS local lookup customization.
+     * WARNING: this is intended for local testing and other special situations.
+     * Future versions of the DnsResolver module may break your assumptions.
+     * Injecting static mappings for public hostnames is generally A VERY BAD IDEA,
+     * since it makes it impossible for the domain owners to migrate the domain.
+     * It is also not an effective domain blocking mechanism, because apps can
+     * easily hardcode IPs or bypass the system DNS resolver.
+     */
+    ResolverHostsParcel[] hosts = {};
+
+    /**
+     * Truncated UDP DNS response handling mode. Handling of UDP responses with the TC (truncated)
+     * bit set. The values are defined in {@code IDnsResolver.aidl}
+     * 0: TC_MODE_DEFAULT
+     * 1: TC_MODE_UDP_TCP
+     * Other values are invalid.
+     */
+    int tcMode = 0;
+}
diff --git a/binder/android/net/ResolverHostsParcel.aidl b/binder/android/net/ResolverHostsParcel.aidl
new file mode 100644
index 0000000..6b372b1
--- /dev/null
+++ b/binder/android/net/ResolverHostsParcel.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/**
+ * A mapping list which translates hostnames or domain names to IP addresses
+ * locally. Mapping multiple addresses to one hostname is supported.
+ * It's similar to /etc/hosts file.
+ *
+ * {@hide}
+ */
+parcelable ResolverHostsParcel {
+    /**
+     * The IPv4 or IPv6 address corresponding to |hostName| field.
+     */
+    @utf8InCpp String ipAddr;
+
+    /**
+     * The hostname is a FQDN.
+     */
+    @utf8InCpp String hostName = "";
+}
diff --git a/binder/android/net/ResolverParamsParcel.aidl b/binder/android/net/ResolverParamsParcel.aidl
index 94576b2..c42367f 100644
--- a/binder/android/net/ResolverParamsParcel.aidl
+++ b/binder/android/net/ResolverParamsParcel.aidl
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.net.ResolverExperimentalOptionsParcel;
+
 /**
  * Configuration for a resolver parameters.
  *
@@ -81,18 +83,25 @@
      * An array containing TLS public key fingerprints (pins) of which each server must match
      * at least one, or empty if there are no pinned keys.
      */
-    // DEPRECATED: no longer to use it
-    @utf8InCpp String[] tlsFingerprints;
+    // DEPRECATED:Superseded by caCertificate below.
+    @utf8InCpp String[] tlsFingerprints = {};
 
     /**
      * Certificate authority that signed the certificate; only used by DNS-over-TLS tests.
      *
      */
-    @utf8InCpp String caCertificate;
+    @utf8InCpp String caCertificate = "";
 
     /**
      * The timeout for the connection attempt to a Private DNS server.
      * It's initialized to 0 to use the predefined default value.
+     * Setting a non-zero value to it takes no effect anymore. The timeout is configurable only
+     * by the experiement flag.
      */
     int tlsConnectTimeoutMs = 0;
+
+    /**
+    * Knobs for OEM to control alternative behavior.
+    */
+    ResolverExperimentalOptionsParcel experimentalOptions;
 }
diff --git a/getaddrinfo.cpp b/getaddrinfo.cpp
index 4207251..3683920 100644
--- a/getaddrinfo.cpp
+++ b/getaddrinfo.cpp
@@ -56,6 +56,9 @@
 #include <android-base/logging.h>
 
 #include "netd_resolv/resolv.h"
+#include "res_comp.h"
+#include "res_debug.h"
+#include "res_init.h"
 #include "resolv_cache.h"
 #include "resolv_private.h"
 
@@ -107,20 +110,13 @@
 };
 
 #define PTON_MAX 16
-#define MAXPACKET (8 * 1024)
-
-typedef union {
-    HEADER hdr;
-    uint8_t buf[MAXPACKET];
-} querybuf;
 
 struct res_target {
     struct res_target* next;
-    const char* name;  /* domain name */
-    int qclass, qtype; /* class and type of query */
-    uint8_t* answer;   /* buffer to put answer */
-    int anslen;        /* size of answer buffer */
-    int n;             /* result length */
+    const char* name;                                                  // domain name
+    int qclass, qtype;                                                 // class and type of query
+    std::vector<uint8_t> answer = std::vector<uint8_t>(MAXPACKET, 0);  // buffer to put answer
+    int n = 0;                                                         // result length
 };
 
 static int str2number(const char*);
@@ -138,15 +134,17 @@
 static const struct afd* find_afd(int);
 static int ip6_str2scopeid(const char*, struct sockaddr_in6*, uint32_t*);
 
-static struct addrinfo* getanswer(const querybuf*, int, const char*, int, const struct addrinfo*,
-                                  int* herrno);
+static struct addrinfo* getanswer(const std::vector<uint8_t>&, int, const char*, int,
+                                  const struct addrinfo*, int* herrno);
 static int dns_getaddrinfo(const char* name, const addrinfo* pai,
                            const android_net_context* netcontext, addrinfo** rv,
                            NetworkDnsEventReported* event);
 static void _sethtent(FILE**);
 static void _endhtent(FILE**);
 static struct addrinfo* _gethtent(FILE**, const char*, const struct addrinfo*);
-static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res);
+static struct addrinfo* getCustomHosts(const size_t netid, const char*, const struct addrinfo*);
+static bool files_getaddrinfo(const size_t netid, const char* name, const addrinfo* pai,
+                              addrinfo** res);
 static int _find_src_addr(const struct sockaddr*, struct sockaddr*, unsigned, uid_t);
 
 static int res_queryN(const char* name, res_target* target, res_state res, int* herrno);
@@ -265,6 +263,7 @@
             .dns_netid = NETID_UNSET,
             .dns_mark = MARK_UNSET,
             .uid = NET_CONTEXT_INVALID_UID,
+            .pid = NET_CONTEXT_INVALID_PID,
     };
     NetworkDnsEventReported event;
     return android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result,
@@ -469,7 +468,7 @@
     // If the servname does not match socktype/protocol, return error code.
     if ((error = get_portmatch(pai, servname))) return error;
 
-    if (!files_getaddrinfo(hostname, pai, &result)) {
+    if (!files_getaddrinfo(netcontext->dns_netid, hostname, pai, &result)) {
         error = dns_getaddrinfo(hostname, pai, netcontext, &result, event);
     }
     if (error) {
@@ -836,8 +835,8 @@
         }                            \
     } while (0)
 
-static struct addrinfo* getanswer(const querybuf* answer, int anslen, const char* qname, int qtype,
-                                  const struct addrinfo* pai, int* herrno) {
+static struct addrinfo* getanswer(const std::vector<uint8_t>& answer, int anslen, const char* qname,
+                                  int qtype, const struct addrinfo* pai, int* herrno) {
     struct addrinfo sentinel = {};
     struct addrinfo *cur;
     struct addrinfo ai;
@@ -851,17 +850,17 @@
     int type, ancount, qdcount;
     int haveanswer, had_error;
     char tbuf[MAXDNAME];
-    int (*name_ok)(const char*);
     char hostbuf[8 * 1024];
 
-    assert(answer != NULL);
     assert(qname != NULL);
     assert(pai != NULL);
 
     cur = &sentinel;
 
     canonname = NULL;
-    eom = answer->buf + anslen;
+    eom = answer.data() + anslen;
+
+    bool (*name_ok)(const char* dn);
     switch (qtype) {
         case T_A:
         case T_AAAA:
@@ -874,18 +873,18 @@
     /*
      * find first satisfactory answer
      */
-    hp = &answer->hdr;
+    hp = reinterpret_cast<const HEADER*>(answer.data());
     ancount = ntohs(hp->ancount);
     qdcount = ntohs(hp->qdcount);
     bp = hostbuf;
     ep = hostbuf + sizeof hostbuf;
-    cp = answer->buf;
+    cp = answer.data();
     BOUNDED_INCR(HFIXEDSZ);
     if (qdcount != 1) {
         *herrno = NO_RECOVERY;
         return (NULL);
     }
-    n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
+    n = dn_expand(answer.data(), eom, cp, bp, ep - bp);
     if ((n < 0) || !(*name_ok)(bp)) {
         *herrno = NO_RECOVERY;
         return (NULL);
@@ -909,7 +908,7 @@
     haveanswer = 0;
     had_error = 0;
     while (ancount-- > 0 && cp < eom && !had_error) {
-        n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
+        n = dn_expand(answer.data(), eom, cp, bp, ep - bp);
         if ((n < 0) || !(*name_ok)(bp)) {
             had_error++;
             continue;
@@ -929,7 +928,7 @@
             continue; /* XXX - had_error++ ? */
         }
         if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && type == T_CNAME) {
-            n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
+            n = dn_expand(answer.data(), eom, cp, tbuf, sizeof tbuf);
             if ((n < 0) || !(*name_ok)(tbuf)) {
                 had_error++;
                 continue;
@@ -1395,16 +1394,11 @@
     res_target q = {};
     res_target q2 = {};
 
-    auto buf = std::make_unique<querybuf>();
-    auto buf2 = std::make_unique<querybuf>();
-
     switch (pai->ai_family) {
         case AF_UNSPEC: {
             /* prefer IPv6 */
             q.name = name;
             q.qclass = C_IN;
-            q.answer = buf->buf;
-            q.anslen = sizeof(buf->buf);
             int query_ipv6 = 1, query_ipv4 = 1;
             if (pai->ai_flags & AI_ADDRCONFIG) {
                 query_ipv6 = have_ipv6(netcontext->app_mark, netcontext->uid);
@@ -1417,8 +1411,6 @@
                     q2.name = name;
                     q2.qclass = C_IN;
                     q2.qtype = T_A;
-                    q2.answer = buf2->buf;
-                    q2.anslen = sizeof(buf2->buf);
                 }
             } else if (query_ipv4) {
                 q.qtype = T_A;
@@ -1431,32 +1423,21 @@
             q.name = name;
             q.qclass = C_IN;
             q.qtype = T_A;
-            q.answer = buf->buf;
-            q.anslen = sizeof(buf->buf);
             break;
         case AF_INET6:
             q.name = name;
             q.qclass = C_IN;
             q.qtype = T_AAAA;
-            q.answer = buf->buf;
-            q.anslen = sizeof(buf->buf);
             break;
         default:
             return EAI_FAMILY;
     }
 
-    res_state res = res_get_state();
-    if (!res) return EAI_MEMORY;
-
-    /* this just sets our netid val in the thread private data so we don't have to
-     * modify the api's all the way down to res_send.c's res_nsend.  We could
-     * fully populate the thread private data here, but if we get down there
-     * and have a cache hit that would be wasted, so we do the rest there on miss
-     */
-    res_setnetcontext(res, netcontext, event);
+    ResState res;
+    res_init(&res, netcontext, event);
 
     int he;
-    if (res_searchN(name, &q, res, &he) < 0) {
+    if (res_searchN(name, &q, &res, &he) < 0) {
         // Return h_errno (he) to catch more detailed errors rather than EAI_NODATA.
         // Note that res_searchN() doesn't set the pair NETDB_INTERNAL and errno.
         // See also herrnoToAiErrno().
@@ -1465,13 +1446,13 @@
 
     addrinfo sentinel = {};
     addrinfo* cur = &sentinel;
-    addrinfo* ai = getanswer(buf.get(), q.n, q.name, q.qtype, pai, &he);
+    addrinfo* ai = getanswer(q.answer, q.n, q.name, q.qtype, pai, &he);
     if (ai) {
         cur->ai_next = ai;
         while (cur && cur->ai_next) cur = cur->ai_next;
     }
     if (q.next) {
-        ai = getanswer(buf2.get(), q2.n, q2.name, q2.qtype, pai, &he);
+        ai = getanswer(q2.answer, q2.n, q2.name, q2.qtype, pai, &he);
         if (ai) cur->ai_next = ai;
     }
     if (sentinel.ai_next == NULL) {
@@ -1551,22 +1532,43 @@
     return res0;
 }
 
-static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res) {
+static struct addrinfo* getCustomHosts(const size_t netid, const char* _Nonnull name,
+                                       const struct addrinfo* _Nonnull pai) {
+    struct addrinfo sentinel = {};
+    struct addrinfo *res0, *res;
+    res = &sentinel;
+    std::vector<std::string> hosts = getCustomizedTableByName(netid, name);
+    for (const std::string& host : hosts) {
+        int error = getaddrinfo_numeric(host.c_str(), nullptr, *pai, &res0);
+        if (!error && res0 != nullptr) {
+            res->ai_next = res0;
+            res = res0;
+            res0 = nullptr;
+        }
+    }
+    return sentinel.ai_next;
+}
+
+static bool files_getaddrinfo(const size_t netid, const char* name, const addrinfo* pai,
+                              addrinfo** res) {
     struct addrinfo sentinel = {};
     struct addrinfo *p, *cur;
-    FILE* hostf = NULL;
+    FILE* hostf = nullptr;
 
     cur = &sentinel;
-
     _sethtent(&hostf);
-    while ((p = _gethtent(&hostf, name, pai)) != NULL) {
+    while ((p = _gethtent(&hostf, name, pai)) != nullptr) {
         cur->ai_next = p;
         while (cur && cur->ai_next) cur = cur->ai_next;
     }
     _endhtent(&hostf);
 
+    if ((p = getCustomHosts(netid, name, pai)) != nullptr) {
+        cur->ai_next = p;
+    }
+
     *res = sentinel.ai_next;
-    return sentinel.ai_next != NULL;
+    return sentinel.ai_next != nullptr;
 }
 
 /* resolver logic */
@@ -1583,7 +1585,6 @@
  */
 static int res_queryN(const char* name, res_target* target, res_state res, int* herrno) {
     uint8_t buf[MAXPACKET];
-    HEADER* hp;
     int n;
     struct res_target* t;
     int rcode;
@@ -1596,10 +1597,7 @@
     ancount = 0;
 
     for (t = target; t; t = t->next) {
-        uint8_t* answer;
-        int anslen;
-
-        hp = (HEADER*) (void*) t->answer;
+        HEADER* hp = (HEADER*)(void*)t->answer.data();
         bool retried = false;
     again:
         hp->rcode = NOERROR; /* default */
@@ -1607,12 +1605,12 @@
         /* make it easier... */
         int cl = t->qclass;
         int type = t->qtype;
-        answer = t->answer;
-        anslen = t->anslen;
+        const int anslen = t->answer.size();
 
         LOG(DEBUG) << __func__ << ": (" << cl << ", " << type << ")";
 
-        n = res_nmkquery(res, QUERY, name, cl, type, NULL, 0, NULL, buf, sizeof(buf));
+        n = res_nmkquery(QUERY, name, cl, type, /*data=*/nullptr, /*datalen=*/0, buf, sizeof(buf),
+                         res->netcontext_flags);
         if (n > 0 &&
             (res->netcontext_flags &
              (NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS)) &&
@@ -1624,7 +1622,7 @@
             return n;
         }
 
-        n = res_nsend(res, buf, n, answer, anslen, &rcode, 0);
+        n = res_nsend(res, buf, n, t->answer.data(), anslen, &rcode, 0);
         if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
             // Record rcode from DNS response header only if no timeout.
             // Keep rcode timeout for reporting later if any.
@@ -1693,7 +1691,7 @@
     assert(name != NULL);
     assert(target != NULL);
 
-    hp = (HEADER*) (void*) target->answer; /*XXX*/
+    hp = (HEADER*)(void*)target->answer.data();
 
     errno = 0;
     *herrno = HOST_NOT_FOUND; /* default, if we never query */
@@ -1725,7 +1723,7 @@
          * the domain stuff is tried.  Will have a better
          * fix after thread pools are used.
          */
-        _resolv_populate_res_for_net(res);
+        resolv_populate_res_for_net(res);
 
         for (const auto& domain : res->search_domains) {
             ret = res_querydomainN(name, domain.c_str(), target, res, herrno);
diff --git a/gethnamaddr.cpp b/gethnamaddr.cpp
index 0d9683c..23f94bb 100644
--- a/gethnamaddr.cpp
+++ b/gethnamaddr.cpp
@@ -74,8 +74,10 @@
 
 #include "hostent.h"
 #include "netd_resolv/resolv.h"
+#include "res_comp.h"
+#include "res_debug.h"  // p_class(), p_type()
+#include "res_init.h"
 #include "resolv_cache.h"
-#include "resolv_private.h"
 #include "stats.pb.h"
 
 using android::net::NetworkDnsEventReported;
@@ -90,12 +92,6 @@
 #define ALIGNBYTES (sizeof(uintptr_t) - 1)
 #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
 
-#define maybe_ok(res, nm, ok) ((ok)(nm) != 0)
-#define maybe_hnok(res, hn) maybe_ok((res), (hn), res_hnok)
-#define maybe_dnok(res, dn) maybe_ok((res), (dn), res_dnok)
-
-#define MAXPACKET (8 * 1024)
-
 constexpr int MAXADDRS = 35;
 
 typedef union {
@@ -116,7 +112,7 @@
 static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
                            const android_net_context* netcontext, getnamaddr* info,
                            NetworkDnsEventReported* event);
-static int dns_gethtbyname(const char* name, int af, getnamaddr* info);
+static int dns_gethtbyname(ResState* res, const char* name, int af, getnamaddr* info);
 
 #define BOUNDED_INCR(x)      \
     do {                     \
@@ -143,7 +139,6 @@
     char tbuf[MAXDNAME];
     char* addr_ptrs[MAXADDRS];
     const char* tname;
-    int (*name_ok)(const char*);
     std::vector<char*> aliases;
 
     _DIAGASSERT(answer != NULL);
@@ -152,6 +147,8 @@
     tname = qname;
     hent->h_name = NULL;
     eom = answer->buf + anslen;
+
+    bool (*name_ok)(const char* dn);
     switch (qtype) {
         case T_A:
         case T_AAAA:
@@ -178,7 +175,7 @@
     if (qdcount != 1) goto no_recovery;
 
     n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
-    if ((n < 0) || !maybe_ok(res, bp, name_ok)) goto no_recovery;
+    if ((n < 0) || !name_ok(bp)) goto no_recovery;
 
     BOUNDED_INCR(n + QFIXEDSZ);
     if (qtype == T_A || qtype == T_AAAA) {
@@ -199,7 +196,7 @@
     had_error = 0;
     while (ancount-- > 0 && cp < eom && !had_error) {
         n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
-        if ((n < 0) || !maybe_ok(res, bp, name_ok)) {
+        if ((n < 0) || !name_ok(bp)) {
             had_error++;
             continue;
         }
@@ -220,7 +217,7 @@
         }
         if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
             n = dn_expand(answer->buf, eom, cp, tbuf, (int) sizeof tbuf);
-            if ((n < 0) || !maybe_ok(res, tbuf, name_ok)) {
+            if ((n < 0) || !name_ok(tbuf)) {
                 had_error++;
                 continue;
             }
@@ -247,7 +244,7 @@
         }
         if (qtype == T_PTR && type == T_CNAME) {
             n = dn_expand(answer->buf, eom, cp, tbuf, (int) sizeof tbuf);
-            if (n < 0 || !maybe_dnok(res, tbuf)) {
+            if (n < 0 || !res_dnok(tbuf)) {
                 had_error++;
                 continue;
             }
@@ -280,7 +277,7 @@
                     continue; /* XXX - had_error++ ? */
                 }
                 n = dn_expand(answer->buf, eom, cp, bp, (int) (ep - bp));
-                if ((n < 0) || !maybe_hnok(res, bp)) {
+                if ((n < 0) || !res_hnok(bp)) {
                     had_error++;
                     break;
                 }
@@ -393,9 +390,8 @@
                          NetworkDnsEventReported* event) {
     getnamaddr info;
 
-    res_state res = res_get_state();
-    if (res == nullptr) return EAI_MEMORY;
-    res_setnetcontext(res, netcontext, event);
+    ResState res;
+    res_init(&res, netcontext, event);
 
     size_t size;
     switch (af) {
@@ -450,7 +446,7 @@
     info.buf = buf;
     info.buflen = buflen;
     if (_hf_gethtbyname2(name, af, &info)) {
-        int error = dns_gethtbyname(name, af, &info);
+        int error = dns_gethtbyname(&res, name, af, &info);
         if (error != 0) return error;
     }
     *result = hp;
@@ -652,7 +648,7 @@
                          });
 }
 
-static int dns_gethtbyname(const char* name, int addr_type, getnamaddr* info) {
+static int dns_gethtbyname(ResState* res, const char* name, int addr_type, getnamaddr* info) {
     int n, type;
     info->hp->h_addrtype = addr_type;
 
@@ -670,9 +666,6 @@
     }
     auto buf = std::make_unique<querybuf>();
 
-    res_state res = res_get_state();
-    if (!res) return EAI_MEMORY;
-
     int he;
     n = res_nsearch(res, name, C_IN, type, buf->buf, (int)sizeof(buf->buf), &he);
     if (n < 0) {
@@ -734,12 +727,10 @@
 
     auto buf = std::make_unique<querybuf>();
 
-    res_state res = res_get_state();
-    if (!res) return EAI_MEMORY;
-
-    res_setnetcontext(res, netcontext, event);
+    ResState res;
+    res_init(&res, netcontext, event);
     int he;
-    n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);
+    n = res_nquery(&res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);
     if (n < 0) {
         LOG(DEBUG) << __func__ << ": res_nquery failed (" << n << ")";
         // Note that res_nquery() doesn't set the pair NETDB_INTERNAL and errno.
diff --git a/include/netd_resolv/resolv.h b/include/netd_resolv/resolv.h
index afd63f5..596a022 100644
--- a/include/netd_resolv/resolv.h
+++ b/include/netd_resolv/resolv.h
@@ -28,8 +28,7 @@
 
 #pragma once
 
-#include "params.h"
-
+#include <arpa/nameser.h>
 #include <netinet/in.h>
 
 /*
@@ -43,6 +42,9 @@
  */
 #define MARK_UNSET 0u
 
+#define NET_CONTEXT_INVALID_UID ((uid_t)-1)
+#define NET_CONTEXT_INVALID_PID ((pid_t)-1)
+
 /*
  * A struct to capture context relevant to network operations.
  *
@@ -59,11 +61,12 @@
     unsigned app_mark;
     unsigned dns_netid;
     unsigned dns_mark;
-    uid_t uid;
+    uid_t uid = NET_CONTEXT_INVALID_UID;
     unsigned flags;
+    // Variable to store the pid of the application sending DNS query.
+    pid_t pid = NET_CONTEXT_INVALID_PID;
 };
 
-#define NET_CONTEXT_INVALID_UID ((uid_t) -1)
 #define NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS 0x00000001
 #define NET_CONTEXT_FLAG_USE_EDNS 0x00000002
 #define NET_CONTEXT_FLAG_USE_DNS_OVER_TLS 0x00000004
@@ -74,7 +77,30 @@
 typedef void (*get_network_context_callback)(unsigned netid, uid_t uid,
                                              android_net_context* netcontext);
 typedef void (*log_callback)(const char* msg);
-typedef int (*tagSocketCallback)(int sockFd, uint32_t tag, uid_t uid);
+typedef int (*tagSocketCallback)(int sockFd, uint32_t tag, uid_t uid, pid_t pid);
+
+// The DnsResolver module invokes this callback once before starting each DNS
+// lookup. The callback receives the android_net_context associated with the
+// request, and the (possibly unqualified) hostname requested by the app via
+// getaddrinfo() or gethostbyname().
+//
+// If the callback returns false, the DnsResolver will abort the request
+// returning EAI_SYSTEM. If the callback returns true, the query will proceed as
+// usual.
+//
+// If this callback is not present (i.e. set to nullptr), the effect is the same
+// of returning true.
+//
+// This callback *will* be invoked concurrently from multiple threads. It must
+// peform its own locking when accessing shared data structures. Furthermore,
+// the callback must not sleep nor perform RPC requests.
+//
+// Be mindful that hostnames could contain sensitive user data. Do not log them
+// and do not transmit them to third parties without explicit user
+// authorization.
+//
+typedef bool (*evaluate_domain_name_callback)(
+    const android_net_context &netcontext, const char *host);
 
 /*
  * Some functions needed by the resolver (e.g. checkCallingPermission()) live in
@@ -87,11 +113,21 @@
     get_network_context_callback get_network_context;
     log_callback log;
     tagSocketCallback tagSocket;
+    evaluate_domain_name_callback evaluate_domain_name;
 };
 
 #define TAG_SYSTEM_DNS 0xFFFFFF82
 
+#define LIBNETD_RESOLV_PUBLIC extern "C" [[gnu::visibility("default")]]
+
 LIBNETD_RESOLV_PUBLIC bool resolv_has_nameservers(unsigned netid);
 
 // Set callbacks and bring DnsResolver up.
 LIBNETD_RESOLV_PUBLIC bool resolv_init(const ResolverNetdCallbacks* callbacks);
+
+// Function that performs RDNS in local cache. The |domain_name_size| is the size of domain_name
+// buffer, which is recommended to NS_MAXDNAME. Function return false if hostname not found or
+// domain_name_size > NS_MAXDNAME.
+LIBNETD_RESOLV_PUBLIC bool resolv_gethostbyaddr_from_cache(unsigned netId, char domain_name[],
+                                                           size_t domain_name_size,
+                                                           const char* ip_address, int af);
diff --git a/include/netd_resolv/resolv_stub.h b/include/netd_resolv/resolv_stub.h
deleted file mode 100644
index 41f56fa..0000000
--- a/include/netd_resolv/resolv_stub.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef NETD_RESOLV_RESOLV_STUB_H
-#define NETD_RESOLV_RESOLV_STUB_H
-
-#include "resolv.h"
-#include "stats.h"
-
-// Struct containing function pointers for every function exported by libnetd_resolv.
-extern struct ResolvStub {
-    bool (*resolv_has_nameservers)(unsigned netid);
-
-    bool (*resolv_init)(const ResolverNetdCallbacks& callbacks);
-} RESOLV_STUB;
-
-int resolv_stub_init();
-
-#endif  // NETD_RESOLV_RESOLV_STUB_H
diff --git a/include/netd_resolv/stats.h b/include/netd_resolv/stats.h
deleted file mode 100644
index 7bfe845..0000000
--- a/include/netd_resolv/stats.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NETD_RES_STATS_H
-#define NETD_RES_STATS_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <sys/socket.h>
-#include <time.h>
-
-#include "params.h"
-
-#define RCODE_INTERNAL_ERROR 254
-#define RCODE_TIMEOUT 255
-
-struct res_sample {
-    time_t at;      // time in s at which the sample was recorded
-    uint16_t rtt;   // round-trip time in ms
-    uint8_t rcode;  // the DNS rcode or RCODE_XXX defined above
-};
-
-// Resolver reachability statistics and run-time parameters.
-struct res_stats {
-    // Stats of the last <sample_count> queries.
-    res_sample samples[MAXNSSAMPLES];
-    // The number of samples stored.
-    uint8_t sample_count;
-    // The next sample to modify.
-    uint8_t sample_next;
-};
-
-// Aggregates the reachability statistics for the given server based on on the stored samples.
-LIBNETD_RESOLV_PUBLIC void android_net_res_stats_aggregate(res_stats* stats, int* successes,
-                                                           int* errors, int* timeouts,
-                                                           int* internal_errors, int* rtt_avg,
-                                                           time_t* last_sample_time);
-
-LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_info_for_net(
-        unsigned netid, int* nscount, sockaddr_storage servers[MAXNS], int* dcount,
-        char domains[MAXDNSRCH][MAXDNSRCHPATH], res_params* params, res_stats stats[MAXNS],
-        int* wait_for_pending_req_timeout_count);
-
-// Returns an array of bools indicating which servers are considered good
-LIBNETD_RESOLV_PUBLIC int android_net_res_stats_get_usable_servers(const res_params* params,
-                                                                   res_stats stats[], int nscount,
-                                                                   bool valid_servers[]);
-
-#endif  // NETD_RES_STATS_H
diff --git a/libnetd_resolv.map.txt b/libnetd_resolv.map.txt
index be193db..06e67aa 100644
--- a/libnetd_resolv.map.txt
+++ b/libnetd_resolv.map.txt
@@ -22,6 +22,7 @@
   global:
     resolv_has_nameservers;
     resolv_init;
+    resolv_gethostbyaddr_from_cache;
   local:
     *;
 };
diff --git a/include/netd_resolv/params.h b/params.h
similarity index 89%
rename from include/netd_resolv/params.h
rename to params.h
index 93e6287..82cd058 100644
--- a/include/netd_resolv/params.h
+++ b/params.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef NETD_RESOLV_PARAMS_H
-#define NETD_RESOLV_PARAMS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -34,7 +33,3 @@
     int base_timeout_msec;      // base query retry timeout (if 0, use RES_TIMEOUT)
     int retry_count;            // number of retries
 };
-
-#define LIBNETD_RESOLV_PUBLIC extern "C" [[gnu::visibility("default")]]
-
-#endif  // NETD_RESOLV_PARAMS_H
diff --git a/res_cache.cpp b/res_cache.cpp
index 4b5ba28..5f87cca 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -58,10 +58,20 @@
 
 #include <server_configurable_flags/get_flags.h>
 
+#include "DnsStats.h"
+#include "res_comp.h"
 #include "res_debug.h"
 #include "resolv_private.h"
+#include "util.h"
 
 using android::base::StringAppendF;
+using android::net::DnsQueryEvent;
+using android::net::DnsStats;
+using android::net::PROTO_DOT;
+using android::net::PROTO_TCP;
+using android::net::PROTO_UDP;
+using android::netdutils::DumpWriter;
+using android::netdutils::IPSockAddr;
 
 /* This code implements a small and *simple* DNS resolver cache.
  *
@@ -140,6 +150,7 @@
  * *****************************************
  */
 const int CONFIG_MAX_ENTRIES = 64 * 2 * 5;
+constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1;
 
 static time_t _time_now(void) {
     struct timeval tv;
@@ -425,6 +436,7 @@
 
     while (numBytes > 0 && p < end) {
         hash = hash * FNV_MULT ^ *p++;
+        numBytes--;
     }
     packet->cursor = p;
     return hash;
@@ -862,10 +874,53 @@
 /* Maximum time for a thread to wait for an pending request */
 constexpr int PENDING_REQUEST_TIMEOUT = 20;
 
-// lock protecting everything in the resolve_cache_info structs (next ptr, etc)
+// lock protecting everything in NetConfig.
 static std::mutex cache_mutex;
 static std::condition_variable cv;
 
+namespace {
+
+// Map format: ReturnCode:rate_denom
+// if the ReturnCode is not associated with any rate_denom, use default
+// Sampling rate varies by return code; events to log are chosen randomly, with a
+// probability proportional to the sampling rate.
+constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10";
+
+std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() {
+    using android::base::ParseInt;
+    using android::base::ParseUint;
+    using android::base::Split;
+    using server_configurable_flags::GetServerConfigurableFlag;
+    std::unordered_map<int, uint32_t> sampling_rate_map{};
+    std::vector<std::string> subsampling_vector =
+            Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map",
+                                            DEFAULT_SUBSAMPLING_MAP),
+                  " ");
+    for (const auto& pair : subsampling_vector) {
+        std::vector<std::string> rate_denom = Split(pair, ":");
+        int return_code;
+        uint32_t denom;
+        if (rate_denom.size() != 2) {
+            LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair;
+            continue;
+        }
+        if (rate_denom[0] == "default") {
+            return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY;
+        } else if (!ParseInt(rate_denom[0], &return_code)) {
+            LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+            continue;
+        }
+        if (!ParseUint(rate_denom[1], &denom)) {
+            LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
+            continue;
+        }
+        sampling_rate_map[return_code] = denom;
+    }
+    return sampling_rate_map;
+}
+
+}  // namespace
+
 // Note that Cache is not thread-safe per se, access to its members must be protected
 // by an external mutex.
 //
@@ -923,21 +978,29 @@
     } pending_requests{};
 };
 
-// TODO: allocate with new
-struct resolv_cache_info {
-    unsigned netid;
-    Cache* cache;  // TODO: use unique_ptr or embed
-    struct resolv_cache_info* next;
-    int nscount;
+struct NetConfig {
+    explicit NetConfig(unsigned netId) : netid(netId) {
+        cache = std::make_unique<Cache>();
+        dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
+    }
+
+    const unsigned netid;
+    std::unique_ptr<Cache> cache;
+    int nscount = 0;
     std::vector<std::string> nameservers;
-    struct addrinfo* nsaddrinfo[MAXNS];  // TODO: Use struct sockaddr_storage.
-    int revision_id;  // # times the nameservers have been replaced
-    res_params params;
-    struct res_stats nsstats[MAXNS];
+    std::vector<IPSockAddr> nameserverSockAddrs;
+    int revision_id = 0;  // # times the nameservers have been replaced
+    res_params params{};
+    res_stats nsstats[MAXNS]{};
     std::vector<std::string> search_domains;
-    int wait_for_pending_req_timeout_count;
+    int wait_for_pending_req_timeout_count = 0;
     // Map format: ReturnCode:rate_denom
     std::unordered_map<int, uint32_t> dns_event_subsampling_map;
+    DnsStats dnsStats;
+    // Customized hostname/address table will be stored in customizedTable.
+    // If resolverParams.hosts is empty, the existing customized table will be erased.
+    HostMapping customizedTable = {};
+    int tc_mode = aidl::android::net::IDnsResolver::TC_MODE_DEFAULT;
 };
 
 /* gets cache associated with a network, or NULL if none exists */
@@ -1115,8 +1178,8 @@
     }
 }
 
-// gets a resolv_cache_info associated with a network, or NULL if not found
-static resolv_cache_info* find_cache_info_locked(unsigned netid) REQUIRES(cache_mutex);
+// Get a NetConfig associated with a network, or nullptr if not found.
+static NetConfig* find_netconfig_locked(unsigned netid) REQUIRES(cache_mutex);
 
 ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int querylen, void* answer,
                                       int answersize, int* answerlen, uint32_t flags) {
@@ -1178,7 +1241,7 @@
                 return RESOLV_CACHE_NOTFOUND;
             }
             if (ret == false) {
-                resolv_cache_info* info = find_cache_info_locked(netid);
+                NetConfig* info = find_netconfig_locked(netid);
                 if (info != NULL) {
                     info->wait_for_pending_req_timeout_count++;
                 }
@@ -1282,164 +1345,167 @@
     return 0;
 }
 
-// Head of the list of caches.
-static struct resolv_cache_info res_cache_list GUARDED_BY(cache_mutex);
+bool resolv_gethostbyaddr_from_cache(unsigned netid, char domain_name[], size_t domain_name_size,
+                                     const char* ip_address, int af) {
+    if (domain_name_size > NS_MAXDNAME) {
+        LOG(WARNING) << __func__ << ": invalid domain_name_size " << domain_name_size;
+        return false;
+    } else if (ip_address == nullptr || ip_address[0] == '\0') {
+        LOG(WARNING) << __func__ << ": invalid ip_address";
+        return false;
+    } else if (af != AF_INET && af != AF_INET6) {
+        LOG(WARNING) << __func__ << ": unsupported AF";
+        return false;
+    }
 
-// insert resolv_cache_info into the list of resolv_cache_infos
-static void insert_cache_info_locked(resolv_cache_info* cache_info);
-// creates a resolv_cache_info
-static resolv_cache_info* create_cache_info();
-// Clears nameservers set for |cache_info| and clears the stats
-static void free_nameservers_locked(resolv_cache_info* cache_info);
+    Cache* cache = nullptr;
+    Entry* node = nullptr;
+
+    ns_rr rr;
+    ns_msg handle;
+    ns_rr rr_query;
+
+    struct sockaddr_in sa;
+    struct sockaddr_in6 sa6;
+    char* addr_buf = nullptr;
+
+    std::lock_guard guard(cache_mutex);
+
+    cache = find_named_cache_locked(netid);
+    if (cache == nullptr) {
+        return false;
+    }
+
+    for (node = cache->mru_list.mru_next; node != nullptr && node != &cache->mru_list;
+         node = node->mru_next) {
+        if (node->answer == nullptr) {
+            continue;
+        }
+
+        memset(&handle, 0, sizeof(handle));
+
+        if (ns_initparse(node->answer, node->answerlen, &handle) < 0) {
+            continue;
+        }
+
+        for (int n = 0; n < ns_msg_count(handle, ns_s_an); n++) {
+            memset(&rr, 0, sizeof(rr));
+
+            if (ns_parserr(&handle, ns_s_an, n, &rr)) {
+                continue;
+            }
+
+            if (ns_rr_type(rr) == ns_t_a && af == AF_INET) {
+                addr_buf = (char*)&(sa.sin_addr);
+            } else if (ns_rr_type(rr) == ns_t_aaaa && af == AF_INET6) {
+                addr_buf = (char*)&(sa6.sin6_addr);
+            } else {
+                continue;
+            }
+
+            if (inet_pton(af, ip_address, addr_buf) != 1) {
+                LOG(WARNING) << __func__ << ": inet_pton() fail";
+                return false;
+            }
+
+            if (memcmp(ns_rr_rdata(rr), addr_buf, ns_rr_rdlen(rr)) == 0) {
+                int query_count = ns_msg_count(handle, ns_s_qd);
+                for (int i = 0; i < query_count; i++) {
+                    memset(&rr_query, 0, sizeof(rr_query));
+                    if (ns_parserr(&handle, ns_s_qd, i, &rr_query)) {
+                        continue;
+                    }
+                    strlcpy(domain_name, ns_rr_name(rr_query), domain_name_size);
+                    if (domain_name[0] != '\0') {
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+static std::unordered_map<unsigned, std::unique_ptr<NetConfig>> sNetConfigMap
+        GUARDED_BY(cache_mutex);
+
+// Clears nameservers set for |netconfig| and clears the stats
+static void free_nameservers_locked(NetConfig* netconfig);
 // Order-insensitive comparison for the two set of servers.
 static bool resolv_is_nameservers_equal(const std::vector<std::string>& oldServers,
                                         const std::vector<std::string>& newServers);
-// clears the stats samples contained withing the given cache_info
-static void res_cache_clear_stats_locked(resolv_cache_info* cache_info);
+// clears the stats samples contained withing the given netconfig.
+static void res_cache_clear_stats_locked(NetConfig* netconfig);
 
 // public API for netd to query if name server is set on specific netid
 bool resolv_has_nameservers(unsigned netid) {
     std::lock_guard guard(cache_mutex);
-    resolv_cache_info* info = find_cache_info_locked(netid);
+    NetConfig* info = find_netconfig_locked(netid);
     return (info != nullptr) && (info->nscount > 0);
 }
 
-namespace {
-
-// Map format: ReturnCode:rate_denom
-// if the ReturnCode is not associated with any rate_denom, use default
-// Sampling rate varies by return code; events to log are chosen randomly, with a
-// probability proportional to the sampling rate.
-constexpr const char DEFAULT_SUBSAMPLING_MAP[] = "default:1 0:100 7:10";
-
-std::unordered_map<int, uint32_t> resolv_get_dns_event_subsampling_map() {
-    using android::base::ParseInt;
-    using android::base::ParseUint;
-    using android::base::Split;
-    using server_configurable_flags::GetServerConfigurableFlag;
-    std::unordered_map<int, uint32_t> sampling_rate_map{};
-    std::vector<std::string> subsampling_vector =
-            Split(GetServerConfigurableFlag("netd_native", "dns_event_subsample_map",
-                                            DEFAULT_SUBSAMPLING_MAP),
-                  " ");
-    for (const auto& pair : subsampling_vector) {
-        std::vector<std::string> rate_denom = Split(pair, ":");
-        int return_code;
-        uint32_t denom;
-        if (rate_denom.size() != 2) {
-            LOG(ERROR) << __func__ << ": invalid subsampling_pair = " << pair;
-            continue;
-        }
-        if (rate_denom[0] == "default") {
-            return_code = DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY;
-        } else if (!ParseInt(rate_denom[0], &return_code)) {
-            LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
-            continue;
-        }
-        if (!ParseUint(rate_denom[1], &denom)) {
-            LOG(ERROR) << __func__ << ": parse subsampling_pair failed = " << pair;
-            continue;
-        }
-        sampling_rate_map[return_code] = denom;
-    }
-    return sampling_rate_map;
-}
-
-}  // namespace
-
 int resolv_create_cache_for_net(unsigned netid) {
     std::lock_guard guard(cache_mutex);
-    Cache* cache = find_named_cache_locked(netid);
-    // Should not happen
-    if (cache) {
+    if (sNetConfigMap.find(netid) != sNetConfigMap.end()) {
         LOG(ERROR) << __func__ << ": Cache is already created, netId: " << netid;
         return -EEXIST;
     }
 
-    resolv_cache_info* cache_info = create_cache_info();
-    if (!cache_info) return -ENOMEM;
-    cache_info->netid = netid;
-    cache_info->cache = new Cache;
-    cache_info->dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
-    insert_cache_info_locked(cache_info);
-
+    sNetConfigMap[netid] = std::make_unique<NetConfig>(netid);
     return 0;
 }
 
 void resolv_delete_cache_for_net(unsigned netid) {
     std::lock_guard guard(cache_mutex);
+    sNetConfigMap.erase(netid);
+}
 
-    struct resolv_cache_info* prev_cache_info = &res_cache_list;
+int resolv_flush_cache_for_net(unsigned netid) {
+    std::lock_guard guard(cache_mutex);
 
-    while (prev_cache_info->next) {
-        struct resolv_cache_info* cache_info = prev_cache_info->next;
-
-        if (cache_info->netid == netid) {
-            prev_cache_info->next = cache_info->next;
-            delete cache_info->cache;
-            free_nameservers_locked(cache_info);
-            free(cache_info);
-            break;
-        }
-
-        prev_cache_info = prev_cache_info->next;
+    NetConfig* netconfig = find_netconfig_locked(netid);
+    if (netconfig == nullptr) {
+        return -ENONET;
     }
+    netconfig->cache->flush();
+
+    // Also clear the NS statistics.
+    res_cache_clear_stats_locked(netconfig);
+    return 0;
 }
 
 std::vector<unsigned> resolv_list_caches() {
     std::lock_guard guard(cache_mutex);
-    struct resolv_cache_info* cache_info = res_cache_list.next;
     std::vector<unsigned> result;
-    while (cache_info) {
-        result.push_back(cache_info->netid);
-        cache_info = cache_info->next;
+    result.reserve(sNetConfigMap.size());
+    for (const auto& [netId, _] : sNetConfigMap) {
+        result.push_back(netId);
     }
     return result;
 }
 
-static resolv_cache_info* create_cache_info() {
-    return (struct resolv_cache_info*) calloc(sizeof(struct resolv_cache_info), 1);
-}
-
-// TODO: convert this to a simple and efficient C++ container.
-static void insert_cache_info_locked(struct resolv_cache_info* cache_info) {
-    struct resolv_cache_info* last;
-    for (last = &res_cache_list; last->next; last = last->next) {}
-    last->next = cache_info;
-}
-
 static Cache* find_named_cache_locked(unsigned netid) {
-    resolv_cache_info* info = find_cache_info_locked(netid);
-    if (info != nullptr) return info->cache;
+    NetConfig* info = find_netconfig_locked(netid);
+    if (info != nullptr) return info->cache.get();
     return nullptr;
 }
 
-static resolv_cache_info* find_cache_info_locked(unsigned netid) {
-    struct resolv_cache_info* cache_info = res_cache_list.next;
-
-    while (cache_info) {
-        if (cache_info->netid == netid) {
-            break;
-        }
-
-        cache_info = cache_info->next;
+static NetConfig* find_netconfig_locked(unsigned netid) {
+    if (auto it = sNetConfigMap.find(netid); it != sNetConfigMap.end()) {
+        return it->second.get();
     }
-    return cache_info;
+    return nullptr;
 }
 
 static void resolv_set_experiment_params(res_params* params) {
-    using android::base::ParseInt;
-    using server_configurable_flags::GetServerConfigurableFlag;
-
     if (params->retry_count == 0) {
-        params->retry_count = RES_DFLRETRY;
-        ParseInt(GetServerConfigurableFlag("netd_native", "retry_count", ""), &params->retry_count);
+        params->retry_count = getExperimentFlagInt("retry_count", RES_DFLRETRY);
     }
 
     if (params->base_timeout_msec == 0) {
-        params->base_timeout_msec = RES_TIMEOUT;
-        ParseInt(GetServerConfigurableFlag("netd_native", "retransmission_time_interval", ""),
-                 &params->base_timeout_msec);
+        params->base_timeout_msec =
+                getExperimentFlagInt("retransmission_time_interval", RES_TIMEOUT);
     }
 }
 
@@ -1471,10 +1537,41 @@
     return res;
 }
 
+bool isValidServer(const std::string& server) {
+    const addrinfo hints = {
+            .ai_family = AF_UNSPEC,
+            .ai_socktype = SOCK_DGRAM,
+    };
+    addrinfo* result = nullptr;
+    if (int err = getaddrinfo_numeric(server.c_str(), "53", hints, &result); err != 0) {
+        LOG(WARNING) << __func__ << ": getaddrinfo_numeric(" << server
+                     << ") = " << gai_strerror(err);
+        return false;
+    }
+    freeaddrinfo(result);
+    return true;
+}
+
 }  // namespace
 
-int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers,
-                           const std::vector<std::string>& domains, const res_params& params) {
+std::vector<std::string> getCustomizedTableByName(const size_t netid, const char* hostname) {
+    std::lock_guard guard(cache_mutex);
+    NetConfig* netconfig = find_netconfig_locked(netid);
+
+    std::vector<std::string> result;
+    if (netconfig != nullptr) {
+        const auto& hosts = netconfig->customizedTable.equal_range(hostname);
+        for (auto i = hosts.first; i != hosts.second; ++i) {
+            result.push_back(i->second);
+        }
+    }
+    return result;
+}
+
+int resolv_set_nameservers(
+        unsigned netid, const std::vector<std::string>& servers,
+        const std::vector<std::string>& domains, const res_params& params,
+        const aidl::android::net::ResolverExperimentalOptionsParcel& experimentalOptions) {
     std::vector<std::string> nameservers = filter_nameservers(servers);
     const int numservers = static_cast<int>(nameservers.size());
 
@@ -1482,61 +1579,64 @@
 
     // Parse the addresses before actually locking or changing any state, in case there is an error.
     // As a side effect this also reduces the time the lock is kept.
-    // TODO: find a better way to replace addrinfo*, something like std::vector<SafeAddrinfo>
-    addrinfo* nsaddrinfo[MAXNS];
-    for (int i = 0; i < numservers; i++) {
-        // The addrinfo structures allocated here are freed in free_nameservers_locked().
-        const addrinfo hints = {
-                .ai_flags = AI_NUMERICHOST,
-                .ai_family = AF_UNSPEC,
-                .ai_socktype = SOCK_DGRAM,
-        };
-        const int rt = getaddrinfo_numeric(nameservers[i].c_str(), "53", hints, &nsaddrinfo[i]);
-        if (rt != 0) {
-            for (int j = 0; j < i; j++) {
-                freeaddrinfo(nsaddrinfo[j]);
-            }
-            LOG(INFO) << __func__ << ": getaddrinfo_numeric(" << nameservers[i]
-                      << ") = " << gai_strerror(rt);
-            return -EINVAL;
-        }
+    std::vector<IPSockAddr> ipSockAddrs;
+    ipSockAddrs.reserve(nameservers.size());
+    for (const auto& server : nameservers) {
+        if (!isValidServer(server)) return -EINVAL;
+        ipSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 53));
     }
 
     std::lock_guard guard(cache_mutex);
-    resolv_cache_info* cache_info = find_cache_info_locked(netid);
+    NetConfig* netconfig = find_netconfig_locked(netid);
 
-    if (cache_info == nullptr) return -ENONET;
+    if (netconfig == nullptr) return -ENONET;
 
-    uint8_t old_max_samples = cache_info->params.max_samples;
-    cache_info->params = params;
-    resolv_set_experiment_params(&cache_info->params);
-    if (!resolv_is_nameservers_equal(cache_info->nameservers, nameservers)) {
+    uint8_t old_max_samples = netconfig->params.max_samples;
+    netconfig->params = params;
+    resolv_set_experiment_params(&netconfig->params);
+    if (!resolv_is_nameservers_equal(netconfig->nameservers, nameservers)) {
         // free current before adding new
-        free_nameservers_locked(cache_info);
-        cache_info->nameservers = std::move(nameservers);
+        free_nameservers_locked(netconfig);
+        netconfig->nameservers = std::move(nameservers);
         for (int i = 0; i < numservers; i++) {
-            cache_info->nsaddrinfo[i] = nsaddrinfo[i];
             LOG(INFO) << __func__ << ": netid = " << netid
-                      << ", addr = " << cache_info->nameservers[i];
+                      << ", addr = " << netconfig->nameservers[i];
         }
-        cache_info->nscount = numservers;
+        netconfig->nameserverSockAddrs = std::move(ipSockAddrs);
+        netconfig->nscount = numservers;
     } else {
-        if (cache_info->params.max_samples != old_max_samples) {
+        if (netconfig->params.max_samples != old_max_samples) {
             // If the maximum number of samples changes, the overhead of keeping the most recent
             // samples around is not considered worth the effort, so they are cleared instead.
             // All other parameters do not affect shared state: Changing these parameters does
             // not invalidate the samples, as they only affect aggregation and the conditions
             // under which servers are considered usable.
-            res_cache_clear_stats_locked(cache_info);
-        }
-        for (int j = 0; j < numservers; j++) {
-            freeaddrinfo(nsaddrinfo[j]);
+            res_cache_clear_stats_locked(netconfig);
         }
     }
 
     // Always update the search paths. Cache-flushing however is not necessary,
     // since the stored cache entries do contain the domain, not just the host name.
-    cache_info->search_domains = filter_domains(domains);
+    netconfig->search_domains = filter_domains(domains);
+
+    // Setup stats for cleartext dns servers.
+    if (!netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_TCP) ||
+        !netconfig->dnsStats.setServers(netconfig->nameserverSockAddrs, PROTO_UDP)) {
+        LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats";
+        return -EINVAL;
+    }
+    netconfig->customizedTable.clear();
+    for (const auto& host : experimentalOptions.hosts) {
+        if (!host.hostName.empty() && !host.ipAddr.empty())
+            netconfig->customizedTable.emplace(host.hostName, host.ipAddr);
+    }
+
+    if (experimentalOptions.tcMode < aidl::android::net::IDnsResolver::TC_MODE_DEFAULT ||
+        experimentalOptions.tcMode >= aidl::android::net::IDnsResolver::TC_MODE_MAX) {
+        LOG(WARNING) << __func__ << ": netid = " << netid << ", invalid TC mode";
+        return -EINVAL;
+    }
+    netconfig->tc_mode = experimentalOptions.tcMode;
 
     return 0;
 }
@@ -1554,56 +1654,51 @@
     return olds == news;
 }
 
-static void free_nameservers_locked(resolv_cache_info* cache_info) {
-    int i;
-    for (i = 0; i < cache_info->nscount; i++) {
-        cache_info->nameservers.clear();
-        if (cache_info->nsaddrinfo[i] != nullptr) {
-            freeaddrinfo(cache_info->nsaddrinfo[i]);
-            cache_info->nsaddrinfo[i] = nullptr;
-        }
-    }
-    cache_info->nscount = 0;
-    res_cache_clear_stats_locked(cache_info);
+static void free_nameservers_locked(NetConfig* netconfig) {
+    netconfig->nscount = 0;
+    netconfig->nameservers.clear();
+    netconfig->nameserverSockAddrs.clear();
+    res_cache_clear_stats_locked(netconfig);
 }
 
-void _resolv_populate_res_for_net(res_state statp) {
-    if (statp == NULL) {
+void resolv_populate_res_for_net(ResState* statp) {
+    if (statp == nullptr) {
         return;
     }
     LOG(INFO) << __func__ << ": netid=" << statp->netid;
 
     std::lock_guard guard(cache_mutex);
-    resolv_cache_info* info = find_cache_info_locked(statp->netid);
-    if (info != NULL) {
-        int nserv;
-        struct addrinfo* ai;
-        for (nserv = 0; nserv < MAXNS; nserv++) {
-            ai = info->nsaddrinfo[nserv];
-            if (ai == NULL) {
-                break;
-            }
+    NetConfig* info = find_netconfig_locked(statp->netid);
+    if (info == nullptr) return;
 
-            if ((size_t)ai->ai_addrlen <= sizeof(statp->nsaddrs[0])) {
-                memcpy(&statp->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
-            } else {
-                LOG(INFO) << __func__ << ": found too long addrlen";
-            }
+    // TODO: Convert nsaddrs[] to c++ container and remove the size-checking.
+    const int serverNum = std::min(MAXNS, static_cast<int>(info->nameserverSockAddrs.size()));
+
+    for (int nserv = 0; nserv < serverNum; nserv++) {
+        sockaddr_storage ss = info->nameserverSockAddrs.at(nserv);
+
+        if (auto sockaddr_len = sockaddrSize(ss); sockaddr_len != 0) {
+            memcpy(&statp->nsaddrs[nserv], &ss, sockaddr_len);
+        } else {
+            LOG(WARNING) << __func__ << ": can't get sa_len from "
+                         << info->nameserverSockAddrs.at(nserv);
         }
-        statp->nscount = nserv;
-        statp->search_domains = info->search_domains;
     }
+
+    statp->nscount = serverNum;
+    statp->search_domains = info->search_domains;
+    statp->tc_mode = info->tc_mode;
 }
 
 /* Resolver reachability statistics. */
 
-static void _res_cache_add_stats_sample_locked(res_stats* stats, const res_sample* sample,
-                                               int max_samples) {
+static void res_cache_add_stats_sample_locked(res_stats* stats, const res_sample& sample,
+                                              int max_samples) {
     // Note: This function expects max_samples > 0, otherwise a (harmless) modification of the
     // allocated but supposedly unused memory for samples[0] will happen
     LOG(INFO) << __func__ << ": adding sample to stats, next = " << unsigned(stats->sample_next)
               << ", count = " << unsigned(stats->sample_count);
-    stats->samples[stats->sample_next] = *sample;
+    stats->samples[stats->sample_next] = sample;
     if (stats->sample_count < max_samples) {
         ++stats->sample_count;
     }
@@ -1612,17 +1707,17 @@
     }
 }
 
-static void res_cache_clear_stats_locked(resolv_cache_info* cache_info) {
+static void res_cache_clear_stats_locked(NetConfig* netconfig) {
     for (int i = 0; i < MAXNS; ++i) {
-        cache_info->nsstats[i].sample_count = 0;
-        cache_info->nsstats[i].sample_next = 0;
+        netconfig->nsstats[i].sample_count = 0;
+        netconfig->nsstats[i].sample_next = 0;
     }
 
     // Increment the revision id to ensure that sample state is not written back if the
     // servers change; in theory it would suffice to do so only if the servers or
     // max_samples actually change, in practice the overhead of checking is higher than the
     // cost, and overflows are unlikely.
-    ++cache_info->revision_id;
+    ++netconfig->revision_id;
 }
 
 int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount,
@@ -1633,7 +1728,7 @@
     int revision_id = -1;
     std::lock_guard guard(cache_mutex);
 
-    resolv_cache_info* info = find_cache_info_locked(netid);
+    NetConfig* info = find_netconfig_locked(netid);
     if (info) {
         if (info->nscount > MAXNS) {
             LOG(INFO) << __func__ << ": nscount " << info->nscount << " > MAXNS " << MAXNS;
@@ -1641,31 +1736,18 @@
             return -1;
         }
         int i;
-        for (i = 0; i < info->nscount; i++) {
-            // Verify that the following assumptions are held, failure indicates corruption:
-            //  - getaddrinfo() may never return a sockaddr > sockaddr_storage
-            //  - all addresses are valid
-            //  - there is only one address per addrinfo thanks to numeric resolution
-            int addrlen = info->nsaddrinfo[i]->ai_addrlen;
-            if (addrlen < (int) sizeof(struct sockaddr) || addrlen > (int) sizeof(servers[0])) {
-                LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_addrlen == " << addrlen;
-                errno = EMSGSIZE;
-                return -1;
-            }
-            if (info->nsaddrinfo[i]->ai_addr == NULL) {
-                LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_addr == NULL";
-                errno = ENOENT;
-                return -1;
-            }
-            if (info->nsaddrinfo[i]->ai_next != NULL) {
-                LOG(INFO) << __func__ << ": nsaddrinfo[" << i << "].ai_next != NULL";
-                errno = ENOTUNIQ;
-                return -1;
-            }
-        }
         *nscount = info->nscount;
+
+        // It shouldn't happen, but just in case of buffer overflow.
+        if (info->nscount != static_cast<int>(info->nameserverSockAddrs.size())) {
+            LOG(INFO) << __func__ << ": nscount " << info->nscount
+                      << " != " << info->nameserverSockAddrs.size();
+            errno = EFAULT;
+            return -1;
+        }
+
         for (i = 0; i < info->nscount; i++) {
-            memcpy(&servers[i], info->nsaddrinfo[i]->ai_addr, info->nsaddrinfo[i]->ai_addrlen);
+            servers[i] = info->nameserverSockAddrs.at(i);
             stats[i] = info->nsstats[i];
         }
 
@@ -1684,10 +1766,10 @@
 std::vector<std::string> resolv_cache_dump_subsampling_map(unsigned netid) {
     using android::base::StringPrintf;
     std::lock_guard guard(cache_mutex);
-    resolv_cache_info* cache_info = find_cache_info_locked(netid);
-    if (cache_info == nullptr) return {};
+    NetConfig* netconfig = find_netconfig_locked(netid);
+    if (netconfig == nullptr) return {};
     std::vector<std::string> result;
-    for (const auto& pair : cache_info->dns_event_subsampling_map) {
+    for (const auto& pair : netconfig->dns_event_subsampling_map) {
         result.push_back(StringPrintf("%s:%d",
                                       (pair.first == DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY)
                                               ? "default"
@@ -1703,9 +1785,9 @@
 // Returns the subsampling rate if the event should be sampled, or 0 if it should be discarded.
 uint32_t resolv_cache_get_subsampling_denom(unsigned netid, int return_code) {
     std::lock_guard guard(cache_mutex);
-    resolv_cache_info* cache_info = find_cache_info_locked(netid);
-    if (cache_info == nullptr) return 0;  // Don't log anything at all.
-    const auto& subsampling_map = cache_info->dns_event_subsampling_map;
+    NetConfig* netconfig = find_netconfig_locked(netid);
+    if (netconfig == nullptr) return 0;  // Don't log anything at all.
+    const auto& subsampling_map = netconfig->dns_event_subsampling_map;
     auto search_returnCode = subsampling_map.find(return_code);
     uint32_t denom;
     if (search_returnCode != subsampling_map.end()) {
@@ -1719,7 +1801,7 @@
 
 int resolv_cache_get_resolver_stats(unsigned netid, res_params* params, res_stats stats[MAXNS]) {
     std::lock_guard guard(cache_mutex);
-    resolv_cache_info* info = find_cache_info_locked(netid);
+    NetConfig* info = find_netconfig_locked(netid);
     if (info) {
         memcpy(stats, info->nsstats, sizeof(info->nsstats));
         *params = info->params;
@@ -1729,15 +1811,22 @@
     return -1;
 }
 
-void _resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, int ns,
-                                             const res_sample* sample, int max_samples) {
-    if (max_samples <= 0) return;
+void resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, const sockaddr* sa,
+                                            const res_sample& sample, int max_samples) {
+    if (max_samples <= 0 || sa == nullptr) return;
 
     std::lock_guard guard(cache_mutex);
-    resolv_cache_info* info = find_cache_info_locked(netid);
+    NetConfig* info = find_netconfig_locked(netid);
 
     if (info && info->revision_id == revision_id) {
-        _res_cache_add_stats_sample_locked(&info->nsstats[ns], sample, max_samples);
+        const int serverNum = std::min(MAXNS, static_cast<int>(info->nameserverSockAddrs.size()));
+        const IPSockAddr ipsa = IPSockAddr::toIPSockAddr(*sa);
+        for (int ns = 0; ns < serverNum; ns++) {
+            if (ipsa == info->nameserverSockAddrs.at(ns)) {
+                res_cache_add_stats_sample_locked(&info->nsstats[ns], sample, max_samples);
+                return;
+            }
+        }
     }
 }
 
@@ -1779,3 +1868,60 @@
     *expiration = e->expires;
     return 0;
 }
+
+int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::string>& servers) {
+    std::lock_guard guard(cache_mutex);
+    const auto info = find_netconfig_locked(netid);
+
+    if (info == nullptr) return -ENONET;
+
+    std::vector<IPSockAddr> serverSockAddrs;
+    serverSockAddrs.reserve(servers.size());
+    for (const auto& server : servers) {
+        serverSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 853));
+    }
+
+    if (!info->dnsStats.setServers(serverSockAddrs, android::net::PROTO_DOT)) {
+        LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats";
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+bool resolv_stats_add(unsigned netid, const android::netdutils::IPSockAddr& server,
+                      const DnsQueryEvent* record) {
+    if (record == nullptr) return false;
+
+    std::lock_guard guard(cache_mutex);
+    if (const auto info = find_netconfig_locked(netid); info != nullptr) {
+        return info->dnsStats.addStats(server, *record);
+    }
+    return false;
+}
+
+void resolv_stats_dump(DumpWriter& dw, unsigned netid) {
+    std::lock_guard guard(cache_mutex);
+    if (const auto info = find_netconfig_locked(netid); info != nullptr) {
+        info->dnsStats.dump(dw);
+    }
+}
+
+void resolv_oem_options_dump(DumpWriter& dw, unsigned netid) {
+    std::lock_guard guard(cache_mutex);
+    if (const auto info = find_netconfig_locked(netid); info != nullptr) {
+        // TODO: dump info->hosts
+        dw.println("TC mode: %s", tc_mode_to_str(info->tc_mode));
+    }
+}
+
+const char* tc_mode_to_str(const int mode) {
+    switch (mode) {
+        case aidl::android::net::IDnsResolver::TC_MODE_DEFAULT:
+            return "default";
+        case aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP:
+            return "UDP_TCP";
+        default:
+            return "unknown";
+    }
+}
diff --git a/res_comp.cpp b/res_comp.cpp
index c9889cb..9a191fa 100644
--- a/res_comp.cpp
+++ b/res_comp.cpp
@@ -70,14 +70,13 @@
  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "res_comp.h"
+
 #include <arpa/nameser.h>
-#include <ctype.h>
 #include <netinet/in.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "resolv_private.h"
-
 /*
  * Expand compressed domain name 'src' to full domain name.
  * 'msg' is a pointer to the begining of the message,
@@ -89,7 +88,7 @@
     int n = ns_name_uncompress(msg, eom, src, dst, (size_t) dstsiz);
 
     if (n > 0 && dst[0] == '.') dst[0] = '\0';
-    return (n);
+    return n;
 }
 
 /*
@@ -141,7 +140,7 @@
 #define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c))
 #define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
 
-int res_hnok(const char* dn) {
+bool res_hnok(const char* dn) {
     int pch = PERIOD, ch = *dn++;
 
     while (ch != '\0') {
@@ -150,60 +149,25 @@
         if (periodchar(ch)) {
             ;
         } else if (periodchar(pch)) {
-            if (!borderchar(ch)) return (0);
+            if (!borderchar(ch)) return false;
         } else if (periodchar(nch) || nch == '\0') {
-            if (!borderchar(ch)) return (0);
+            if (!borderchar(ch)) return false;
         } else {
-            if (!middlechar(ch)) return (0);
+            if (!middlechar(ch)) return false;
         }
         pch = ch, ch = nch;
     }
-    return (1);
-}
-
-/*
- * hostname-like (A, MX, WKS) owners can have "*" as their first label
- * but must otherwise be as a host name.
- */
-int res_ownok(const char* dn) {
-    if (asterchar(dn[0])) {
-        if (periodchar(dn[1])) return (res_hnok(dn + 2));
-        if (dn[1] == '\0') return (1);
-    }
-    return (res_hnok(dn));
-}
-
-/*
- * SOA RNAMEs and RP RNAMEs can have any printable character in their first
- * label, but the rest of the name has to look like a host name.
- */
-int res_mailok(const char* dn) {
-    int ch, escaped = 0;
-
-    /* "." is a valid missing representation */
-    if (*dn == '\0') return (1);
-
-    /* otherwise <label>.<hostname> */
-    while ((ch = *dn++) != '\0') {
-        if (!domainchar(ch)) return (0);
-        if (!escaped && periodchar(ch)) break;
-        if (escaped)
-            escaped = 0;
-        else if (bslashchar(ch))
-            escaped = 1;
-    }
-    if (periodchar(ch)) return (res_hnok(dn));
-    return (0);
+    return true;
 }
 
 /*
  * This function is quite liberal, since RFC 1034's character sets are only
  * recommendations.
  */
-int res_dnok(const char* dn) {
+bool res_dnok(const char* dn) {
     int ch;
 
     while ((ch = *dn++) != '\0')
-        if (!domainchar(ch)) return (0);
-    return (1);
+        if (!domainchar(ch)) return false;
+    return true;
 }
diff --git a/res_comp.h b/res_comp.h
new file mode 100644
index 0000000..6022838
--- /dev/null
+++ b/res_comp.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+int dn_expand(const uint8_t* msg, const uint8_t* eom, const uint8_t* src, char* dst, int dstsiz);
+int dn_comp(const char* src, uint8_t* dst, int dstsiz, uint8_t** dnptrs, uint8_t** lastdnptr);
+int dn_skipname(const uint8_t* ptr, const uint8_t* eom);
+
+bool res_hnok(const char* dn);
+bool res_dnok(const char* dn);
diff --git a/res_init.cpp b/res_init.cpp
index 12f4124..16fd98c 100644
--- a/res_init.cpp
+++ b/res_init.cpp
@@ -92,24 +92,27 @@
 #include "netd_resolv/resolv.h"
 #include "resolv_private.h"
 
-// Set up Resolver state default settings.
-// Note: this is called with statp zero-initialized
-void res_init(res_state statp) {
-    statp->netid = NETID_UNSET;
+void res_init(ResState* statp, const struct android_net_context* _Nonnull netcontext,
+              android::net::NetworkDnsEventReported* _Nonnull event) {
+    memset(statp, 0, sizeof *statp);
+
+    statp->netid = netcontext->dns_netid;
+    statp->uid = netcontext->uid;
+    statp->pid = netcontext->pid;
     statp->id = arc4random_uniform(65536);
-    statp->_mark = MARK_UNSET;
+    statp->_mark = netcontext->dns_mark;
+    statp->netcontext_flags = netcontext->flags;
+    statp->event = event;
 
     statp->ndots = 1;
     statp->_vcsock = -1;
-    statp->_flags = 0;
-    statp->netcontext_flags = 0;
 
     for (int ns = 0; ns < MAXNS; ns++) {
         statp->nssocks[ns] = -1;
     }
 
     // The following dummy initialization is probably useless because
-    // it's overwritten later by _resolv_populate_res_for_net().
+    // it's overwritten later by resolv_populate_res_for_net().
     // TODO: check if it's safe to remove.
     const sockaddr_union u{
             .sin.sin_addr.s_addr = INADDR_ANY,
@@ -142,14 +145,3 @@
         }
     }
 }
-
-void res_setnetcontext(res_state statp, const struct android_net_context* netcontext,
-                       android::net::NetworkDnsEventReported* _Nonnull event) {
-    if (statp != nullptr) {
-        statp->netid = netcontext->dns_netid;
-        statp->uid = netcontext->uid;
-        statp->_mark = netcontext->dns_mark;
-        statp->netcontext_flags = netcontext->flags;
-        statp->event = event;
-    }
-}
diff --git a/res_init.h b/res_init.h
index af76414..4c759c4 100644
--- a/res_init.h
+++ b/res_init.h
@@ -15,5 +15,8 @@
  */
 #pragma once
 
-struct __res_state;
-void res_init(__res_state* statp);
+#include "resolv_private.h"
+
+// TODO: make this a constructor for ResState
+void res_init(ResState* res, const struct android_net_context* netcontext,
+              android::net::NetworkDnsEventReported* event);
diff --git a/res_mkquery.cpp b/res_mkquery.cpp
index 5dde9be..b95d31f 100644
--- a/res_mkquery.cpp
+++ b/res_mkquery.cpp
@@ -80,8 +80,11 @@
 #include <string.h>
 
 #include <android-base/logging.h>
+#include <netd_resolv/resolv.h>  // NET_CONTEXT_FLAG_USE_DNS_OVER_TLS
 
-#include "resolv_private.h"
+#include "res_comp.h"
+#include "res_debug.h"
+#include "resolv_private.h"  // res_state
 
 // Queries will be padded to a multiple of this length when EDNS0 is active.
 constexpr uint16_t kEdns0Padding = 128;
@@ -93,19 +96,15 @@
         "11",     "12",     "13",      "ZONEINIT", "ZONEREF",
 };
 
-/*
- * Form all types of queries.
- * Returns the size of the result or -1.
- */
-int res_nmkquery(res_state statp, int op,     /* opcode of query */
-                 const char* dname,           /* domain name */
-                 int cl, int type,            /* class and type of query */
-                 const uint8_t* data,         /* resource record data */
-                 int datalen,                 /* length of data */
-                 const uint8_t* /*newrr_in*/, /* new rr for modify or append */
-                 uint8_t* buf,                /* buffer to put query */
-                 int buflen)                  /* size of buffer */
-{
+// Form all types of queries. Returns the size of the result or -1.
+int res_nmkquery(int op,               // opcode of query
+                 const char* dname,    // domain name
+                 int cl, int type,     // class and type of query
+                 const uint8_t* data,  // resource record data
+                 int datalen,          // length of data
+                 uint8_t* buf,         // buffer to put query
+                 int buflen,           // size of buffer
+                 int netcontext_flags) {
     HEADER* hp;
     uint8_t *cp, *ep;
     int n;
@@ -123,7 +122,7 @@
     hp->id = htons(arc4random_uniform(65536));
     hp->opcode = op;
     hp->rd = true;
-    hp->ad = (statp->netcontext_flags & NET_CONTEXT_FLAG_USE_DNS_OVER_TLS) != 0U;
+    hp->ad = (netcontext_flags & NET_CONTEXT_FLAG_USE_DNS_OVER_TLS) != 0U;
     hp->rcode = NOERROR;
     cp = buf + HFIXEDSZ;
     ep = buf + buflen;
diff --git a/res_query.cpp b/res_query.cpp
index f54ff3f..7702b44 100644
--- a/res_query.cpp
+++ b/res_query.cpp
@@ -89,12 +89,6 @@
 #include "resolv_cache.h"
 #include "resolv_private.h"
 
-#if PACKETSZ > 1024
-#define MAXPACKET PACKETSZ
-#else
-#define MAXPACKET 1024
-#endif
-
 /*
  * Formulate a normal query, send, and await answer.
  * Returned answer is placed in supplied buffer "answer".
@@ -123,7 +117,8 @@
 
     LOG(DEBUG) << __func__ << ": (" << cl << ", " << type << ")";
 
-    n = res_nmkquery(statp, QUERY, name, cl, type, NULL, 0, NULL, buf, sizeof(buf));
+    n = res_nmkquery(QUERY, name, cl, type, /*data=*/nullptr, 0, buf, sizeof(buf),
+                     statp->netcontext_flags);
     if (n > 0 &&
         (statp->netcontext_flags &
          (NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS)) &&
@@ -255,7 +250,7 @@
          * be loaded once for the thread instead of each
          * time a query is tried.
          */
-        _resolv_populate_res_for_net(statp);
+        resolv_populate_res_for_net(statp);
 
         for (const auto& domain : statp->search_domains) {
             if (domain == "." || domain == "") ++root_on_list;
diff --git a/res_send.cpp b/res_send.cpp
index 6616ca3..66548bd 100644
--- a/res_send.cpp
+++ b/res_send.cpp
@@ -83,7 +83,6 @@
 
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
-#include <netinet/in.h>
 
 #include <errno.h>
 #include <fcntl.h>
@@ -104,11 +103,15 @@
 #include "DnsTlsTransport.h"
 #include "PrivateDnsConfiguration.h"
 #include "netd_resolv/resolv.h"
-#include "netd_resolv/stats.h"
 #include "private/android_filesystem_config.h"
+
+#include "res_comp.h"
 #include "res_debug.h"
+#include "res_init.h"
 #include "resolv_cache.h"
+#include "stats.h"
 #include "stats.pb.h"
+#include "util.h"
 
 // TODO: use the namespace something like android::netd_resolv for libnetd_resolv
 using android::net::CacheStatus;
@@ -129,12 +132,12 @@
 using android::net::PrivateDnsStatus;
 using android::net::PROTO_TCP;
 using android::net::PROTO_UDP;
+using android::netdutils::IPSockAddr;
 using android::netdutils::Slice;
 using android::netdutils::Stopwatch;
 
 static DnsTlsDispatcher sDnsTlsDispatcher;
 
-static int get_salen(const struct sockaddr*);
 static struct sockaddr* get_nsaddr(res_state, size_t);
 static int send_vc(res_state, res_params* params, const uint8_t*, int, uint8_t*, int, int*, int,
                    time_t*, int*, int*);
@@ -224,15 +227,6 @@
     return tsnow;
 }
 
-static struct iovec evConsIovec(void* buf, size_t cnt) {
-    struct iovec ret;
-
-    memset(&ret, 0xf5, sizeof ret);
-    ret.iov_base = buf;
-    ret.iov_len = cnt;
-    return ret;
-}
-
 // END: Code copied from ISC eventlib
 
 /* BIONIC-BEGIN: implement source port randomization */
@@ -405,24 +399,20 @@
 
 int res_nsend(res_state statp, const uint8_t* buf, int buflen, uint8_t* ans, int anssiz, int* rcode,
               uint32_t flags) {
-    int gotsomewhere, terrno, v_circuit, resplen, n;
-    ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED;
+    LOG(DEBUG) << __func__;
 
+    // Should not happen
     if (anssiz < HFIXEDSZ) {
         // TODO: Remove errno once callers stop using it
         errno = EINVAL;
         return -EINVAL;
     }
-    LOG(DEBUG) << __func__;
     res_pquery(buf, buflen);
 
-    v_circuit = buflen > PACKETSZ;
-    gotsomewhere = 0;
-    terrno = ETIMEDOUT;
-
     int anslen = 0;
     Stopwatch cacheStopwatch;
-    cache_status = resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
+    ResolvCacheStatus cache_status =
+            resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
     const int32_t cacheLatencyUs = saturate_cast<int32_t>(cacheStopwatch.timeTakenUs());
     if (cache_status == RESOLV_CACHE_FOUND) {
         HEADER* hp = (HEADER*)(void*)ans;
@@ -430,11 +420,12 @@
         DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
         dnsQueryEvent->set_latency_micros(cacheLatencyUs);
         dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
+        dnsQueryEvent->set_type(getQueryType(buf, buflen));
         return anslen;
     } else if (cache_status != RESOLV_CACHE_UNSUPPORTED) {
         // had a cache miss for a known network, so populate the thread private
         // data so the normal resolve path can do its thing
-        _resolv_populate_res_for_net(statp);
+        resolv_populate_res_for_net(statp);
     }
     if (statp->nscount == 0) {
         // We have no nameservers configured, so there's no point trying.
@@ -447,6 +438,25 @@
         return -ESRCH;
     }
 
+    // DoT
+    if (!(statp->netcontext_flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS)) {
+        bool fallback = false;
+        int resplen = res_tls_send(statp, Slice(const_cast<uint8_t*>(buf), buflen),
+                                   Slice(ans, anssiz), rcode, &fallback);
+        if (resplen > 0) {
+            LOG(DEBUG) << __func__ << ": got answer from DoT";
+            res_pquery(ans, resplen);
+            if (cache_status == RESOLV_CACHE_NOTFOUND) {
+                resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
+            }
+            return resplen;
+        }
+        if (!fallback) {
+            _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+            return -ETIMEDOUT;
+        }
+    }
+
     res_stats stats[MAXNS];
     res_params params;
     int revision_id = resolv_cache_get_resolver_stats(statp->netid, &params, stats);
@@ -467,129 +477,88 @@
         res_set_usable_server(selectedServer, statp->nscount, usable_servers);
     }
 
-    /*
-     * Send request, RETRY times, or until successful.
-     */
+    // Send request, RETRY times, or until successful.
     int retryTimes = (flags & ANDROID_RESOLV_NO_RETRY) ? 1 : params.retry_count;
+    int useTcp = buflen > PACKETSZ;
+    int gotsomewhere = 0;
+    int terrno = ETIMEDOUT;
 
     for (int attempt = 0; attempt < retryTimes; ++attempt) {
-
-        for (int ns = 0; ns < statp->nscount; ns++) {
+        for (int ns = 0; ns < statp->nscount; ++ns) {
             if (!usable_servers[ns]) continue;
-            int nsaplen;
-            time_t now = 0;
-            int delay = 0;
-            *rcode = RCODE_INTERNAL_ERROR;
-            const sockaddr* nsap = get_nsaddr(statp, ns);
-            nsaplen = get_salen(nsap);
 
-        same_ns:
-            // TODO: Since we expect there is only one DNS server being queried here while this
-            // function tries to query all of private DNS servers. Consider moving it to other
-            // reasonable place. In addition, maybe add stats for private DNS.
-            if (!(statp->netcontext_flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS)) {
-                bool fallback = false;
-                resplen = res_tls_send(statp, Slice(const_cast<uint8_t*>(buf), buflen),
-                                       Slice(ans, anssiz), rcode, &fallback);
-                if (resplen > 0) {
-                    LOG(DEBUG) << __func__ << ": got answer from DoT";
-                    res_pquery(ans, resplen);
-                    if (cache_status == RESOLV_CACHE_NOTFOUND) {
-                        resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
-                    }
-                    return resplen;
-                }
-                if (!fallback) {
-                    _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
-                    res_nclose(statp);
-                    return -terrno;
-                }
-            }
+            *rcode = RCODE_INTERNAL_ERROR;
+
+            // Get server addr
+            const sockaddr* nsap = get_nsaddr(statp, ns);
+            const int nsaplen = sockaddrSize(nsap);
 
             static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
             char abuf[NI_MAXHOST];
-            DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
-            dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
-
             if (getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), NULL, 0, niflags) == 0)
                 LOG(DEBUG) << __func__ << ": Querying server (# " << ns + 1
                            << ") address = " << abuf;
 
+            ::android::net::Protocol query_proto = useTcp ? PROTO_TCP : PROTO_UDP;
+            time_t now = 0;
+            int delay = 0;
+            bool fallbackTCP = false;
+            const bool shouldRecordStats = (attempt == 0);
+            int resplen;
             Stopwatch queryStopwatch;
-            if (v_circuit) {
-                /* Use VC; at most one attempt per server. */
-                bool shouldRecordStats = (attempt == 0);
+            if (useTcp) {
+                // TCP; at most one attempt per server.
                 attempt = retryTimes;
+                resplen = send_vc(statp, &params, buf, buflen, ans, anssiz, &terrno, ns, &now,
+                                  rcode, &delay);
 
-                n = send_vc(statp, &params, buf, buflen, ans, anssiz, &terrno, ns, &now, rcode,
-                            &delay);
-
-                dnsQueryEvent->set_latency_micros(
-                        saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
-                dnsQueryEvent->set_dns_server_index(ns);
-                dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
-                dnsQueryEvent->set_retry_times(attempt);
-                dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
-                dnsQueryEvent->set_protocol(PROTO_TCP);
-                dnsQueryEvent->set_type(getQueryType(buf, buflen));
-
-                /*
-                 * Only record stats the first time we try a query. This ensures that
-                 * queries that deterministically fail (e.g., a name that always returns
-                 * SERVFAIL or times out) do not unduly affect the stats.
-                 */
-                if (shouldRecordStats) {
-                    res_sample sample;
-                    _res_stats_set_sample(&sample, now, *rcode, delay);
-                    _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns, &sample,
-                                                            params.max_samples);
+                if (buflen <= PACKETSZ && resplen <= 0 &&
+                    statp->tc_mode == aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP) {
+                    // reset to UDP for next query on next DNS server if resolver is currently doing
+                    // TCP fallback retry and current server does not support TCP connectin
+                    useTcp = false;
                 }
-
-                LOG(INFO) << __func__ << ": used send_vc " << n;
-
-                if (n < 0) {
-                    _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
-                    res_nclose(statp);
-                    return -terrno;
-                };
-                if (n == 0) goto next_ns;
-                resplen = n;
+                LOG(INFO) << __func__ << ": used send_vc " << resplen;
             } else {
-                /* Use datagrams. */
-                LOG(INFO) << __func__ << ": using send_dg";
-
-                n = send_dg(statp, &params, buf, buflen, ans, anssiz, &terrno, ns, &v_circuit,
-                            &gotsomewhere, &now, rcode, &delay);
-
-                dnsQueryEvent->set_latency_micros(
-                        saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
-                dnsQueryEvent->set_dns_server_index(ns);
-                dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
-                dnsQueryEvent->set_retry_times(attempt);
-                dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
-                dnsQueryEvent->set_protocol(PROTO_UDP);
-                dnsQueryEvent->set_type(getQueryType(buf, buflen));
-
-                /* Only record stats the first time we try a query. See above. */
-                if (attempt == 0) {
-                    res_sample sample;
-                    _res_stats_set_sample(&sample, now, *rcode, delay);
-                    _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns, &sample,
-                                                            params.max_samples);
-                }
-
-                LOG(INFO) << __func__ << ": used send_dg " << n;
-
-                if (n < 0) {
-                    _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
-                    res_nclose(statp);
-                    return -terrno;
-                };
-                if (n == 0) goto next_ns;
-                if (v_circuit) goto same_ns;
-                resplen = n;
+                // UDP
+                resplen = send_dg(statp, &params, buf, buflen, ans, anssiz, &terrno, ns, &useTcp,
+                                  &gotsomewhere, &now, rcode, &delay);
+                fallbackTCP = useTcp ? true : false;
+                LOG(INFO) << __func__ << ": used send_dg " << resplen;
             }
 
+            DnsQueryEvent* dnsQueryEvent = addDnsQueryEvent(statp->event);
+            dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(cache_status));
+            dnsQueryEvent->set_latency_micros(saturate_cast<int32_t>(queryStopwatch.timeTakenUs()));
+            dnsQueryEvent->set_dns_server_index(ns);
+            dnsQueryEvent->set_ip_version(ipFamilyToIPVersion(nsap->sa_family));
+            dnsQueryEvent->set_retry_times(attempt);
+            dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
+            dnsQueryEvent->set_protocol(query_proto);
+            dnsQueryEvent->set_type(getQueryType(buf, buflen));
+
+            // Only record stats the first time we try a query. This ensures that
+            // queries that deterministically fail (e.g., a name that always returns
+            // SERVFAIL or times out) do not unduly affect the stats.
+            if (shouldRecordStats) {
+                res_sample sample;
+                _res_stats_set_sample(&sample, now, *rcode, delay);
+                resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, nsap, sample,
+                                                       params.max_samples);
+                resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(*nsap), dnsQueryEvent);
+            }
+
+            if (resplen == 0) continue;
+            if (fallbackTCP) {
+                ns--;
+                continue;
+            }
+            if (resplen < 0) {
+                _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
+                res_nclose(statp);
+                return -terrno;
+            };
+
             LOG(DEBUG) << __func__ << ": got answer:";
             res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
 
@@ -598,38 +567,21 @@
             }
             res_nclose(statp);
             return (resplen);
-        next_ns:;
         }  // for each ns
     }  // for each retry
     res_nclose(statp);
-    if (!v_circuit) {
-        if (!gotsomewhere) {
-            // TODO: Remove errno once callers stop using it
-            errno = ECONNREFUSED; /* no nameservers found */
-            terrno = ECONNREFUSED;
-        } else {
-            // TODO: Remove errno once callers stop using it
-            errno = ETIMEDOUT; /* no answer obtained */
-            terrno = ETIMEDOUT;
-        }
-    } else {
-        errno = terrno;
-    }
+    terrno = useTcp ? terrno : gotsomewhere ? ETIMEDOUT : ECONNREFUSED;
+    // TODO: Remove errno once callers stop using it
+    errno = useTcp ? terrno
+                   : gotsomewhere ? ETIMEDOUT /* no answer obtained */
+                                  : ECONNREFUSED /* no nameservers found */;
+
     _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
     return -terrno;
 }
 
 /* Private */
 
-static int get_salen(const struct sockaddr* sa) {
-    if (sa->sa_family == AF_INET)
-        return (sizeof(struct sockaddr_in));
-    else if (sa->sa_family == AF_INET6)
-        return (sizeof(struct sockaddr_in6));
-    else
-        return (0); /* unknown, die on connect */
-}
-
 static struct sockaddr* get_nsaddr(res_state statp, size_t n) {
     return (struct sockaddr*)(void*)&statp->nsaddrs[n];
 }
@@ -665,13 +617,12 @@
     struct sockaddr* nsap;
     int nsaplen;
     int truncating, connreset, n;
-    struct iovec iov[2];
     uint8_t* cp;
 
     LOG(INFO) << __func__ << ": using send_vc";
 
     nsap = get_nsaddr(statp, (size_t) ns);
-    nsaplen = get_salen(nsap);
+    nsaplen = sockaddrSize(nsap);
 
     connreset = 0;
 same_ns:
@@ -711,7 +662,7 @@
                     return -1;
             }
         }
-        resolv_tag_socket(statp->_vcsock, statp->uid);
+        resolv_tag_socket(statp->_vcsock, statp->uid, statp->pid);
         if (statp->_mark != MARK_UNSET) {
             if (setsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &statp->_mark,
                            sizeof(statp->_mark)) < 0) {
@@ -750,8 +701,10 @@
      * Send length & message
      */
     uint16_t len = htons(static_cast<uint16_t>(buflen));
-    iov[0] = evConsIovec(&len, INT16SZ);
-    iov[1] = evConsIovec((void*) buf, (size_t) buflen);
+    const iovec iov[] = {
+            {.iov_base = &len, .iov_len = INT16SZ},
+            {.iov_base = const_cast<uint8_t*>(buf), .iov_len = static_cast<size_t>(buflen)},
+    };
     if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
         *terrno = errno;
         PLOG(DEBUG) << __func__ << ": write failed: ";
@@ -859,8 +812,8 @@
 }
 
 /* return -1 on error (errno set), 0 on success */
-static int connect_with_timeout(int sock, const struct sockaddr* nsap, socklen_t salen,
-                                const struct timespec timeout) {
+static int connect_with_timeout(int sock, const sockaddr* nsap, socklen_t salen,
+                                const timespec timeout) {
     int res, origflags;
 
     origflags = fcntl(sock, F_GETFL, 0);
@@ -872,8 +825,8 @@
         goto done;
     }
     if (res != 0) {
-        struct timespec now = evNowTime();
-        struct timespec finish = evAddTime(now, timeout);
+        timespec now = evNowTime();
+        timespec finish = evAddTime(now, timeout);
         LOG(INFO) << __func__ << ": " << sock << " send_vc";
         res = retrying_poll(sock, POLLIN | POLLOUT, &finish);
         if (res <= 0) {
@@ -937,7 +890,7 @@
     int resplen, n, s;
 
     nsap = get_nsaddr(statp, (size_t) ns);
-    nsaplen = get_salen(nsap);
+    nsaplen = sockaddrSize(nsap);
     if (statp->nssocks[ns] == -1) {
         statp->nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (statp->nssocks[ns] < 0) {
@@ -954,7 +907,7 @@
             }
         }
 
-        resolv_tag_socket(statp->nssocks[ns], statp->uid);
+        resolv_tag_socket(statp->nssocks[ns], statp->uid, statp->pid);
         if (statp->_mark != MARK_UNSET) {
             if (setsockopt(statp->nssocks[ns], SOL_SOCKET, SO_MARK, &(statp->_mark),
                            sizeof(statp->_mark)) < 0) {
@@ -1098,6 +1051,7 @@
     char hbuf[NI_MAXHOST];
     char sbuf[NI_MAXSERV];
     constexpr int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+    const int err = errno;
 
     if (!WOULD_LOG(DEBUG)) return;
 
@@ -1107,6 +1061,7 @@
         strncpy(sbuf, "?", sizeof(sbuf) - 1);
         sbuf[sizeof(sbuf) - 1] = '\0';
     }
+    errno = err;
     PLOG(DEBUG) << __func__ << ": " << str << " ([" << hbuf << "]." << sbuf << "): ";
 }
 
@@ -1141,8 +1096,9 @@
             return PrivateDnsModes::PDM_OPPORTUNISTIC;
         case PrivateDnsMode::STRICT:
             return PrivateDnsModes::PDM_STRICT;
+        default:
+            return PrivateDnsModes::PDM_UNKNOWN;
     }
-    return PrivateDnsModes::PDM_UNKNOWN;
 }
 
 static int res_tls_send(res_state statp, const Slice query, const Slice answer, int* rcode,
@@ -1237,9 +1193,9 @@
                      uint8_t* ans, int ansLen, int* rcode, uint32_t flags,
                      NetworkDnsEventReported* event) {
     assert(event != nullptr);
-    res_state res = res_get_state();
-    res_setnetcontext(res, netContext, event);
-    _resolv_populate_res_for_net(res);
+    ResState res;
+    res_init(&res, netContext, event);
+    resolv_populate_res_for_net(&res);
     *rcode = NOERROR;
-    return res_nsend(res, msg, msgLen, ans, ansLen, rcode, flags);
+    return res_nsend(&res, msg, msgLen, ans, ansLen, rcode, flags);
 }
diff --git a/res_state.cpp b/res_state.cpp
deleted file mode 100644
index 7388082..0000000
--- a/res_state.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#define LOG_TAG "resolv"
-
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <netdb.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h> /* for gettid() */
-
-#include <android-base/logging.h>
-
-#include "res_init.h"
-#include "resolv_cache.h"
-#include "resolv_private.h"
-
-typedef struct {
-    // TODO: Have one __res_state per network so we don't have to repopulate frequently.
-    struct __res_state _nres[1];
-} _res_thread;
-
-static _res_thread* res_thread_alloc(void) {
-    _res_thread* rt = (_res_thread*) calloc(1, sizeof(*rt));
-    return rt;
-}
-
-static void res_thread_free(void* _rt) {
-    _res_thread* rt = (_res_thread*) _rt;
-
-    LOG(VERBOSE) << __func__ << ": rt=" << rt << " for thread=" << gettid();
-
-    res_nclose(rt->_nres);
-    free(rt);
-}
-
-static pthread_key_t _res_key;
-
-__attribute__((constructor)) static void __res_key_init() {
-    pthread_key_create(&_res_key, res_thread_free);
-}
-
-static _res_thread* res_thread_get(void) {
-    _res_thread* rt = (_res_thread*) pthread_getspecific(_res_key);
-    if (rt != NULL) {
-        return rt;
-    }
-
-    /* It is the first time this function is called in this thread,
-     * we need to create a new thread-specific DNS resolver state. */
-    rt = res_thread_alloc();
-    if (rt == NULL) {
-        return NULL;
-    }
-    pthread_setspecific(_res_key, rt);
-
-    LOG(VERBOSE) << __func__ << ": tid=" << gettid() << ", rt=" << rt;
-    res_init(rt->_nres);
-    return rt;
-}
-
-struct __res_state _nres;
-
-res_state res_get_state(void) {
-    _res_thread* rt = res_thread_get();
-
-    return rt ? rt->_nres : NULL;
-}
diff --git a/res_stats.cpp b/res_stats.cpp
index 5faf7ca..f3b79bb 100644
--- a/res_stats.cpp
+++ b/res_stats.cpp
@@ -21,8 +21,7 @@
 
 #include <android-base/logging.h>
 
-#include "netd_resolv/stats.h"
-
+#include "stats.h"
 
 // Calculate the round-trip-time from start time t0 and end time t1.
 int _res_stats_calculate_rtt(const timespec* t1, const timespec* t0) {
@@ -119,11 +118,11 @@
     android_net_res_stats_aggregate(stats, &successes, &errors, &timeouts, &internal_errors,
                                     &rtt_avg, &last_sample_time);
     if (successes >= 0 && errors >= 0 && timeouts >= 0) {
-        int total = successes + errors + timeouts;
+        int total = successes + errors + timeouts + internal_errors;
         LOG(INFO) << __func__ << ": NS stats: S " << successes << " + E " << errors << " + T "
                   << timeouts << " + I " << internal_errors << " = " << total
                   << ", rtt = " << rtt_avg << ", min_samples = " << unsigned(params->min_samples);
-        if (total >= params->min_samples && (errors > 0 || timeouts > 0)) {
+        if (total >= params->min_samples) {
             int success_rate = successes * 100 / total;
             LOG(INFO) << __func__ << ": success rate " << success_rate;
             if (success_rate < params->success_threshold) {
diff --git a/resolv_cache.h b/resolv_cache.h
index 9a9787e..d34ae95 100644
--- a/resolv_cache.h
+++ b/resolv_cache.h
@@ -28,22 +28,27 @@
 
 #pragma once
 
-#include "netd_resolv/resolv.h"
-
-#include <stddef.h>
-
 #include <unordered_map>
 #include <vector>
 
-struct __res_state;
-struct resolv_cache;
+#include <aidl/android/net/IDnsResolver.h>
+#include <aidl/android/net/ResolverExperimentalOptionsParcel.h>
 
-constexpr int DNSEVENT_SUBSAMPLING_MAP_DEFAULT_KEY = -1;
+#include <netdutils/DumpWriter.h>
+#include <netdutils/InternetAddresses.h>
+#include <stats.pb.h>
 
-/* sets the name server addresses to the provided res_state structure. The
- * name servers are retrieved from the cache which is associated
- * with the network to which the res_state structure is associated */
-void _resolv_populate_res_for_net(struct __res_state* statp);
+#include "ResolverStats.h"
+#include "params.h"
+
+// Sets the name server addresses to the provided ResState.
+// The name servers are retrieved from the cache which is associated
+// with the network to which ResState is associated.
+struct ResState;
+
+typedef std::multimap<std::string /* hostname */, std::string /* IPv4/IPv6 address */> HostMapping;
+
+void resolv_populate_res_for_net(ResState* statp);
 
 std::vector<unsigned> resolv_list_caches();
 
@@ -69,9 +74,16 @@
 /* Notify the cache a request failed */
 void _resolv_cache_query_failed(unsigned netid, const void* query, int querylen, uint32_t flags);
 
+// Get a customized table for a given network.
+std::vector<std::string> getCustomizedTableByName(const size_t netid, const char* hostname);
+
 // Sets name servers for a given network.
-int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers,
-                           const std::vector<std::string>& domains, const res_params& params);
+// TODO: Pass all of ResolverParamsParcel and remove the res_params argument.
+int resolv_set_nameservers(
+        unsigned netid, const std::vector<std::string>& servers,
+        const std::vector<std::string>& domains, const res_params& params,
+        const aidl::android::net::ResolverExperimentalOptionsParcel& experimentalOptions = {
+                {} /* hosts */, aidl::android::net::IDnsResolver::TC_MODE_DEFAULT});
 
 // Creates the cache associated with the given network.
 int resolv_create_cache_for_net(unsigned netid);
@@ -79,6 +91,9 @@
 // Deletes the cache associated with the given network.
 void resolv_delete_cache_for_net(unsigned netid);
 
+// Flushes the cache associated with the given network.
+int resolv_flush_cache_for_net(unsigned netid);
+
 // For test only.
 // Return true if the cache is existent in the given network, false otherwise.
 bool has_named_cache(unsigned netid);
@@ -87,3 +102,16 @@
 // Get the expiration time of a cache entry. Return 0 on success; otherwise, an negative error is
 // returned if the expiration time can't be acquired.
 int resolv_cache_get_expiration(unsigned netid, const std::vector<char>& query, time_t* expiration);
+
+// Set private DNS servers to DnsStats for a given network.
+int resolv_stats_set_servers_for_dot(unsigned netid, const std::vector<std::string>& servers);
+
+// Add a statistics record to DnsStats for a given network.
+bool resolv_stats_add(unsigned netid, const android::netdutils::IPSockAddr& server,
+                      const android::net::DnsQueryEvent* record);
+
+void resolv_stats_dump(android::netdutils::DumpWriter& dw, unsigned netid);
+
+void resolv_oem_options_dump(android::netdutils::DumpWriter& dw, unsigned netid);
+
+const char* tc_mode_to_str(const int mode);
diff --git a/resolv_cache_unit_test.cpp b/resolv_cache_unit_test.cpp
index f62f317..03f8806 100644
--- a/resolv_cache_unit_test.cpp
+++ b/resolv_cache_unit_test.cpp
@@ -25,24 +25,26 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android/multinetwork.h>
+#include <arpa/inet.h>
 #include <cutils/properties.h>
 #include <gmock/gmock-matchers.h>
 #include <gtest/gtest.h>
 
-#include "netd_resolv/stats.h"
+#include "res_init.h"
 #include "resolv_cache.h"
 #include "resolv_private.h"
+#include "stats.h"
 #include "tests/dns_responder/dns_responder.h"
 
 using namespace std::chrono_literals;
 
 constexpr int TEST_NETID = 30;
 constexpr int TEST_NETID_2 = 31;
+constexpr int DNS_PORT = 53;
 
 // Constant values sync'd from res_cache.cpp
 constexpr int DNS_HEADER_SIZE = 12;
 constexpr int MAX_ENTRIES = 64 * 2 * 5;
-constexpr int MAXPACKET = 8 * 1024;
 
 namespace {
 
@@ -64,9 +66,10 @@
 };
 
 std::vector<char> makeQuery(int op, const char* qname, int qclass, int qtype) {
-    res_state res = res_get_state();
     uint8_t buf[MAXPACKET] = {};
-    const int len = res_nmkquery(res, op, qname, qclass, qtype, NULL, 0, NULL, buf, sizeof(buf));
+    const int len = res_nmkquery(op, qname, qclass, qtype, /*data=*/nullptr, /*datalen=*/0, buf,
+                                 sizeof(buf),
+                                 /*netcontext_flags=*/0);
     return std::vector<char>(buf, buf + len);
 }
 
@@ -83,7 +86,7 @@
                 .rclass = question.qclass,
                 .ttl = ttl,
         };
-        test::DNSResponder::fillAnswerRdata(rdata_str, record);
+        test::DNSResponder::fillRdata(rdata_str, record);
         header.answers.push_back(std::move(record));
     }
 
@@ -97,13 +100,6 @@
     return std::time(nullptr);
 }
 
-std::string addrToString(const sockaddr_storage* addr) {
-    char out[INET6_ADDRSTRLEN] = {0};
-    getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
-                NI_NUMERICHOST);
-    return std::string(out);
-}
-
 // Comparison for res_stats. Simply check the count in the cache test.
 bool operator==(const res_stats& a, const res_stats& b) {
     return std::tie(a.sample_count, a.sample_next) == std::tie(b.sample_count, b.sample_next);
@@ -194,6 +190,13 @@
         return resolv_set_nameservers(netId, setup.servers, setup.domains, setup.params);
     }
 
+    void cacheAddStats(uint32_t netId, int revision_id, const sockaddr* sa,
+                       const res_sample& sample, int max_samples) {
+        resolv_cache_add_resolver_stats_sample(netId, revision_id, sa, sample, max_samples);
+    }
+
+    int cacheFlush(uint32_t netId) { return resolv_flush_cache_for_net(netId); }
+
     void expectCacheStats(const std::string& msg, uint32_t netId, const CacheStats& expected) {
         int nscount = -1;
         sockaddr_storage servers[MAXNS];
@@ -221,6 +224,11 @@
         EXPECT_TRUE(params == expected.setup.params) << msg;
 
         // res_stats checking.
+        if (expected.stats.size() == 0) {
+            for (int ns = 0; ns < nscount; ns++) {
+                EXPECT_EQ(0U, stats[ns].sample_count) << msg;
+            }
+        }
         for (size_t i = 0; i < expected.stats.size(); i++) {
             EXPECT_TRUE(stats[i] == expected.stats[i]) << msg;
         }
@@ -714,6 +722,117 @@
     expectCacheStats("GetStats", TEST_NETID, cacheStats);
 }
 
+TEST_F(ResolvCacheTest, FlushCache) {
+    EXPECT_EQ(0, cacheCreate(TEST_NETID));
+    const SetupParams setup = {
+            .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"},
+            .domains = {"domain1.com", "domain2.com"},
+            .params = kParams,
+    };
+    EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, setup));
+    EXPECT_TRUE(resolv_has_nameservers(TEST_NETID));
+
+    res_sample sample = {.at = time(NULL), .rtt = 100, .rcode = ns_r_noerror};
+    sockaddr_in sin = {.sin_family = AF_INET, .sin_port = htons(DNS_PORT)};
+    ASSERT_TRUE(inet_pton(AF_INET, setup.servers[0].c_str(), &sin.sin_addr));
+    cacheAddStats(TEST_NETID, 1 /*revision_id*/, (const sockaddr*)&sin, sample,
+                  setup.params.max_samples);
+
+    const CacheStats cacheStats = {
+            .setup = setup,
+            .stats = {{{sample}, 1 /*sample_count*/, 1 /*sample_next*/}},
+            .pendingReqTimeoutCount = 0,
+    };
+    expectCacheStats("FlushCache: a record in cache stats", TEST_NETID, cacheStats);
+
+    EXPECT_EQ(0, cacheFlush(TEST_NETID));
+    const CacheStats cacheStats_empty = {
+            .setup = setup,
+            .stats = {},
+            .pendingReqTimeoutCount = 0,
+    };
+    expectCacheStats("FlushCache: no record in cache stats", TEST_NETID, cacheStats_empty);
+}
+
+TEST_F(ResolvCacheTest, GetHostByAddrFromCache_InvalidArgs) {
+    char domain_name[NS_MAXDNAME] = {};
+    const char query_v4[] = "1.2.3.5";
+
+    // invalid buffer size
+    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME + 1, nullptr,
+                                                 AF_INET));
+    EXPECT_STREQ("", domain_name);
+
+    // invalid query
+    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, nullptr,
+                                                 AF_INET));
+    EXPECT_STREQ("", domain_name);
+
+    // unsupported AF
+    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+                                                 AF_UNSPEC));
+    EXPECT_STREQ("", domain_name);
+}
+
+TEST_F(ResolvCacheTest, GetHostByAddrFromCache) {
+    char domain_name[NS_MAXDNAME] = {};
+    const char query_v4[] = "1.2.3.5";
+    const char query_v6[] = "2001:db8::102:304";
+    const char query_v6_unabbreviated[] = "2001:0db8:0000:0000:0000:0000:0102:0304";
+    const char query_v6_mixed[] = "2001:db8::1.2.3.4";
+    const char answer[] = "existent.in.cache";
+
+    // cache does not exist
+    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+                                                 AF_INET));
+    EXPECT_STREQ("", domain_name);
+
+    // cache is empty
+    EXPECT_EQ(0, cacheCreate(TEST_NETID));
+    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+                                                 AF_INET));
+    EXPECT_STREQ("", domain_name);
+
+    // no v4 match in cache
+    CacheEntry ce = makeCacheEntry(QUERY, "any.data", ns_c_in, ns_t_a, "1.2.3.4");
+    EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
+    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+                                                 AF_INET));
+    EXPECT_STREQ("", domain_name);
+
+    // v4 match
+    ce = makeCacheEntry(QUERY, answer, ns_c_in, ns_t_a, query_v4);
+    EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
+    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
+                                                AF_INET));
+    EXPECT_STREQ(answer, domain_name);
+
+    // no v6 match in cache
+    memset(domain_name, 0, NS_MAXDNAME);
+    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v6,
+                                                 AF_INET6));
+    EXPECT_STREQ("", domain_name);
+
+    // v6 match
+    ce = makeCacheEntry(QUERY, answer, ns_c_in, ns_t_aaaa, query_v6);
+    EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
+    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v6,
+                                                AF_INET6));
+    EXPECT_STREQ(answer, domain_name);
+
+    // v6 match with unabbreviated address format
+    memset(domain_name, 0, NS_MAXDNAME);
+    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME,
+                                                query_v6_unabbreviated, AF_INET6));
+    EXPECT_STREQ(answer, domain_name);
+
+    // v6 with mixed address format
+    memset(domain_name, 0, NS_MAXDNAME);
+    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME,
+                                                query_v6_mixed, AF_INET6));
+    EXPECT_STREQ(answer, domain_name);
+}
+
 namespace {
 
 constexpr int EAI_OK = 0;
@@ -783,7 +902,7 @@
     }
 }
 
-// TODO: Tests for struct resolv_cache_info, including:
+// TODO: Tests for NetConfig, including:
 //     - res_params
 //         -- resolv_cache_get_resolver_stats()
 //     - res_stats
diff --git a/resolv_callback_unit_test.cpp b/resolv_callback_unit_test.cpp
new file mode 100644
index 0000000..6de8f45
--- /dev/null
+++ b/resolv_callback_unit_test.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "resolv_callback_unit_test"
+
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
+
+#include "DnsResolver.h"
+#include "getaddrinfo.h"
+#include "resolv_cache.h"
+#include "tests/resolv_test_utils.h"
+
+namespace android::net {
+
+using android::net::NetworkDnsEventReported;
+using android::netdutils::ScopedAddrinfo;
+
+// Use maximum reserved appId for applications to avoid conflict with existing uids.
+const int TEST_UID = 99999;
+// Use testUid to make sure TagSocketCallback is called.
+static int testUid = 0;
+
+bool isApiLevelSupported(size_t requiredVersion) {
+    if (android::base::GetUintProperty<uint64_t>("ro.build.version.sdk", 0) >= requiredVersion)
+        return true;
+    else
+        return false;
+}
+
+#define SKIP_IF_APILEVEL_LESS_THAN(version)                                        \
+    do {                                                                           \
+        if (!isApiLevelSupported(version)) {                                       \
+            GTEST_LOG_(INFO) << "Skip. Required API version: " << version << "\n"; \
+            return;                                                                \
+        }                                                                          \
+    } while (0)
+
+void getNetworkContextCallback(uint32_t, uint32_t, android_net_context*) {
+    // No-op
+}
+
+bool checkCallingPermissionCallback(const char*) {
+    // No-op
+    return true;
+}
+
+void logCallback(const char*) {
+    // No-op
+}
+
+int tagSocketCallback(int, uint32_t, uid_t uid, pid_t) {
+    testUid = uid;
+    return true;
+}
+
+bool evaluateDomainNameCallback(const android_net_context&, const char*) {
+    // No-op
+    return true;
+}
+
+void initDnsResolverCallbacks() {
+    ResolverNetdCallbacks callbacks = {
+            .check_calling_permission = &checkCallingPermissionCallback,
+            .get_network_context = &getNetworkContextCallback,
+            .log = &logCallback,
+            .tagSocket = &tagSocketCallback,
+            .evaluate_domain_name = &evaluateDomainNameCallback,
+    };
+    // It returns fail since socket 'dnsproxyd' has been occupied.
+    // But the callback funtions is configured successfully and can
+    // be tested when running unit test cases.
+    resolv_init(&callbacks);
+}
+
+void resetDnsResolverCallbacks() {
+    ResolverNetdCallbacks callbacks = {
+            .check_calling_permission = nullptr,
+            .get_network_context = nullptr,
+            .log = nullptr,
+            .tagSocket = nullptr,
+            .evaluate_domain_name = nullptr,
+    };
+    resolv_init(&callbacks);
+}
+
+void resetCallbackParams() {
+    testUid = 0;
+}
+
+class CallbackTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        initDnsResolverCallbacks();
+        // Create cache for test
+        android::net::gDnsResolv->resolverCtrl.createNetworkCache(TEST_NETID);
+    }
+
+    void TearDown() override {
+        // Reset related parameters and callback functions.
+        resetCallbackParams();
+        resetDnsResolverCallbacks();
+        // Delete cache for test
+        android::net::gDnsResolv->resolverCtrl.destroyNetworkCache(TEST_NETID);
+    }
+
+    int SetResolvers() {
+        const std::vector<std::string> servers = {test::kDefaultListenAddr};
+        const std::vector<std::string> domains = {"example.com"};
+        const res_params params = {
+                .sample_validity = 300,
+                .success_threshold = 25,
+                .min_samples = 8,
+                .max_samples = 8,
+                .base_timeout_msec = 1000,
+                .retry_count = 2,
+        };
+        return resolv_set_nameservers(TEST_NETID, servers, domains, params);
+    }
+
+    const android_net_context mNetcontext = {
+            .app_netid = TEST_NETID,
+            .app_mark = MARK_UNSET,
+            .dns_netid = TEST_NETID,
+            .dns_mark = MARK_UNSET,
+            .uid = TEST_UID,
+    };
+};
+
+TEST_F(CallbackTest, tagSocketCallback) {
+    // tagSocketCallback is used when ro.build.version.sdk >=30.
+    // If ro.build.version.sdk < 30, don't run this test case.
+    SKIP_IF_APILEVEL_LESS_THAN(30);
+    test::DNSResponder dns;
+    dns.addMapping(kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4);
+    ASSERT_TRUE(dns.startServer());
+    EXPECT_EQ(SetResolvers(), 0);
+
+    addrinfo* result = nullptr;
+    const addrinfo hints = {.ai_family = AF_INET};
+    NetworkDnsEventReported event;
+    // tagSocketCallback will be called.
+    const int rv = resolv_getaddrinfo("hello", nullptr, &hints, &mNetcontext, &result, &event);
+    ScopedAddrinfo result_cleanup(result);
+    EXPECT_EQ(testUid, TEST_UID);
+    EXPECT_EQ(rv, 0);
+}
+}  // end of namespace android::net
diff --git a/resolv_private.h b/resolv_private.h
index 4c881c2..a062c1a 100644
--- a/resolv_private.h
+++ b/resolv_private.h
@@ -46,25 +46,18 @@
  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/*
- *	@(#)resolv.h	8.1 (Berkeley) 6/2/93
- *	Id: resolv.h,v 1.7.2.11.4.2 2004/06/25 00:41:05 marka Exp
- */
-
-#ifndef NETD_RESOLV_PRIVATE_H
-#define NETD_RESOLV_PRIVATE_H
+#pragma once
 
 #include <android-base/logging.h>
 #include <net/if.h>
-#include <resolv.h>
 #include <time.h>
 #include <string>
 #include <vector>
 
 #include "DnsResolver.h"
-#include "netd_resolv/params.h"
 #include "netd_resolv/resolv.h"
-#include "netd_resolv/stats.h"
+#include "params.h"
+#include "stats.h"
 #include "stats.pb.h"
 
 // Linux defines MAXHOSTNAMELEN as 64, while the domain name limit in
@@ -86,10 +79,12 @@
     struct sockaddr_in sin;
     struct sockaddr_in6 sin6;
 };
+constexpr int MAXPACKET = 8 * 1024;
 
-struct __res_state {
+struct ResState {
     unsigned netid;                           // NetId: cache key and socket mark
     uid_t uid;                                // uid of the app that sent the DNS lookup
+    pid_t pid;                                // pid of the app that sent the DNS lookup
     int nscount;                              // number of name srvers
     uint16_t id;                              // current message id
     std::vector<std::string> search_domains;  // domains to search
@@ -101,9 +96,11 @@
     uint32_t _flags;                          // See RES_F_* defines below
     android::net::NetworkDnsEventReported* event;
     uint32_t netcontext_flags;
+    int tc_mode;
 };
 
-typedef struct __res_state* res_state;
+// TODO: remove these legacy aliases
+typedef ResState* res_state;
 
 /* Retrieve a local copy of the stats for the given netid. The buffer must have space for
  * MAXNS __resolver_stats. Returns the revision id of the resolvers used.
@@ -113,8 +110,8 @@
 /* Add a sample to the shared struct for the given netid and server, provided that the
  * revision_id of the stored servers has not changed.
  */
-void _resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, int ns,
-                                             const res_sample* sample, int max_samples);
+void resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id, const sockaddr* sa,
+                                            const res_sample& sample, int max_samples);
 
 // Calculate the round-trip-time from start time t0 and end time t1.
 int _res_stats_calculate_rtt(const timespec* t1, const timespec* t0);
@@ -140,33 +137,18 @@
 
 extern const char* const _res_opcodes[];
 
-/* Things involving an internal (static) resolver context. */
-struct __res_state* res_get_state(void);
-
-int res_hnok(const char*);
-int res_ownok(const char*);
-int res_mailok(const char*);
-int res_dnok(const char*);
-int dn_skipname(const uint8_t*, const uint8_t*);
-void putlong(uint32_t, uint8_t*);
-void putshort(uint16_t, uint8_t*);
-
 int res_nameinquery(const char*, int, int, const uint8_t*, const uint8_t*);
 int res_queriesmatch(const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*);
 
 int res_nquery(res_state, const char*, int, int, uint8_t*, int, int*);
 int res_nsearch(res_state, const char*, int, int, uint8_t*, int, int*);
 int res_nquerydomain(res_state, const char*, const char*, int, int, uint8_t*, int, int*);
-int res_nmkquery(res_state, int, const char*, int, int, const uint8_t*, int, const uint8_t*,
-                 uint8_t*, int);
+int res_nmkquery(int op, const char* qname, int cl, int type, const uint8_t* data, int datalen,
+                 uint8_t* buf, int buflen, int netcontext_flags);
 int res_nsend(res_state, const uint8_t*, int, uint8_t*, int, int*, uint32_t);
 void res_nclose(res_state);
 int res_nopt(res_state, int, uint8_t*, int, int);
 
-struct android_net_context;
-void res_setnetcontext(res_state, const struct android_net_context*,
-                       android::net::NetworkDnsEventReported* event);
-
 int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hints,
                         addrinfo** result);
 
@@ -188,9 +170,9 @@
 
 android::net::IpVersion ipFamilyToIPVersion(int ipFamily);
 
-inline void resolv_tag_socket(int sock, uid_t uid) {
+inline void resolv_tag_socket(int sock, uid_t uid, pid_t pid) {
     if (android::net::gResNetdCallbacks.tagSocket != nullptr) {
-        if (int err = android::net::gResNetdCallbacks.tagSocket(sock, TAG_SYSTEM_DNS, uid)) {
+        if (int err = android::net::gResNetdCallbacks.tagSocket(sock, TAG_SYSTEM_DNS, uid, pid)) {
             LOG(WARNING) << "Failed to tag socket: " << strerror(-err);
         }
     }
@@ -200,4 +182,9 @@
     }
 }
 
-#endif  // NETD_RESOLV_PRIVATE_H
+inline std::string addrToString(const sockaddr_storage* addr) {
+    char out[INET6_ADDRSTRLEN] = {0};
+    getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), out, INET6_ADDRSTRLEN, nullptr, 0,
+                NI_NUMERICHOST);
+    return std::string(out);
+}
diff --git a/resolv_tls_unit_test.cpp b/resolv_tls_unit_test.cpp
index 5d3ca26..ed9c218 100644
--- a/resolv_tls_unit_test.cpp
+++ b/resolv_tls_unit_test.cpp
@@ -148,6 +148,7 @@
 
     EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
     EXPECT_EQ(QUERY, r.response);
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 // Fake Socket that echoes the observed query ID as the response body.
@@ -188,6 +189,7 @@
         // after each use.
         EXPECT_EQ(0, (r.response[2] << 8) | r.response[3]);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 // These queries might be handled in serial or parallel as they race the
@@ -207,6 +209,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(QUERY, r.response);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 // A server that waits until sDelay queries are queued before responding.
@@ -272,6 +275,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(QUERY, r.response);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 TEST_F(TransportTest, ParallelColliding_Max) {
@@ -291,6 +295,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(QUERY, r.response);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 TEST_F(TransportTest, ParallelUnique) {
@@ -310,6 +315,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(queries[i], r.response);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 TEST_F(TransportTest, ParallelUnique_Max) {
@@ -331,6 +337,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(queries[i], r.response);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 TEST_F(TransportTest, IdExhaustion) {
@@ -358,6 +365,7 @@
         EXPECT_EQ(std::future_status::timeout,
                 result.wait_for(std::chrono::duration<int>::zero()));
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 // Responses can come back from the server in any order.  This should have no
@@ -379,6 +387,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(queries[i], r.response);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 TEST_F(TransportTest, ReverseOrder_Max) {
@@ -398,6 +407,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(queries[i], r.response);
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 // Returning null from the factory indicates a connection failure.
@@ -420,6 +430,7 @@
 
     EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
     EXPECT_TRUE(r.response.empty());
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 // Simulate a socket that connects but then immediately receives a server
@@ -445,6 +456,9 @@
 
     EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
     EXPECT_TRUE(r.response.empty());
+
+    // Reconnections are triggered since DnsTlsQueryMap is not empty.
+    EXPECT_EQ(transport.getConnectCounter(), DnsTlsQueryMap::kMaxTries);
 }
 
 // Simulate a server that occasionally closes the connection and silently
@@ -537,6 +551,9 @@
         EXPECT_EQ(DnsTlsTransport::Response::network_error, r.code);
         EXPECT_TRUE(r.response.empty());
     }
+
+    // Reconnections are triggered since DnsTlsQueryMap is not empty.
+    EXPECT_EQ(transport.getConnectCounter(), DnsTlsQueryMap::kMaxTries);
 }
 
 TEST_F(TransportTest, PartialDrop) {
@@ -561,6 +578,33 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         EXPECT_EQ(queries[i], r.response);
     }
+
+    // TODO: transport.getConnectCounter() seems not stable in this test. Find how to check the
+    // connect attempts for this test.
+}
+
+TEST_F(TransportTest, ConnectCounter) {
+    FakeSocketLimited::sLimit = 2;       // Close the socket after 2 queries.
+    FakeSocketLimited::sMaxSize = SIZE;  // No query drops.
+    FakeSocketFactory<FakeSocketLimited> factory;
+    DnsTlsTransport transport(SERVER1, MARK, &factory);
+
+    // Connecting on demand.
+    EXPECT_EQ(transport.getConnectCounter(), 0);
+
+    const int num_queries = 10;
+    std::vector<std::future<DnsTlsTransport::Result>> results;
+    results.reserve(num_queries);
+    for (int i = 0; i < num_queries; i++) {
+        // Reconnections take place every two queries.
+        results.push_back(transport.query(makeSlice(QUERY)));
+    }
+    for (int i = 0; i < num_queries; i++) {
+        auto r = results[i].get();
+        EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
+    }
+
+    EXPECT_EQ(transport.getConnectCounter(), num_queries / FakeSocketLimited::sLimit);
 }
 
 // Simulate a malfunctioning server that injects extra miscellaneous
@@ -604,6 +648,7 @@
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
         // Don't check the response because this server is malfunctioning.
     }
+    EXPECT_EQ(transport.getConnectCounter(), 1);
 }
 
 // Dispatcher tests
@@ -612,28 +657,38 @@
 TEST_F(DispatcherTest, Query) {
     bytevec ans(4096);
     int resplen = 0;
+    bool connectTriggered = false;
 
     auto factory = std::make_unique<FakeSocketFactory<FakeSocketEcho>>();
     DnsTlsDispatcher dispatcher(std::move(factory));
-    auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY),
-                              makeSlice(ans), &resplen);
+    auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY), makeSlice(ans), &resplen,
+                              &connectTriggered);
 
     EXPECT_EQ(DnsTlsTransport::Response::success, r);
     EXPECT_EQ(int(QUERY.size()), resplen);
+    EXPECT_TRUE(connectTriggered);
     ans.resize(resplen);
     EXPECT_EQ(QUERY, ans);
+
+    // Expect to reuse the connection.
+    r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY), makeSlice(ans), &resplen,
+                         &connectTriggered);
+    EXPECT_EQ(DnsTlsTransport::Response::success, r);
+    EXPECT_FALSE(connectTriggered);
 }
 
 TEST_F(DispatcherTest, AnswerTooLarge) {
     bytevec ans(SIZE - 1);  // Too small to hold the answer
     int resplen = 0;
+    bool connectTriggered = false;
 
     auto factory = std::make_unique<FakeSocketFactory<FakeSocketEcho>>();
     DnsTlsDispatcher dispatcher(std::move(factory));
-    auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY),
-                              makeSlice(ans), &resplen);
+    auto r = dispatcher.query(SERVER1, MARK, makeSlice(QUERY), makeSlice(ans), &resplen,
+                              &connectTriggered);
 
     EXPECT_EQ(DnsTlsTransport::Response::limit_error, r);
+    EXPECT_TRUE(connectTriggered);
 }
 
 template<class T>
@@ -678,10 +733,11 @@
             auto q = make_query(i, SIZE);
             bytevec ans(4096);
             int resplen = 0;
+            bool connectTriggered = false;
             unsigned mark = key.first;
             const DnsTlsServer& server = key.second;
-            auto r = dispatcher->query(server, mark, makeSlice(q),
-                                       makeSlice(ans), &resplen);
+            auto r = dispatcher->query(server, mark, makeSlice(q), makeSlice(ans), &resplen,
+                                       &connectTriggered);
             EXPECT_EQ(DnsTlsTransport::Response::success, r);
             EXPECT_EQ(int(q.size()), resplen);
             ans.resize(resplen);
diff --git a/resolv_unit_test.cpp b/resolv_unit_test.cpp
index 3295cac..31d7bf1 100644
--- a/resolv_unit_test.cpp
+++ b/resolv_unit_test.cpp
@@ -22,6 +22,7 @@
 #include <gtest/gtest.h>
 #include <netdb.h>
 #include <netdutils/InternetAddresses.h>
+#include <resolv_stats_test_utils.h>
 
 #include "dns_responder.h"
 #include "getaddrinfo.h"
@@ -42,6 +43,10 @@
 // The buffer size of resolv_gethostbyname().
 constexpr unsigned int MAXPACKET = 8 * 1024;
 
+// Specifying 0 in ai_socktype or ai_protocol of struct addrinfo indicates
+// that any type or protocol can be returned by getaddrinfo().
+constexpr unsigned int ANY = 0;
+
 class TestBase : public ::testing::Test {
   protected:
     struct DnsMessage {
@@ -67,7 +72,7 @@
                 .rclass = rclass,
                 .ttl = ttl,
         };
-        EXPECT_TRUE(test::DNSResponder::fillAnswerRdata(rdata, record));
+        EXPECT_TRUE(test::DNSResponder::fillRdata(rdata, record));
         return record;
     }
 
@@ -387,6 +392,62 @@
 
 TEST_F(ResolvGetAddrInfoTest, AlphabeticalHostname_NoData) {
     constexpr char v4_host_name[] = "v4only.example.com.";
+    // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+    // So don't need to config those values: event_type, return_code, latency_micros,
+    // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+    constexpr char event_ipv6[] = R"Event(
+             NetworkDnsEventReported {
+             dns_query_events:
+             {
+               dns_query_event:[
+                {
+                 rcode: 0,
+                 type: 28,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                 latency_micros: 0,
+                },
+                {
+                 rcode: 0,
+                 type: 28,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                 latency_micros: 0,
+                },
+                {
+                 rcode: 0,
+                 type: 28,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                 latency_micros: 0,
+                },
+                {
+                 rcode: 0,
+                 type: 28,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                 latency_micros: 0,
+                }
+               ]
+             }
+        })Event";
+
     test::DNSResponder dns;
     dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
     ASSERT_TRUE(dns.startServer());
@@ -397,6 +458,7 @@
     const addrinfo hints = {.ai_family = AF_INET6};
     NetworkDnsEventReported event;
     int rv = resolv_getaddrinfo("v4only", nullptr, &hints, &mNetcontext, &result, &event);
+    EXPECT_THAT(event, NetworkDnsEventEq(fromNetworkDnsEventReportedStr(event_ipv6)));
     ScopedAddrinfo result_cleanup(result);
     EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
     EXPECT_EQ(nullptr, result);
@@ -407,7 +469,66 @@
     constexpr char host_name[] = "sawadee.example.com.";
     constexpr char v4addr[] = "1.2.3.4";
     constexpr char v6addr[] = "::1.2.3.4";
+    // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+    // So don't need to config those values: event_type, return_code, latency_micros,
+    // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+    constexpr char event_ipv4[] = R"Event(
+             NetworkDnsEventReported {
+             dns_query_events:
+             {
+               dns_query_event:[
+                {
+                 rcode: 0,
+                 type: 1,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                },
+                {
+                 rcode: 0,
+                 type: 1,
+                 cache_hit: 2,
+                 ip_version: 0,
+                 protocol: 0,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                }
+               ]
+             }
+        })Event";
 
+    constexpr char event_ipv6[] = R"Event(
+             NetworkDnsEventReported {
+             dns_query_events:
+             {
+               dns_query_event:[
+                {
+                 rcode: 0,
+                 type: 28,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                },
+                {
+                 rcode: 0,
+                 type: 28,
+                 cache_hit: 2,
+                 ip_version: 0,
+                 protocol: 0,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                }
+               ]
+             }
+        })Event";
     test::DNSResponder dns;
     dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
     dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
@@ -417,9 +538,10 @@
     static const struct TestConfig {
         int ai_family;
         const std::string expected_addr;
+        const std::string expected_event;
     } testConfigs[]{
-            {AF_INET, v4addr},
-            {AF_INET6, v6addr},
+            {AF_INET, v4addr, event_ipv4},
+            {AF_INET6, v6addr, event_ipv6},
     };
 
     for (const auto& config : testConfigs) {
@@ -430,6 +552,8 @@
         const addrinfo hints = {.ai_family = config.ai_family};
         NetworkDnsEventReported event;
         int rv = resolv_getaddrinfo("sawadee", nullptr, &hints, &mNetcontext, &result, &event);
+        EXPECT_THAT(event,
+                    NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
         ScopedAddrinfo result_cleanup(result);
         EXPECT_EQ(0, rv);
         EXPECT_TRUE(result != nullptr);
@@ -690,7 +814,48 @@
     constexpr char host_name[] = "jiababuei.example.com.";
     constexpr char v4addr[] = "1.2.3.4";
     constexpr char v6addr[] = "::1.2.3.4";
+    // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+    // So don't need to config those values: event_type, return_code, latency_micros,
+    // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+    constexpr char event_ipv4[] = R"Event(
+             NetworkDnsEventReported {
+             dns_query_events:
+             {
+               dns_query_event:[
+                {
+                 rcode: 0,
+                 type: 1,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                 latency_micros: 0,
+                }
+               ]
+             }
+        })Event";
 
+    constexpr char event_ipv6[] = R"Event(
+             NetworkDnsEventReported {
+             dns_query_events:
+             {
+               dns_query_event:[
+                {
+                 rcode: 0,
+                 type: 28,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 0,
+                 dns_server_index: 0,
+                 connected: 0,
+                 latency_micros: 0,
+                }
+               ]
+             }
+        })Event";
     test::DNSResponder dns;
     dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
     dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
@@ -700,9 +865,10 @@
     static const struct TestConfig {
         int ai_family;
         const std::string expected_addr;
+        const std::string expected_event;
     } testConfigs[]{
-            {AF_INET, v4addr},
-            {AF_INET6, v6addr},
+            {AF_INET, v4addr, event_ipv4},
+            {AF_INET6, v6addr, event_ipv6},
     };
 
     for (const auto& config : testConfigs) {
@@ -715,6 +881,8 @@
         NetworkDnsEventReported event;
         int rv = resolv_gethostbyname("jiababuei", config.ai_family, &hbuf, tmpbuf, sizeof(tmpbuf),
                                       &mNetcontext, &hp, &event);
+        EXPECT_THAT(event,
+                    NetworkDnsEventEq(fromNetworkDnsEventReportedStr(config.expected_event)));
         EXPECT_EQ(0, rv);
         EXPECT_TRUE(hp != nullptr);
         EXPECT_EQ(1U, GetNumQueries(dns, host_name));
diff --git a/stats.h b/stats.h
new file mode 100644
index 0000000..cc7de9f
--- /dev/null
+++ b/stats.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <time.h>
+
+#include "params.h"
+
+#define RCODE_INTERNAL_ERROR 254
+#define RCODE_TIMEOUT 255
+
+struct res_sample {
+    time_t at;      // time in s at which the sample was recorded
+    uint16_t rtt;   // round-trip time in ms
+    uint8_t rcode;  // the DNS rcode or RCODE_XXX defined above
+};
+
+// Resolver reachability statistics and run-time parameters.
+struct res_stats {
+    // Stats of the last <sample_count> queries.
+    res_sample samples[MAXNSSAMPLES];
+    // The number of samples stored.
+    uint8_t sample_count;
+    // The next sample to modify.
+    uint8_t sample_next;
+};
+
+// Aggregates the reachability statistics for the given server based on on the stored samples.
+void android_net_res_stats_aggregate(res_stats* stats, int* successes, int* errors, int* timeouts,
+                                     int* internal_errors, int* rtt_avg, time_t* last_sample_time);
+
+int android_net_res_stats_get_info_for_net(unsigned netid, int* nscount,
+                                           sockaddr_storage servers[MAXNS], int* dcount,
+                                           char domains[MAXDNSRCH][MAXDNSRCHPATH],
+                                           res_params* params, res_stats stats[MAXNS],
+                                           int* wait_for_pending_req_timeout_count);
+
+// Returns an array of bools indicating which servers are considered good
+int android_net_res_stats_get_usable_servers(const res_params* params, res_stats stats[],
+                                             int nscount, bool valid_servers[]);
diff --git a/tests/Android.bp b/tests/Android.bp
index f59c371..fa2ea31 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -6,7 +6,6 @@
     ],
     header_libs: [
         "libnetd_resolv_headers",
-        "libnetd_resolv_internal_headers",
     ],
     shared_libs: [
         "libbase",
@@ -15,13 +14,60 @@
     static_libs: [
         "libnetd_test_dnsresponder",
         "libnetdutils",
-        "dnsresolver_aidl_interface-V2-cpp",
+    ],
+}
+
+cc_library_static {
+    name: "golddata_proto",
+    defaults: ["netd_defaults"],
+    proto: {
+        export_proto_headers: true,
+        type: "full",
+    },
+    srcs: [
+        "golddata.proto",
     ],
 }
 
 cc_test {
+    name: "resolv_gold_test",
+    test_suites: ["device-tests", "mts"],
+    require_root: true,
+    defaults: ["netd_defaults"],
+    data: ["testdata/*.pbtxt"],
+    srcs: [
+        "resolv_gold_test.cpp",
+    ],
+    header_libs: [
+        "libnetd_client_headers",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libcrypto",
+        "libprotobuf-cpp-full",
+        "libssl",
+    ],
+    static_libs: [
+        "dnsresolver_aidl_interface-unstable-ndk_platform",
+        "golddata_proto",
+        "libbase",
+        "libgmock",
+        "liblog",
+        "libnetd_resolv",
+        "libnetd_test_dnsresponder_ndk",
+        "libnetd_test_resolv_utils",
+        "libnetdutils",
+        "netd_aidl_interface-ndk_platform",
+        "netd_event_listener_interface-ndk_platform",
+        "server_configurable_flags",
+        "stats_proto",
+    ],
+    compile_multilib: "first",
+}
+
+cc_test {
     name: "resolv_stress_test",
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "mts"],
     // This won't work with test_config
     // require_root: true,
     test_config: "resolv_stress_test_config.xml",
@@ -31,26 +77,104 @@
     ],
     header_libs: [
         "libnetd_resolv_headers",
-        "libnetd_resolv_internal_headers",
     ],
     shared_libs: [
         "libbase",
-        "libbinder",
+        "libbinder_ndk",
         "libnetd_client",
         "libutils",
     ],
     static_libs: [
+        "dnsresolver_aidl_interface-ndk_platform",
         "libgmock",
-        "libnetd_test_dnsresponder",
+        "libnetd_test_dnsresponder_ndk",
         "libnetd_test_resolv_utils",
         "libnetdutils",
-        "netd_aidl_interface-V2-cpp",
-        "netd_event_listener_interface-cpp",
-        "dnsresolver_aidl_interface-V2-cpp",
+        "netd_event_listener_interface-ndk_platform",
+        "netd_aidl_interface-ndk_platform",
+    ],
+    compile_multilib: "first",
+}
+
+cc_test {
+    name: "resolv_integration_test",
+    test_suites: ["device-tests", "mts"],
+    require_root: true,
+    defaults: ["netd_defaults"],
+    tidy: false, // cuts test build time by > 1m30s
+    srcs: [
+        "dns_responder/dns_responder.cpp",
+        "dnsresolver_binder_test.cpp",
+        "resolv_integration_test.cpp",
+    ],
+    header_libs: [
+        "libnetd_resolv_headers",
+    ],
+    // TODO: make static link libc++ work.
+    //stl: "libc++_static",
+    shared_libs: [
+        "libbinder_ndk",
+        "libnetd_client",
+    ],
+    static_libs: [
+        "dnsresolver_aidl_interface-unstable-ndk_platform",
+        "libbase",
+        "libbpf_android",
+        "libcrypto_static",
+        "libgmock",
+        "liblog",
+        "libnetd_test_dnsresponder_ndk",
+        "libnetd_test_metrics_listener",
+        "libnetd_test_resolv_utils",
+        "libnetd_test_tun_interface",
+        "libnetd_test_utils",
+        "libnetdutils",
+        "libssl",
+        "libutils",
+        "netd_aidl_interface-ndk_platform",
+        "netd_event_listener_interface-ndk_platform",
     ],
     compile_multilib: "both",
-    sanitize: {
-        address: true,
-        recover: ["all"],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
     },
 }
+
+cc_test_library {
+    name: "resolv_stats_test_utils",
+    srcs: [
+        "resolv_stats_test_utils.cpp"
+    ],
+    defaults: ["netd_defaults"],
+    export_include_dirs: ["."],
+    static_libs: [
+        "libbase",
+        "libgmock",
+        "liblog",
+        "libnetdutils",
+        "libprotobuf-cpp-lite",
+        "stats_proto",
+    ],
+}
+
+cc_test {
+    name: "resolv_stats_test_utils_test",
+    srcs: [
+        "resolv_stats_test_utils_test.cpp",
+    ],
+    defaults: ["netd_defaults"],
+    test_suites: ["device-tests"],
+    static_libs: [
+        "libbase",
+        "libgmock",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "resolv_stats_test_utils",
+        "stats_proto",
+    ],
+}
diff --git a/tests/dns_metrics_listener/Android.bp b/tests/dns_metrics_listener/Android.bp
index f19ca88..8a957d6 100644
--- a/tests/dns_metrics_listener/Android.bp
+++ b/tests/dns_metrics_listener/Android.bp
@@ -7,11 +7,11 @@
         "test_metrics.cpp",
     ],
     shared_libs: [
-        "libbase",
-        "libbinder",
-        "libutils",
+        "libbinder_ndk",
     ],
     static_libs: [
-        "netd_event_listener_interface-V1-cpp",
+        "libbase",
+        "libutils",
+        "netd_event_listener_interface-ndk_platform",
     ],
 }
diff --git a/tests/dns_metrics_listener/base_metrics_listener.cpp b/tests/dns_metrics_listener/base_metrics_listener.cpp
index e6dbb32..c46f093 100644
--- a/tests/dns_metrics_listener/base_metrics_listener.cpp
+++ b/tests/dns_metrics_listener/base_metrics_listener.cpp
@@ -20,51 +20,51 @@
 namespace net {
 namespace metrics {
 
-android::binder::Status BaseMetricsListener::onDnsEvent(
+::ndk::ScopedAStatus BaseMetricsListener::onDnsEvent(
         int32_t /*netId*/, int32_t /*eventType*/, int32_t /*returnCode*/, int32_t /*latencyMs*/,
-        const std::string& /*hostname*/, const ::std::vector<std::string>& /*ipAddresses*/,
+        const std::string& /*hostname*/, const std::vector<std::string>& /*ipAddresses*/,
         int32_t /*ipAddressesCount*/, int32_t /*uid*/) {
     // default no-op
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 };
 
-android::binder::Status BaseMetricsListener::onPrivateDnsValidationEvent(
-        int32_t /*netId*/, const ::android::String16& /*ipAddress*/,
-        const ::android::String16& /*hostname*/, bool /*validated*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onPrivateDnsValidationEvent(
+        int32_t /*netId*/, const std::string& /*ipAddress*/, const std::string& /*hostname*/,
+        bool /*validated*/) {
     // default no-op
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 };
 
-android::binder::Status BaseMetricsListener::onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
-                                                            int32_t /*latencyMs*/,
-                                                            const ::android::String16& /*ipAddr*/,
-                                                            int32_t /*port*/, int32_t /*uid*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
+                                                         int32_t /*latencyMs*/,
+                                                         const std::string& /*ipAddr*/,
+                                                         int32_t /*port*/, int32_t /*uid*/) {
     // default no-op
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 };
 
-android::binder::Status BaseMetricsListener::onWakeupEvent(
-        const ::android::String16& /*prefix*/, int32_t /*uid*/, int32_t /*ethertype*/,
-        int32_t /*ipNextHeader*/, const ::std::vector<uint8_t>& /*dstHw*/,
-        const ::android::String16& /*srcIp*/, const ::android::String16& /*dstIp*/,
-        int32_t /*srcPort*/, int32_t /*dstPort*/, int64_t /*timestampNs*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onWakeupEvent(
+        const std::string& /*prefix*/, int32_t /*uid*/, int32_t /*ethertype*/,
+        int32_t /*ipNextHeader*/, const std::vector<int8_t>& /*dstHw*/,
+        const std::string& /*srcIp*/, const std::string& /*dstIp*/, int32_t /*srcPort*/,
+        int32_t /*dstPort*/, int64_t /*timestampNs*/) {
     // default no-op
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 };
 
-android::binder::Status BaseMetricsListener::onTcpSocketStatsEvent(
-        const ::std::vector<int32_t>& /*networkIds*/, const ::std::vector<int32_t>& /*sentPackets*/,
-        const ::std::vector<int32_t>& /*lostPackets*/, const ::std::vector<int32_t>& /*rttUs*/,
-        const ::std::vector<int32_t>& /*sentAckDiffMs*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onTcpSocketStatsEvent(
+        const std::vector<int32_t>& /*networkIds*/, const std::vector<int32_t>& /*sentPackets*/,
+        const std::vector<int32_t>& /*lostPackets*/, const std::vector<int32_t>& /*rttUs*/,
+        const std::vector<int32_t>& /*sentAckDiffMs*/) {
     // default no-op
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 };
 
-android::binder::Status BaseMetricsListener::onNat64PrefixEvent(
-        int32_t /*netId*/, bool /*added*/, const ::std::string& /*prefixString*/,
-        int32_t /*prefixLength*/) {
+::ndk::ScopedAStatus BaseMetricsListener::onNat64PrefixEvent(int32_t /*netId*/, bool /*added*/,
+                                                             const std::string& /*prefixString*/,
+                                                             int32_t /*prefixLength*/) {
     // default no-op
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 };
 
 }  // namespace metrics
diff --git a/tests/dns_metrics_listener/base_metrics_listener.h b/tests/dns_metrics_listener/base_metrics_listener.h
index 9ba8423..d50997d 100644
--- a/tests/dns_metrics_listener/base_metrics_listener.h
+++ b/tests/dns_metrics_listener/base_metrics_listener.h
@@ -19,43 +19,44 @@
 #include <string>
 #include <vector>
 
-#include "android/net/metrics/BnNetdEventListener.h"
+#include <aidl/android/net/metrics/BnNetdEventListener.h>
 
 namespace android {
 namespace net {
 namespace metrics {
 
-class BaseMetricsListener : public BnNetdEventListener {
+class BaseMetricsListener : public aidl::android::net::metrics::BnNetdEventListener {
   public:
     BaseMetricsListener() = default;
     ~BaseMetricsListener() = default;
 
-    virtual android::binder::Status onDnsEvent(int32_t /*netId*/, int32_t /*eventType*/,
-                                               int32_t /*returnCode*/, int32_t /*latencyMs*/,
-                                               const std::string& /*hostname*/,
-                                               const ::std::vector<std::string>& /*ipAddresses*/,
-                                               int32_t /*ipAddressesCount*/,
-                                               int32_t /*uid*/) override;
-    virtual android::binder::Status onPrivateDnsValidationEvent(
-            int32_t /*netId*/, const ::android::String16& /*ipAddress*/,
-            const ::android::String16& /*hostname*/, bool /*validated*/) override;
-    virtual android::binder::Status onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
-                                                   int32_t /*latencyMs*/,
-                                                   const ::android::String16& /*ipAddr*/,
-                                                   int32_t /*port*/, int32_t /*uid*/) override;
-    virtual android::binder::Status onWakeupEvent(
-            const ::android::String16& /*prefix*/, int32_t /*uid*/, int32_t /*ethertype*/,
-            int32_t /*ipNextHeader*/, const ::std::vector<uint8_t>& /*dstHw*/,
-            const ::android::String16& /*srcIp*/, const ::android::String16& /*dstIp*/,
-            int32_t /*srcPort*/, int32_t /*dstPort*/, int64_t /*timestampNs*/) override;
-    virtual android::binder::Status onTcpSocketStatsEvent(
-            const ::std::vector<int32_t>& /*networkIds*/,
-            const ::std::vector<int32_t>& /*sentPackets*/,
-            const ::std::vector<int32_t>& /*lostPackets*/, const ::std::vector<int32_t>& /*rttUs*/,
-            const ::std::vector<int32_t>& /*sentAckDiffMs*/) override;
-    virtual android::binder::Status onNat64PrefixEvent(int32_t /*netId*/, bool /*added*/,
-                                                       const ::std::string& /*prefixString*/,
-                                                       int32_t /*prefixLength*/) override;
+    virtual ::ndk::ScopedAStatus onDnsEvent(int32_t /*netId*/, int32_t /*eventType*/,
+                                            int32_t /*returnCode*/, int32_t /*latencyMs*/,
+                                            const std::string& /*hostname*/,
+                                            const std::vector<std::string>& /*ipAddresses*/,
+                                            int32_t /*ipAddressesCount*/, int32_t /*uid*/) override;
+    virtual ::ndk::ScopedAStatus onPrivateDnsValidationEvent(int32_t /*netId*/,
+                                                             const std::string& /*ipAddress*/,
+                                                             const std::string& /*hostname*/,
+                                                             bool /*validated*/) override;
+    virtual ::ndk::ScopedAStatus onConnectEvent(int32_t /*netId*/, int32_t /*error*/,
+                                                int32_t /*latencyMs*/,
+                                                const std::string& /*ipAddr*/, int32_t /*port*/,
+                                                int32_t /*uid*/) override;
+    virtual ::ndk::ScopedAStatus onWakeupEvent(const std::string& /*prefix*/, int32_t /*uid*/,
+                                               int32_t /*ethertype*/, int32_t /*ipNextHeader*/,
+                                               const std::vector<int8_t>& /*dstHw*/,
+                                               const std::string& /*srcIp*/,
+                                               const std::string& /*dstIp*/, int32_t /*srcPort*/,
+                                               int32_t /*dstPort*/,
+                                               int64_t /*timestampNs*/) override;
+    virtual ::ndk::ScopedAStatus onTcpSocketStatsEvent(
+            const std::vector<int32_t>& /*networkIds*/, const std::vector<int32_t>& /*sentPackets*/,
+            const std::vector<int32_t>& /*lostPackets*/, const std::vector<int32_t>& /*rttUs*/,
+            const std::vector<int32_t>& /*sentAckDiffMs*/) override;
+    virtual ::ndk::ScopedAStatus onNat64PrefixEvent(int32_t /*netId*/, bool /*added*/,
+                                                    const std::string& /*prefixString*/,
+                                                    int32_t /*prefixLength*/) override;
 };
 
 }  // namespace metrics
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.cpp b/tests/dns_metrics_listener/dns_metrics_listener.cpp
index acfb416..18c98d2 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.cpp
+++ b/tests/dns_metrics_listener/dns_metrics_listener.cpp
@@ -29,26 +29,24 @@
 constexpr milliseconds kRetryIntervalMs{20};
 constexpr milliseconds kEventTimeoutMs{5000};
 
-android::binder::Status DnsMetricsListener::onNat64PrefixEvent(int32_t netId, bool added,
-                                                               const std::string& prefixString,
-                                                               int32_t /*prefixLength*/) {
+::ndk::ScopedAStatus DnsMetricsListener::onNat64PrefixEvent(int32_t netId, bool added,
+                                                            const std::string& prefixString,
+                                                            int32_t /*prefixLength*/) {
     std::lock_guard lock(mMutex);
     if (netId == mNetId) mNat64Prefix = added ? prefixString : "";
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 }
 
-android::binder::Status DnsMetricsListener::onPrivateDnsValidationEvent(
-        int32_t netId, const ::android::String16& ipAddress,
-        const ::android::String16& /*hostname*/, bool validated) {
+::ndk::ScopedAStatus DnsMetricsListener::onPrivateDnsValidationEvent(
+        int32_t netId, const std::string& ipAddress, const std::string& /*hostname*/,
+        bool validated) {
     {
         std::lock_guard lock(mMutex);
-        std::string serverAddr(String8(ipAddress.string()));
-
         // keep updating the server to have latest validation status.
-        mValidationRecords.insert_or_assign({netId, serverAddr}, validated);
+        mValidationRecords.insert_or_assign({netId, ipAddress}, validated);
     }
     mCv.notify_one();
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 }
 
 bool DnsMetricsListener::waitForNat64Prefix(ExpectNat64PrefixStatus status,
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.h b/tests/dns_metrics_listener/dns_metrics_listener.h
index d933b13..d6268e2 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.h
+++ b/tests/dns_metrics_listener/dns_metrics_listener.h
@@ -42,14 +42,13 @@
     DnsMetricsListener(int32_t netId) : mNetId(netId){};
 
     // Override DNS metrics event(s).
-    android::binder::Status onNat64PrefixEvent(int32_t netId, bool added,
-                                               const std::string& prefixString,
-                                               int32_t /*prefixLength*/) override;
+    ::ndk::ScopedAStatus onNat64PrefixEvent(int32_t netId, bool added,
+                                            const std::string& prefixString,
+                                            int32_t /*prefixLength*/) override;
 
-    android::binder::Status onPrivateDnsValidationEvent(int32_t netId,
-                                                        const ::android::String16& ipAddress,
-                                                        const ::android::String16& /*hostname*/,
-                                                        bool validated) override;
+    ::ndk::ScopedAStatus onPrivateDnsValidationEvent(int32_t netId, const std::string& ipAddress,
+                                                     const std::string& /*hostname*/,
+                                                     bool validated) override;
 
     // Wait for expected NAT64 prefix status until timeout.
     bool waitForNat64Prefix(ExpectNat64PrefixStatus status,
diff --git a/tests/dns_metrics_listener/test_metrics.cpp b/tests/dns_metrics_listener/test_metrics.cpp
index 094c3a8..036abe8 100644
--- a/tests/dns_metrics_listener/test_metrics.cpp
+++ b/tests/dns_metrics_listener/test_metrics.cpp
@@ -33,11 +33,11 @@
 }
 
 // Derived class TestOnDnsEvent.
-android::binder::Status TestOnDnsEvent::onDnsEvent(int32_t netId, int32_t eventType,
-                                                   int32_t returnCode, int32_t /*latencyMs*/,
-                                                   const std::string& hostname,
-                                                   const ::std::vector<std::string>& ipAddresses,
-                                                   int32_t ipAddressesCount, int32_t /*uid*/) {
+::ndk::ScopedAStatus TestOnDnsEvent::onDnsEvent(int32_t netId, int32_t eventType,
+                                                int32_t returnCode, int32_t /*latencyMs*/,
+                                                const std::string& hostname,
+                                                const ::std::vector<std::string>& ipAddresses,
+                                                int32_t ipAddressesCount, int32_t /*uid*/) {
     // A bitwise-OR combination of all expected test cases.
     // Currently, the maximum number of test case is 32 because a 32-bits bitwise-OR combination
     // is used for checking and recording verified test cases.
@@ -77,7 +77,7 @@
         notify();
     }
 
-    return android::binder::Status::ok();
+    return ::ndk::ScopedAStatus::ok();
 };
 
 }  // namespace metrics
diff --git a/tests/dns_metrics_listener/test_metrics.h b/tests/dns_metrics_listener/test_metrics.h
index d098aca..03c9842 100644
--- a/tests/dns_metrics_listener/test_metrics.h
+++ b/tests/dns_metrics_listener/test_metrics.h
@@ -83,10 +83,10 @@
     bool isVerified() override { return (getVerified() & EventFlag::onDnsEvent) != 0; }
 
     // Override for testing verification.
-    android::binder::Status onDnsEvent(int32_t netId, int32_t eventType, int32_t returnCode,
-                                       int32_t /*latencyMs*/, const std::string& hostname,
-                                       const std::vector<std::string>& ipAddresses,
-                                       int32_t ipAddressesCount, int32_t /*uid*/) override;
+    ::ndk::ScopedAStatus onDnsEvent(int32_t netId, int32_t eventType, int32_t returnCode,
+                                    int32_t /*latencyMs*/, const std::string& hostname,
+                                    const std::vector<std::string>& ipAddresses,
+                                    int32_t ipAddressesCount, int32_t /*uid*/) override;
 
   private:
     const std::vector<TestResult>& mResults;  // Expected results for test verification.
diff --git a/tests/dns_responder/Android.bp b/tests/dns_responder/Android.bp
index 0f07088..c1e785f 100644
--- a/tests/dns_responder/Android.bp
+++ b/tests/dns_responder/Android.bp
@@ -1,14 +1,15 @@
+// TODO: Remove libnetd_test_dnsresponder after eliminating all users.
 cc_library_static {
     name: "libnetd_test_dnsresponder",
     defaults: ["netd_defaults"],
     shared_libs: [
-        "dnsresolver_aidl_interface-cpp",
+        "dnsresolver_aidl_interface-unstable-cpp",
         "libbase",
         "libbinder",
         "libnetd_client",
         "libnetdutils",
         "libssl",
-        "netd_aidl_interface-V2-cpp",
+        "netd_aidl_interface-cpp",
     ],
     srcs: [
         "dns_responder.cpp",
@@ -17,3 +18,35 @@
     ],
     export_include_dirs: ["."],
 }
+
+cc_library {
+    name: "libnetd_test_dnsresponder_ndk",
+    defaults: ["netd_defaults"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libnetd_client",
+    ],
+    header_libs: [
+        "libnetd_resolv_headers",
+        "libnetd_resolv_internal_headers",
+    ],
+    export_header_lib_headers: [
+        "libnetd_resolv_internal_headers",
+    ],
+    static_libs: [
+        "dnsresolver_aidl_interface-unstable-ndk_platform",
+        "libbase",
+        "libcrypto_static",
+        "liblog",
+        "libnetdutils",
+        "libssl",
+        "netd_event_listener_interface-ndk_platform",
+        "netd_aidl_interface-ndk_platform",
+    ],
+    srcs: [
+        "dns_responder.cpp",
+        "dns_responder_client_ndk.cpp",
+        "dns_tls_frontend.cpp",
+    ],
+    export_include_dirs: ["."],
+}
diff --git a/tests/dns_responder/dns_responder.cpp b/tests/dns_responder/dns_responder.cpp
index 072c2dd..62b783c 100644
--- a/tests/dns_responder/dns_responder.cpp
+++ b/tests/dns_responder/dns_responder.cpp
@@ -36,10 +36,12 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <netdutils/InternetAddresses.h>
+#include <netdutils/Slice.h>
 #include <netdutils/SocketOption.h>
 
 using android::netdutils::enableSockopt;
 using android::netdutils::ScopedAddrinfo;
+using android::netdutils::Slice;
 
 namespace test {
 
@@ -139,6 +141,17 @@
     return it->second;
 }
 
+const char* dnsproto2str(int protocol) {
+    switch (protocol) {
+        case IPPROTO_TCP:
+            return "TCP";
+        case IPPROTO_UDP:
+            return "UDP";
+        default:
+            return "UNKNOWN";
+    }
+}
+
 const char* DNSName::read(const char* buffer, const char* buffer_end) {
     const char* cur = buffer;
     bool last = false;
@@ -402,6 +415,15 @@
     return buffer_cur;
 }
 
+// TODO: convert all callers to this interface, then delete the old one.
+bool DNSHeader::write(std::vector<uint8_t>* out) const {
+    char buffer[4096];
+    char* end = this->write(buffer, buffer + sizeof buffer);
+    if (end == nullptr) return false;
+    out->insert(out->end(), buffer, end);
+    return true;
+}
+
 std::string DNSHeader::toString() const {
     // TODO
     return std::string();
@@ -446,58 +468,79 @@
 
 void DNSResponder::addMapping(const std::string& name, ns_type type, const std::string& addr) {
     std::lock_guard lock(mappings_mutex_);
-    // TODO: Consider using std::map::insert_or_assign().
-    auto it = mappings_.find(QueryKey(name, type));
-    if (it != mappings_.end()) {
-        LOG(INFO) << "Overwriting mapping for (" << name << ", " << dnstype2str(type)
-                  << "), previous address " << it->second << " new address " << addr;
-        it->second = addr;
-        return;
-    }
-    mappings_.try_emplace({name, type}, addr);
+    mappings_[{name, type}] = addr;
 }
 
 void DNSResponder::addMappingDnsHeader(const std::string& name, ns_type type,
                                        const DNSHeader& header) {
     std::lock_guard lock(mappings_mutex_);
-    // TODO: Consider using std::map::insert_or_assign().
-    auto it = dnsheader_mappings_.find(QueryKey(name, type));
-    if (it != dnsheader_mappings_.end()) {
-        // TODO: Perhaps replace header pointer with header content once DNSHeader::toString() has
-        // been implemented.
-        LOG(INFO) << "Overwriting mapping for (" << name << ", " << dnstype2str(type)
-                  << "), previous header " << (void*)&it->second << " new header "
-                  << (void*)&header;
-        it->second = header;
-        return;
-    }
-    dnsheader_mappings_.try_emplace({name, type}, header);
+    dnsheader_mappings_[{name, type}] = header;
+}
+
+void DNSResponder::addMappingBinaryPacket(const std::vector<uint8_t>& query,
+                                          const std::vector<uint8_t>& response) {
+    std::lock_guard lock(mappings_mutex_);
+    packet_mappings_[query] = response;
 }
 
 void DNSResponder::removeMapping(const std::string& name, ns_type type) {
     std::lock_guard lock(mappings_mutex_);
-    auto it = mappings_.find(QueryKey(name, type));
-    if (it != mappings_.end()) {
-        mappings_.erase(it);
-        return;
+    if (!mappings_.erase({name, type})) {
+        LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type)
+                   << "), not present in registered mappings";
     }
-    LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type)
-               << "), not present in registered mappings";
 }
 
 void DNSResponder::removeMappingDnsHeader(const std::string& name, ns_type type) {
     std::lock_guard lock(mappings_mutex_);
-    auto it = dnsheader_mappings_.find(QueryKey(name, type));
-    if (it != dnsheader_mappings_.end()) {
-        dnsheader_mappings_.erase(it);
-        return;
+    if (!dnsheader_mappings_.erase({name, type})) {
+        LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type)
+                   << "), not present in registered DnsHeader mappings";
     }
-    LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type)
-               << "), not present in registered DnsHeader mappings";
 }
 
+void DNSResponder::removeMappingBinaryPacket(const std::vector<uint8_t>& query) {
+    std::lock_guard lock(mappings_mutex_);
+    if (!packet_mappings_.erase(query)) {
+        LOG(ERROR) << "Cannot remove mapping, not present in registered BinaryPacket mappings";
+        LOG(INFO) << "Hex dump:";
+        LOG(INFO) << android::netdutils::toHex(
+                Slice(const_cast<uint8_t*>(query.data()), query.size()), 32);
+    }
+}
+
+// Set response probability on all supported protocols.
 void DNSResponder::setResponseProbability(double response_probability) {
-    response_probability_ = response_probability;
+    setResponseProbability(response_probability, IPPROTO_TCP);
+    setResponseProbability(response_probability, IPPROTO_UDP);
+}
+
+// Set response probability on specific protocol. It's caller's duty to ensure that the |protocol|
+// can be supported by DNSResponder.
+void DNSResponder::setResponseProbability(double response_probability, int protocol) {
+    switch (protocol) {
+        case IPPROTO_TCP:
+            response_probability_tcp_ = response_probability;
+            break;
+        case IPPROTO_UDP:
+            response_probability_udp_ = response_probability;
+            break;
+        default:
+            LOG(FATAL) << "Unsupported protocol " << protocol;  // abort() by log level FATAL
+    }
+}
+
+double DNSResponder::getResponseProbability(int protocol) const {
+    switch (protocol) {
+        case IPPROTO_TCP:
+            return response_probability_tcp_;
+        case IPPROTO_UDP:
+            return response_probability_udp_;
+        default:
+            LOG(FATAL) << "Unsupported protocol " << protocol;  // abort() by log level FATAL
+            // unreachable
+            return -1;
+    }
 }
 
 void DNSResponder::setEdns(Edns edns) {
@@ -505,7 +548,7 @@
 }
 
 bool DNSResponder::running() const {
-    return socket_.get() != -1;
+    return (udp_socket_.ok()) && (tcp_socket_.ok());
 }
 
 bool DNSResponder::startServer() {
@@ -514,59 +557,51 @@
         return false;
     }
 
-    // Set up UDP socket.
-    addrinfo ai_hints{
-            .ai_flags = AI_PASSIVE,
-            .ai_family = AF_UNSPEC,
-            .ai_socktype = SOCK_DGRAM,
-    };
-    addrinfo* ai_res = nullptr;
-    int rv = getaddrinfo(listen_address_.c_str(), listen_service_.c_str(), &ai_hints, &ai_res);
-    ScopedAddrinfo ai_res_cleanup(ai_res);
-    if (rv) {
-        LOG(ERROR) << "getaddrinfo(" << listen_address_ << ", " << listen_service_
-                   << ") failed: " << gai_strerror(rv);
+    // Create UDP, TCP socket
+    if (udp_socket_ = createListeningSocket(SOCK_DGRAM); udp_socket_.get() < 0) {
+        PLOG(ERROR) << "failed to create UDP socket";
         return false;
     }
-    for (const addrinfo* ai = ai_res; ai; ai = ai->ai_next) {
-        socket_.reset(socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, ai->ai_protocol));
-        if (socket_.get() < 0) {
-            PLOG(INFO) << "ignore creating socket " << socket_.get() << " failed";
-            continue;
-        }
-        enableSockopt(socket_.get(), SOL_SOCKET, SO_REUSEPORT).ignoreError();
-        enableSockopt(socket_.get(), SOL_SOCKET, SO_REUSEADDR).ignoreError();
-        std::string host_str = addr2str(ai->ai_addr, ai->ai_addrlen);
-        if (bind(socket_.get(), ai->ai_addr, ai->ai_addrlen)) {
-            LOG(INFO) << "failed to bind UDP " << host_str << ":" << listen_service_;
-            continue;
-        }
-        LOG(INFO) << "bound to UDP " << host_str << ":" << listen_service_;
-        break;
+
+    if (tcp_socket_ = createListeningSocket(SOCK_STREAM); tcp_socket_.get() < 0) {
+        PLOG(ERROR) << "failed to create TCP socket";
+        return false;
+    }
+
+    if (listen(tcp_socket_.get(), 1) < 0) {
+        PLOG(ERROR) << "failed to listen TCP socket";
+        return false;
     }
 
     // Set up eventfd socket.
     event_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
     if (event_fd_.get() == -1) {
-        PLOG(ERROR) << "failed to create eventfd " << event_fd_.get();
+        PLOG(ERROR) << "failed to create eventfd";
         return false;
     }
 
     // Set up epoll socket.
     epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
     if (epoll_fd_.get() < 0) {
-        PLOG(ERROR) << "epoll_create1() failed on fd " << epoll_fd_.get();
+        PLOG(ERROR) << "epoll_create1() failed on fd";
         return false;
     }
 
-    LOG(INFO) << "adding socket " << socket_.get() << " to epoll";
-    if (!addFd(socket_.get(), EPOLLIN)) {
-        LOG(ERROR) << "failed to add the socket " << socket_.get() << " to epoll";
+    LOG(INFO) << "adding UDP socket to epoll";
+    if (!addFd(udp_socket_.get(), EPOLLIN)) {
+        LOG(ERROR) << "failed to add the UDP socket to epoll";
         return false;
     }
-    LOG(INFO) << "adding eventfd " << event_fd_.get() << " to epoll";
+
+    LOG(INFO) << "adding TCP socket to epoll";
+    if (!addFd(tcp_socket_.get(), EPOLLIN)) {
+        LOG(ERROR) << "failed to add the TCP socket to epoll";
+        return false;
+    }
+
+    LOG(INFO) << "adding eventfd to epoll";
     if (!addFd(event_fd_.get(), EPOLLIN)) {
-        LOG(ERROR) << "failed to add the eventfd " << event_fd_.get() << " to epoll";
+        LOG(ERROR) << "failed to add the eventfd to epoll";
         return false;
     }
 
@@ -590,12 +625,14 @@
     }
     handler_thread_.join();
     epoll_fd_.reset();
-    socket_.reset();
+    event_fd_.reset();
+    udp_socket_.reset();
+    tcp_socket_.reset();
     LOG(INFO) << "server stopped successfully";
     return true;
 }
 
-std::vector<std::pair<std::string, ns_type>> DNSResponder::queries() const {
+std::vector<DNSResponder::QueryInfo> DNSResponder::queries() const {
     std::lock_guard lock(queries_mutex_);
     return queries_;
 }
@@ -603,8 +640,10 @@
 std::string DNSResponder::dumpQueries() const {
     std::lock_guard lock(queries_mutex_);
     std::string out;
+
     for (const auto& q : queries_) {
-        out += "{\"" + q.first + "\", " + std::to_string(q.second) + "} ";
+        out += "{\"" + q.name + "\", " + std::to_string(q.type) + "\", " +
+               dnsproto2str(q.protocol) + "} ";
     }
     return out;
 }
@@ -614,6 +653,15 @@
     queries_.clear();
 }
 
+bool DNSResponder::hasOptPseudoRR(DNSHeader* header) const {
+    if (header->additionals.empty()) return false;
+
+    // OPT RR may be placed anywhere within the additional section. See RFC 6891 section 6.1.1.
+    auto found = std::find_if(header->additionals.begin(), header->additionals.end(),
+                              [](const auto& a) { return a.rtype == ns_type::ns_t_opt; });
+    return found != header->additionals.end();
+}
+
 void DNSResponder::requestHandler() {
     epoll_event evs[EPOLL_MAX_EVENTS];
     while (true) {
@@ -629,8 +677,10 @@
             if (fd == event_fd_.get() && (events & (EPOLLIN | EPOLLERR))) {
                 handleEventFd();
                 return;
-            } else if (fd == socket_.get() && (events & (EPOLLIN | EPOLLERR))) {
-                handleQuery();
+            } else if (fd == udp_socket_.get() && (events & (EPOLLIN | EPOLLERR))) {
+                handleQuery(IPPROTO_UDP);
+            } else if (fd == tcp_socket_.get() && (events & (EPOLLIN | EPOLLERR))) {
+                handleQuery(IPPROTO_TCP);
             } else {
                 LOG(WARNING) << "unexpected epoll events " << events << " on fd " << fd;
             }
@@ -638,9 +688,9 @@
     }
 }
 
-bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, char* response,
+bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, int protocol, char* response,
                                     size_t* response_len) const {
-    LOG(DEBUG) << "request: '" << str2hex(buffer, len) << "'";
+    LOG(DEBUG) << "request: '" << str2hex(buffer, len) << "', on " << dnsproto2str(protocol);
     const char* buffer_end = buffer + len;
     DNSHeader header;
     const char* cur = header.read(buffer, buffer_end);
@@ -684,13 +734,12 @@
     {
         std::lock_guard lock(queries_mutex_);
         for (const DNSQuestion& question : header.questions) {
-            queries_.push_back(make_pair(question.qname.name, ns_type(question.qtype)));
+            queries_.push_back({question.qname.name, ns_type(question.qtype), protocol});
         }
     }
-
     // Ignore requests with the preset probability.
     auto constexpr bound = std::numeric_limits<unsigned>::max();
-    if (arc4random_uniform(bound) > bound * response_probability_) {
+    if (arc4random_uniform(bound) > bound * getResponseProbability(protocol)) {
         if (error_rcode_ < 0) {
             LOG(ERROR) << "Returning no response";
             return false;
@@ -703,13 +752,7 @@
 
     // Make the response. The query has been read into |header| which is used to build and return
     // the response as well.
-    switch (mapping_type_) {
-        case MappingType::DNS_HEADER:
-            return makeResponseFromDnsHeader(&header, response, response_len);
-        case MappingType::ADDRESS_OR_HOSTNAME:
-        default:
-            return makeResponse(&header, response, response_len);
-    }
+    return makeResponse(&header, protocol, response, response_len);
 }
 
 bool DNSResponder::addAnswerRecords(const DNSQuestion& question,
@@ -742,7 +785,7 @@
                     .rclass = ns_class::ns_c_in,
                     .ttl = kAnswerRecordTtlSec,  // seconds
             };
-            if (!fillAnswerRdata(it->second, record)) return false;
+            if (!fillRdata(it->second, record)) return false;
             answers->push_back(std::move(record));
             if (rtype != ns_type::ns_t_cname) break;
             rname = it->second;
@@ -758,7 +801,7 @@
     return true;
 }
 
-bool DNSResponder::fillAnswerRdata(const std::string& rdatastr, DNSRecord& record) {
+bool DNSResponder::fillRdata(const std::string& rdatastr, DNSRecord& record) {
     if (record.rtype == ns_type::ns_t_a) {
         record.rdata.resize(4);
         if (inet_pton(AF_INET, rdatastr.c_str(), record.rdata.data()) != 1) {
@@ -771,7 +814,8 @@
             LOG(ERROR) << "inet_pton(AF_INET6, " << rdatastr << ") failed";
             return false;
         }
-    } else if ((record.rtype == ns_type::ns_t_ptr) || (record.rtype == ns_type::ns_t_cname)) {
+    } else if ((record.rtype == ns_type::ns_t_ptr) || (record.rtype == ns_type::ns_t_cname) ||
+               (record.rtype == ns_type::ns_t_ns)) {
         constexpr char delimiter = '.';
         std::string name = rdatastr;
         std::vector<char> rdata;
@@ -808,6 +852,16 @@
     return true;
 }
 
+bool DNSResponder::writePacket(const DNSHeader* header, char* response,
+                               size_t* response_len) const {
+    char* response_cur = header->write(response, response + *response_len);
+    if (response_cur == nullptr) {
+        return false;
+    }
+    *response_len = response_cur - response;
+    return true;
+}
+
 bool DNSResponder::makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response,
                                      size_t* response_len) const {
     header->answers.clear();
@@ -815,13 +869,69 @@
     header->additionals.clear();
     header->rcode = rcode;
     header->qr = true;
-    char* response_cur = header->write(response, response + *response_len);
-    if (response_cur == nullptr) return false;
-    *response_len = response_cur - response;
+    return writePacket(header, response, response_len);
+}
+
+bool DNSResponder::makeTruncatedResponse(DNSHeader* header, char* response,
+                                         size_t* response_len) const {
+    // Build a minimal response for non-EDNS response over UDP. Truncate all stub RRs in answer,
+    // authority and additional section. EDNS response truncation has not supported here yet
+    // because the EDNS response must have an OPT record. See RFC 6891 section 7.
+    header->answers.clear();
+    header->authorities.clear();
+    header->additionals.clear();
+    header->qr = true;
+    header->tr = true;
+    return writePacket(header, response, response_len);
+}
+
+bool DNSResponder::makeResponse(DNSHeader* header, int protocol, char* response,
+                                size_t* response_len) const {
+    char buffer[4096];
+    size_t buffer_len = sizeof(buffer);
+    bool ret;
+
+    switch (mapping_type_) {
+        case MappingType::DNS_HEADER:
+            ret = makeResponseFromDnsHeader(header, buffer, &buffer_len);
+            break;
+        case MappingType::BINARY_PACKET:
+            ret = makeResponseFromBinaryPacket(header, buffer, &buffer_len);
+            break;
+        case MappingType::ADDRESS_OR_HOSTNAME:
+        default:
+            ret = makeResponseFromAddressOrHostname(header, buffer, &buffer_len);
+    }
+
+    if (!ret) return false;
+
+    // Return truncated response if the built non-EDNS response size which is larger than 512 bytes
+    // will be responded over UDP. The truncated response implementation here just simply set up
+    // the TC bit and truncate all stub RRs in answer, authority and additional section. It is
+    // because the resolver will retry DNS query over TCP and use the full TCP response. See also
+    // RFC 1035 section 4.2.1 for UDP response truncation and RFC 6891 section 4.3 for EDNS larger
+    // response size capability.
+    // TODO: Perhaps keep the stub RRs as possible.
+    // TODO: Perhaps truncate the EDNS based response over UDP. See also RFC 6891 section 4.3,
+    // section 6.2.5 and section 7.
+    if (protocol == IPPROTO_UDP && buffer_len > kMaximumUdpSize &&
+        !hasOptPseudoRR(header) /* non-EDNS */) {
+        LOG(INFO) << "Return truncated response because original response length " << buffer_len
+                  << " is larger than " << kMaximumUdpSize << " bytes.";
+        return makeTruncatedResponse(header, response, response_len);
+    }
+
+    if (buffer_len > *response_len) {
+        LOG(ERROR) << "buffer overflow on line " << __LINE__;
+        return false;
+    }
+    memcpy(response, buffer, buffer_len);
+    *response_len = buffer_len;
     return true;
 }
 
-bool DNSResponder::makeResponse(DNSHeader* header, char* response, size_t* response_len) const {
+bool DNSResponder::makeResponseFromAddressOrHostname(DNSHeader* header, char* response,
+                                                     size_t* response_len) const {
     for (const DNSQuestion& question : header->questions) {
         if (question.qclass != ns_class::ns_c_in && question.qclass != ns_class::ns_c_any) {
             LOG(INFO) << "unsupported question class " << question.qclass;
@@ -832,14 +942,8 @@
             return makeErrorResponse(header, ns_rcode::ns_r_servfail, response, response_len);
         }
     }
-
     header->qr = true;
-    char* response_cur = header->write(response, response + *response_len);
-    if (response_cur == nullptr) {
-        return false;
-    }
-    *response_len = response_cur - response;
-    return true;
+    return writePacket(header, response, response_len);
 }
 
 bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response,
@@ -880,18 +984,50 @@
         LOG(INFO) << "no mapping found for " << name << " " << dnstype2str(qtype)
                   << ", couldn't build a response from DNSHeader mapping";
 
-        // Note that do nothing as makeResponse() if no mapping is found. It just changes the QR
-        // flag from query (0) to response (1) in the query. Then, send the modified query back as
-        // a response.
+        // Note that do nothing as makeResponseFromAddressOrHostname() if no mapping is found. It
+        // just changes the QR flag from query (0) to response (1) in the query. Then, send the
+        // modified query back as a response.
         header->qr = true;
     }
+    return writePacket(header, response, response_len);
+}
 
-    char* response_cur = header->write(response, response + *response_len);
-    if (response_cur == nullptr) {
-        return false;
+bool DNSResponder::makeResponseFromBinaryPacket(DNSHeader* header, char* response,
+                                                size_t* response_len) const {
+    std::lock_guard guard(mappings_mutex_);
+
+    // Build a search key of mapping from the query.
+    // TODO: Perhaps pass the query packet buffer directly from the caller.
+    std::vector<uint8_t> queryKey;
+    if (!header->write(&queryKey)) return false;
+    // Clear ID field (byte 0-1) because it is not required by the mapping key.
+    queryKey[0] = 0;
+    queryKey[1] = 0;
+
+    const auto it = packet_mappings_.find(queryKey);
+    if (it != packet_mappings_.end()) {
+        if (it->second.size() > *response_len) {
+            LOG(ERROR) << "buffer overflow on line " << __LINE__;
+            return false;
+        } else {
+            std::copy(it->second.begin(), it->second.end(), response);
+            // Leave the "RD" flag assignment for testing. The "RD" flag of the response keep
+            // using the one from the raw packet mapping but the received query.
+            // Assign "ID" field from query to response. See RFC 1035 section 4.1.1.
+            reinterpret_cast<uint16_t*>(response)[0] = htons(header->id);  // bytes 0-1: id
+            *response_len = it->second.size();
+            return true;
+        }
+    } else {
+        // TODO: handle correctly. See also TODO in addAnswerRecords().
+        // TODO: Perhaps dump packet content to indicate which query failed.
+        LOG(INFO) << "no mapping found, couldn't build a response from BinaryPacket mapping";
+        // Note that do nothing as makeResponseFromAddressOrHostname() if no mapping is found. It
+        // just changes the QR flag from query (0) to response (1) in the query. Then, send the
+        // modified query back as a response.
+        header->qr = true;
+        return writePacket(header, response, response_len);
     }
-    *response_len = response_cur - response;
-    return true;
 }
 
 void DNSResponder::setDeferredResp(bool deferred_resp) {
@@ -913,38 +1049,99 @@
     return true;
 }
 
-void DNSResponder::handleQuery() {
+void DNSResponder::handleQuery(int protocol) {
     char buffer[4096];
     sockaddr_storage sa;
     socklen_t sa_len = sizeof(sa);
-    ssize_t len;
-    do {
-        len = recvfrom(socket_.get(), buffer, sizeof(buffer), 0, (sockaddr*)&sa, &sa_len);
-    } while (len < 0 && (errno == EAGAIN || errno == EINTR));
-    if (len <= 0) {
-        PLOG(INFO) << "recvfrom() failed, len=" << len;
-        return;
+    ssize_t len = 0;
+    android::base::unique_fd tcpFd;
+    switch (protocol) {
+        case IPPROTO_UDP:
+            do {
+                len = recvfrom(udp_socket_.get(), buffer, sizeof(buffer), 0, (sockaddr*)&sa,
+                               &sa_len);
+            } while (len < 0 && (errno == EAGAIN || errno == EINTR));
+            if (len <= 0) {
+                PLOG(ERROR) << "recvfrom() failed, len=" << len;
+                return;
+            }
+            break;
+        case IPPROTO_TCP:
+            tcpFd.reset(accept4(tcp_socket_.get(), reinterpret_cast<sockaddr*>(&sa), &sa_len,
+                                SOCK_CLOEXEC));
+            if (tcpFd.get() < 0) {
+                PLOG(ERROR) << "failed to accept client socket";
+                return;
+            }
+            // Get the message length from two byte length field.
+            // See also RFC 1035, section 4.2.2 and RFC 7766, section 8
+            uint8_t queryMessageLengthField[2];
+            if (read(tcpFd.get(), &queryMessageLengthField, 2) != 2) {
+                PLOG(ERROR) << "Not enough length field bytes";
+                return;
+            }
+
+            const uint16_t qlen = (queryMessageLengthField[0] << 8) | queryMessageLengthField[1];
+            while (len < qlen) {
+                ssize_t ret = read(tcpFd.get(), buffer + len, qlen - len);
+                if (ret <= 0) {
+                    PLOG(ERROR) << "Error while reading query";
+                    return;
+                }
+                len += ret;
+            }
+            break;
     }
-    LOG(DEBUG) << "read " << len << " bytes";
+    LOG(DEBUG) << "read " << len << " bytes on " << dnsproto2str(protocol);
     std::lock_guard lock(cv_mutex_);
     char response[4096];
     size_t response_len = sizeof(response);
-    if (handleDNSRequest(buffer, len, response, &response_len) && response_len > 0) {
+    // TODO: check whether sending malformed packets to DnsResponder
+    if (handleDNSRequest(buffer, len, protocol, response, &response_len) && response_len > 0) {
         // place wait_for after handleDNSRequest() so we can check the number of queries in
         // test case before it got responded.
         std::unique_lock guard(cv_mutex_for_deferred_resp_);
         cv_for_deferred_resp_.wait(
                 guard, [this]() REQUIRES(cv_mutex_for_deferred_resp_) { return !deferred_resp_; });
+        len = 0;
 
-        len = sendto(socket_.get(), response, response_len, 0,
-                     reinterpret_cast<const sockaddr*>(&sa), sa_len);
-        std::string host_str = addr2str(reinterpret_cast<const sockaddr*>(&sa), sa_len);
+        switch (protocol) {
+            case IPPROTO_UDP:
+                len = sendto(udp_socket_.get(), response, response_len, 0,
+                             reinterpret_cast<const sockaddr*>(&sa), sa_len);
+                if (len < 0) {
+                    PLOG(ERROR) << "Failed to send response";
+                }
+                break;
+            case IPPROTO_TCP:
+                // Get the message length from two byte length field.
+                // See also RFC 1035, section 4.2.2 and RFC 7766, section 8
+                uint8_t responseMessageLengthField[2];
+                responseMessageLengthField[0] = response_len >> 8;
+                responseMessageLengthField[1] = response_len;
+                if (write(tcpFd.get(), responseMessageLengthField, 2) != 2) {
+                    PLOG(ERROR) << "Failed to write response length field";
+                    break;
+                }
+                if (write(tcpFd.get(), response, response_len) !=
+                    static_cast<ssize_t>(response_len)) {
+                    PLOG(ERROR) << "Failed to write response";
+                    break;
+                }
+                len = response_len;
+                break;
+        }
+        const std::string host_str = addr2str(reinterpret_cast<const sockaddr*>(&sa), sa_len);
         if (len > 0) {
             LOG(DEBUG) << "sent " << len << " bytes to " << host_str;
         } else {
-            PLOG(INFO) << "sendto() failed for " << host_str;
+            const char* method_str = (protocol == IPPROTO_TCP) ? "write()" : "sendto()";
+            LOG(ERROR) << method_str << " failed for " << host_str;
         }
         // Test that the response is actually a correct DNS message.
+        // TODO: Perhaps make DNS message validation to support name compression. Or it throws
+        // a warning for a valid DNS message with name compression while the binary packet mapping
+        // is used.
         const char* response_end = response + len;
         DNSHeader header;
         const char* cur = header.read(response, response_end);
@@ -972,4 +1169,42 @@
     }
 }
 
+android::base::unique_fd DNSResponder::createListeningSocket(int socket_type) {
+    addrinfo ai_hints{
+            .ai_flags = AI_PASSIVE,
+            .ai_family = AF_UNSPEC,
+            .ai_socktype = socket_type,
+    };
+    addrinfo* ai_res = nullptr;
+    const int rv =
+            getaddrinfo(listen_address_.c_str(), listen_service_.c_str(), &ai_hints, &ai_res);
+    ScopedAddrinfo ai_res_cleanup(ai_res);
+    if (rv) {
+        LOG(ERROR) << "getaddrinfo(" << listen_address_ << ", " << listen_service_
+                   << ") failed: " << gai_strerror(rv);
+        return {};
+    }
+    for (const addrinfo* ai = ai_res; ai; ai = ai->ai_next) {
+        android::base::unique_fd fd(
+                socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, ai->ai_protocol));
+        if (fd.get() < 0) {
+            PLOG(ERROR) << "ignore creating socket failed";
+            continue;
+        }
+        enableSockopt(fd.get(), SOL_SOCKET, SO_REUSEPORT).ignoreError();
+        enableSockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR).ignoreError();
+        const std::string host_str = addr2str(ai->ai_addr, ai->ai_addrlen);
+        const char* socket_str = (socket_type == SOCK_STREAM) ? "TCP" : "UDP";
+
+        if (bind(fd.get(), ai->ai_addr, ai->ai_addrlen)) {
+            PLOG(ERROR) << "failed to bind " << socket_str << " " << host_str << ":"
+                        << listen_service_;
+            continue;
+        }
+        LOG(INFO) << "bound to " << socket_str << " " << host_str << ":" << listen_service_;
+        return fd;
+    }
+    return {};
+}
+
 }  // namespace test
diff --git a/tests/dns_responder/dns_responder.h b/tests/dns_responder/dns_responder.h
index ed4b089..7382364 100644
--- a/tests/dns_responder/dns_responder.h
+++ b/tests/dns_responder/dns_responder.h
@@ -34,6 +34,10 @@
 // Default TTL of the DNS answer record.
 constexpr unsigned kAnswerRecordTtlSec = 5;
 
+// The maximum UDP response size in bytes the DNS responder allows to send. It is used in non-EDNS
+// case. See RFC 1035 section 4.2.1.
+constexpr unsigned kMaximumUdpSize = 512;
+
 namespace test {
 
 struct DNSName {
@@ -94,6 +98,7 @@
     std::vector<DNSRecord> additionals;
     const char* read(const char* buffer, const char* buffer_end);
     char* write(char* buffer, const char* buffer_end) const;
+    bool write(std::vector<uint8_t>* out) const;
     std::string toString() const;
 
   private:
@@ -122,20 +127,26 @@
  */
 class DNSResponder {
   public:
-    enum class Edns : uint8_t {
+    enum class Edns {
         ON,
         FORMERR_ON_EDNS,  // DNS server not supporting EDNS will reply FORMERR.
         FORMERR_UNCOND,   // DNS server reply FORMERR unconditionally
         DROP              // DNS server not supporting EDNS will not do any response.
     };
     // Indicate which mapping the DNS server used to build the response.
-    // See also addMapping(), addMappingDnsHeader(), removeMapping(), removeMappingDnsHeader(),
-    // makeResponse(), makeResponseFromDnsHeader().
+    // See also addMapping{, DnsHeader, BinaryPacket}, removeMapping{, DnsHeader, BinaryPacket},
+    // makeResponse{, FromDnsHeader, FromBinaryPacket}.
     // TODO: Perhaps break class DNSResponder for each mapping.
-    // TODO: Add the mapping from (raw dns query) to (raw dns response).
-    enum class MappingType : uint8_t {
+    enum class MappingType {
         ADDRESS_OR_HOSTNAME,  // Use the mapping from (name, type) to (address or hostname)
         DNS_HEADER,           // Use the mapping from (name, type) to (DNSHeader)
+        BINARY_PACKET,        // Use the mapping from (query packet) to (response packet)
+    };
+
+    struct QueryInfo {
+        std::string name;
+        ns_type type;
+        int protocol;  // Either IPPROTO_TCP or IPPROTO_UDP
     };
 
     DNSResponder(std::string listen_address = kDefaultListenAddr,
@@ -152,26 +163,33 @@
 
     ~DNSResponder();
 
-    // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}.
+    // Functions used for accessing mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER, BINARY_PACKET}.
     void addMapping(const std::string& name, ns_type type, const std::string& addr);
     void addMappingDnsHeader(const std::string& name, ns_type type, const DNSHeader& header);
+    void addMappingBinaryPacket(const std::vector<uint8_t>& query,
+                                const std::vector<uint8_t>& response);
     void removeMapping(const std::string& name, ns_type type);
     void removeMappingDnsHeader(const std::string& name, ns_type type);
+    void removeMappingBinaryPacket(const std::vector<uint8_t>& query);
 
     void setResponseProbability(double response_probability);
+    void setResponseProbability(double response_probability, int protocol);
     void setEdns(Edns edns);
     bool running() const;
     bool startServer();
     bool stopServer();
     const std::string& listen_address() const { return listen_address_; }
     const std::string& listen_service() const { return listen_service_; }
-    std::vector<std::pair<std::string, ns_type>> queries() const;
+    std::vector<QueryInfo> queries() const;
     std::string dumpQueries() const;
     void clearQueries();
     std::condition_variable& getCv() { return cv; }
     std::mutex& getCvMutex() { return cv_mutex_; }
     void setDeferredResp(bool deferred_resp);
-    static bool fillAnswerRdata(const std::string& rdatastr, DNSRecord& record);
+    static bool fillRdata(const std::string& rdatastr, DNSRecord& record);
+
+    // TODO: Make DNSResponder record unknown queries in a vector for improving the debugging.
+    // Unit test could dump the unexpected query for further debug if any unexpected failure.
 
   private:
     // Key used for accessing mappings.
@@ -194,32 +212,61 @@
         }
     };
 
+    // Used for generating combined hash value of a vector.
+    // std::hash<T> doesn't provide a specialization for std::vector<T>.
+    struct QueryKeyVectorHash {
+        std::size_t operator()(const std::vector<uint8_t>& v) const {
+            std::size_t combined = 0;
+            for (const uint8_t i : v) {
+                // Hash combination comes from boost::hash_combine
+                // See also system/extras/simpleperf/utils.h
+                combined ^=
+                        std::hash<uint8_t>{}(i) + 0x9e3779b9 + (combined << 6) + (combined >> 2);
+            }
+            return combined;
+        }
+    };
+
     void requestHandler();
 
+    // Check if any OPT Pseudo RR in the additional section.
+    bool hasOptPseudoRR(DNSHeader* header) const;
+
     // Parses and generates a response message for incoming DNS requests.
     // Returns false to ignore the request, which might be due to either parsing error
     // or unresponsiveness.
-    bool handleDNSRequest(const char* buffer, ssize_t buffer_len, char* response,
+    bool handleDNSRequest(const char* buffer, ssize_t buffer_len, int protocol, char* response,
                           size_t* response_len) const;
 
     bool addAnswerRecords(const DNSQuestion& question, std::vector<DNSRecord>* answers) const;
 
     bool generateErrorResponse(DNSHeader* header, ns_rcode rcode, char* response,
                                size_t* response_len) const;
-    // TODO: Change makeErrorResponse and makeResponse{, FromDnsHeader} to use C++ containers
+
+    // TODO: Change writePacket, makeErrorResponse, makeTruncatedResponse and
+    // makeResponse{, FromAddressOrHostname, FromDnsHeader, FromBinaryPacket} to use C++ containers
     // instead of the unsafe pointer + length buffer.
+    bool writePacket(const DNSHeader* header, char* response, size_t* response_len) const;
+    // Build an error response with a given rcode.
     bool makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response,
                            size_t* response_len) const;
-    // Build a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER}.
-    bool makeResponse(DNSHeader* header, char* response, size_t* response_len) const;
+    // Build a truncated response.
+    bool makeTruncatedResponse(DNSHeader* header, char* response, size_t* response_len) const;
+    // Build a response.
+    bool makeResponse(DNSHeader* header, int protocol, char* response, size_t* response_len) const;
+    // Helper for building a response from mapping {ADDRESS_OR_HOSTNAME, DNS_HEADER, BINARY_PACKET}.
+    bool makeResponseFromAddressOrHostname(DNSHeader* header, char* response,
+                                           size_t* response_len) const;
     bool makeResponseFromDnsHeader(DNSHeader* header, char* response, size_t* response_len) const;
+    bool makeResponseFromBinaryPacket(DNSHeader* header, char* response,
+                                      size_t* response_len) const;
 
     // Add a new file descriptor to be polled by the handler thread.
     bool addFd(int fd, uint32_t events);
 
     // Read the query sent from the client and send the answer back to the client. It
     // makes sure the I/O communicated with the client is correct.
-    void handleQuery();
+    void handleQuery(int protocol);
 
     // Trigger the handler thread to terminate.
     bool sendToEventFd();
@@ -227,16 +274,25 @@
     // Used in the handler thread for the termination signal.
     void handleEventFd();
 
-    // Address and service to listen on, currently limited to UDP.
+    // TODO: Move createListeningSocket to resolv_test_utils.h
+    android::base::unique_fd createListeningSocket(int socket_type);
+
+    double getResponseProbability(int protocol) const;
+
+    // Address and service to listen on TCP and UDP.
     const std::string listen_address_;
     const std::string listen_service_;
     // Error code to return for requests for an unknown name.
     const ns_rcode error_rcode_;
     // Mapping type the DNS server used to build the response.
     const MappingType mapping_type_;
-    // Probability that a valid response is being sent instead of being sent
-    // instead of returning error_rcode_.
-    std::atomic<double> response_probability_ = 1.0;
+    // Probability that a valid response on TCP is being sent instead of
+    // returning error_rcode_ or no response.
+    std::atomic<double> response_probability_tcp_ = 1.0;
+    // Probability that a valid response on UDP is being sent instead of
+    // returning error_rcode_ or no response.
+    std::atomic<double> response_probability_udp_ = 1.0;
+
     // Maximum number of fds for epoll.
     const int EPOLL_MAX_EVENTS = 2;
 
@@ -251,16 +307,20 @@
     // decides which mapping is used. See also makeResponse{, FromDnsHeader}.
     // - mappings_: Mapping from (name, type) to (address or hostname).
     // - dnsheader_mappings_: Mapping from (name, type) to (DNSHeader).
+    // - packet_mappings_: Mapping from (query packet) to (response packet).
     std::unordered_map<QueryKey, std::string, QueryKeyHash> mappings_ GUARDED_BY(mappings_mutex_);
     std::unordered_map<QueryKey, DNSHeader, QueryKeyHash> dnsheader_mappings_
             GUARDED_BY(mappings_mutex_);
+    std::unordered_map<std::vector<uint8_t>, std::vector<uint8_t>, QueryKeyVectorHash>
+            packet_mappings_ GUARDED_BY(mappings_mutex_);
 
     mutable std::mutex mappings_mutex_;
     // Query names received so far and the corresponding mutex.
-    mutable std::vector<std::pair<std::string, ns_type>> queries_ GUARDED_BY(queries_mutex_);
+    mutable std::vector<QueryInfo> queries_ GUARDED_BY(queries_mutex_);
     mutable std::mutex queries_mutex_;
     // Socket on which the server is listening.
-    android::base::unique_fd socket_;
+    android::base::unique_fd udp_socket_;
+    android::base::unique_fd tcp_socket_;
     // File descriptor for epoll.
     android::base::unique_fd epoll_fd_;
     // Eventfd used to signal for the handler thread termination.
diff --git a/tests/dns_responder/dns_responder_client.cpp b/tests/dns_responder/dns_responder_client.cpp
index 473bfb5..2b24811 100644
--- a/tests/dns_responder/dns_responder_client.cpp
+++ b/tests/dns_responder/dns_responder_client.cpp
@@ -35,38 +35,17 @@
 using android::net::INetd;
 using android::net::ResolverParamsParcel;
 
-static const char kCaCert[] = R"(
------BEGIN CERTIFICATE-----
-MIIC4TCCAcmgAwIBAgIUQUHZnWhL6M4qcS+I0lLkMyqf3VMwDQYJKoZIhvcNAQEL
-BQAwADAeFw0xOTA2MTAwODM3MzlaFw0yOTA2MDcwODM3MzlaMAAwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCapRbBg6dRT4id4DxmlyktomE8gpm4W+VA
-ZOivhKat4CvGfVjVIAUYxV7LOGREkkT8Qhn5/gU0lShsnURzEDWY+IjMDDw+kRAm
-iFAlMRnCobTp/tenseNRB2tDuUhkRbzaT6qaidPbKy099p909gxf4YqsgY2NfsY2
-JkideqIkVq2hvLehsu3BgiK06TGUgxOHfj74vx7iGyujq1v38J1hlox5vj/svJF6
-jVdDw8p2UkJbO2N9u3al0nNSMG+MCgd3cvKUySTnjedYXsYB0WyH/JZn//KDq6o+
-as6eQVHuH1fu+8XPzBNATlkHzy+YAs7T+UWbkoa1F8wIElVQg66lAgMBAAGjUzBR
-MB0GA1UdDgQWBBShu/e54D3VdqdLOWo9Ou5hbjaIojAfBgNVHSMEGDAWgBShu/e5
-4D3VdqdLOWo9Ou5hbjaIojAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
-A4IBAQBFkEGqqzzdQlhP5m1kzh+SiUCwekzSump0CSk0JAXAdeLNbWs3H+pE1/hM
-Fx7oFonoX5O6pi68JfcIP0u7wNuZkKqubUl4gG6aHDkAad2oeTov0Be7EKt8Ekwf
-tmFWVQQrF9otlG3Stn4vmE5zVNGQXDgRsNKPekSo0XJz757q5WgblauB71Rekvio
-TCUXXt3jf3SuovcUFjgBkaohikBRbLiPWZrW4y0XUsgBKI6sLtiSZOPiNevY2xAR
-y7mCSmi4wP7vtUQ5G8znkAMKoo0FzyfjSogGQeREUM8Oe9Mmh/D39sq/D4TsiAxt
-Pwl59DlzlHHJhmOL+SCGciBX4X7p
------END CERTIFICATE-----
-)";
-
-void DnsResponderClient::SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+void DnsResponderClient::SetupMappings(unsigned numHosts, const std::vector<std::string>& domains,
                                        std::vector<Mapping>* mappings) {
-    mappings->resize(num_hosts * domains.size());
-    auto mappings_it = mappings->begin();
-    for (unsigned i = 0; i < num_hosts; ++i) {
+    mappings->resize(numHosts * domains.size());
+    auto mappingsIt = mappings->begin();
+    for (unsigned i = 0; i < numHosts; ++i) {
         for (const auto& domain : domains) {
-            mappings_it->host = StringPrintf("host%u", i);
-            mappings_it->entry = StringPrintf("%s.%s.", mappings_it->host.c_str(), domain.c_str());
-            mappings_it->ip4 = StringPrintf("192.0.2.%u", i % 253 + 1);
-            mappings_it->ip6 = StringPrintf("2001:db8::%x", i % 65534 + 1);
-            ++mappings_it;
+            mappingsIt->host = StringPrintf("host%u", i);
+            mappingsIt->entry = StringPrintf("%s.%s.", mappingsIt->host.c_str(), domain.c_str());
+            mappingsIt->ip4 = StringPrintf("192.0.2.%u", i % 253 + 1);
+            mappingsIt->ip6 = StringPrintf("2001:db8::%x", i % 65534 + 1);
+            ++mappingsIt;
         }
     }
 }
@@ -103,7 +82,6 @@
     paramsParcel.tlsServers = tlsServers;
     paramsParcel.tlsFingerprints = {};
     paramsParcel.caCertificate = caCert;
-    paramsParcel.tlsConnectTimeoutMs = 1000;
 
     return paramsParcel;
 }
@@ -129,17 +107,29 @@
     return rv.isOk();
 }
 
-void DnsResponderClient::SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+bool DnsResponderClient::SetResolversFromParcel(const ResolverParamsParcel& resolverParams) {
+    const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+    if (!rv.isOk()) LOG(ERROR) << "SetResolversFromParcel() -> " << rv.toString8();
+    return rv.isOk();
+}
+
+ResolverParamsParcel DnsResponderClient::GetDefaultResolverParamsParcel() {
+    return makeResolverParamsParcel(TEST_NETID, kDefaultParams, kDefaultServers,
+                                    kDefaultSearchDomains, {} /* tlsHostname */, kDefaultServers,
+                                    kCaCert);
+}
+
+void DnsResponderClient::SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
                                          std::vector<std::unique_ptr<test::DNSResponder>>* dns,
                                          std::vector<std::string>* servers) {
-    const char* listen_srv = "53";
-    dns->resize(num_servers);
-    servers->resize(num_servers);
-    for (unsigned i = 0; i < num_servers; ++i) {
+    const char* listenSrv = "53";
+    dns->resize(numServers);
+    servers->resize(numServers);
+    for (unsigned i = 0; i < numServers; ++i) {
         auto& server = (*servers)[i];
         auto& d = (*dns)[i];
         server = StringPrintf("127.0.0.%u", i + 100);
-        d = std::make_unique<test::DNSResponder>(server, listen_srv, ns_rcode::ns_r_servfail);
+        d = std::make_unique<test::DNSResponder>(server, listenSrv, ns_rcode::ns_r_servfail);
         for (const auto& mapping : mappings) {
             d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
             d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
diff --git a/tests/dns_responder/dns_responder_client.h b/tests/dns_responder/dns_responder_client.h
index 0b2966b..93ced99 100644
--- a/tests/dns_responder/dns_responder_client.h
+++ b/tests/dns_responder/dns_responder_client.h
@@ -25,6 +25,7 @@
 #include "android/net/IDnsResolver.h"
 #include "android/net/INetd.h"
 #include "dns_responder.h"
+#include "dns_tls_certificate.h"
 
 inline const std::vector<std::string> kDefaultServers = {"127.0.0.3"};
 inline const std::vector<std::string> kDefaultSearchDomains = {"example.com"};
@@ -36,6 +37,8 @@
         2,        // retry count
 };
 
+// TODO: Replace binder service related code to ndk version after finishing all tests migration
+// from libbinder to libbinder_ndk.
 class DnsResponderClient {
   public:
     struct Mapping {
@@ -50,14 +53,12 @@
     static void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
                               std::vector<Mapping>* mappings);
 
+    // This function is deprecated. Please use SetResolversFromParcel() instead.
     bool SetResolversForNetwork(const std::vector<std::string>& servers = kDefaultServers,
                                 const std::vector<std::string>& domains = kDefaultSearchDomains,
                                 const std::vector<int>& params = kDefaultParams);
 
-    bool SetResolversForNetwork(const std::vector<std::string>& servers,
-                                const std::vector<std::string>& searchDomains,
-                                const std::string& params);
-
+    // This function is deprecated. Please use SetResolversFromParcel() instead.
     bool SetResolversWithTls(const std::vector<std::string>& servers,
                              const std::vector<std::string>& searchDomains,
                              const std::vector<int>& params, const std::string& name) {
@@ -66,12 +67,18 @@
         return SetResolversWithTls(servers, searchDomains, params, servers, name);
     }
 
+    // This function is deprecated. Please use SetResolversFromParcel() instead.
     bool SetResolversWithTls(const std::vector<std::string>& servers,
                              const std::vector<std::string>& searchDomains,
                              const std::vector<int>& params,
                              const std::vector<std::string>& tlsServers, const std::string& name);
 
-    static void SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
+    bool SetResolversFromParcel(const android::net::ResolverParamsParcel& resolverParams);
+
+    // Return a default resolver configuration for opportunistic mode.
+    static android::net::ResolverParamsParcel GetDefaultResolverParamsParcel();
+
+    static void SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
                                 std::vector<std::unique_ptr<test::DNSResponder>>* dns,
                                 std::vector<std::string>* servers);
 
diff --git a/tests/dns_responder/dns_responder_client_ndk.cpp b/tests/dns_responder/dns_responder_client_ndk.cpp
new file mode 100644
index 0000000..12152cc
--- /dev/null
+++ b/tests/dns_responder/dns_responder_client_ndk.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dns_responder_client"
+
+#include "dns_responder_client_ndk.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <android/binder_manager.h>
+#include "NetdClient.h"
+
+// TODO: make this dynamic and stop depending on implementation details.
+#define TEST_OEM_NETWORK "oem29"
+#define TEST_NETID 30
+
+// TODO: move this somewhere shared.
+static const char* ANDROID_DNS_MODE = "ANDROID_DNS_MODE";
+
+using aidl::android::net::IDnsResolver;
+using aidl::android::net::INetd;
+using aidl::android::net::ResolverParamsParcel;
+using android::base::StringPrintf;
+using android::net::ResolverStats;
+
+void DnsResponderClient::SetupMappings(unsigned numHosts, const std::vector<std::string>& domains,
+                                       std::vector<Mapping>* mappings) {
+    mappings->resize(numHosts * domains.size());
+    auto mappingsIt = mappings->begin();
+    for (unsigned i = 0; i < numHosts; ++i) {
+        for (const auto& domain : domains) {
+            mappingsIt->host = StringPrintf("host%u", i);
+            mappingsIt->entry = StringPrintf("%s.%s.", mappingsIt->host.c_str(), domain.c_str());
+            mappingsIt->ip4 = StringPrintf("192.0.2.%u", i % 253 + 1);
+            mappingsIt->ip6 = StringPrintf("2001:db8::%x", i % 65534 + 1);
+            ++mappingsIt;
+        }
+    }
+}
+
+// TODO: Use SetResolverConfiguration() with ResolverParamsParcel struct directly.
+// DEPRECATED: Use SetResolverConfiguration() in new code
+ResolverParamsParcel DnsResponderClient::makeResolverParamsParcel(
+        int netId, const std::vector<int>& params, const std::vector<std::string>& servers,
+        const std::vector<std::string>& domains, const std::string& tlsHostname,
+        const std::vector<std::string>& tlsServers, const std::string& caCert) {
+    ResolverParamsParcel paramsParcel;
+
+    paramsParcel.netId = netId;
+    paramsParcel.sampleValiditySeconds = params[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY];
+    paramsParcel.successThreshold = params[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD];
+    paramsParcel.minSamples = params[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES];
+    paramsParcel.maxSamples = params[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES];
+    if (params.size() > IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC) {
+        paramsParcel.baseTimeoutMsec = params[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC];
+    } else {
+        paramsParcel.baseTimeoutMsec = 0;
+    }
+    if (params.size() > IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT) {
+        paramsParcel.retryCount = params[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT];
+    } else {
+        paramsParcel.retryCount = 0;
+    }
+    paramsParcel.servers = servers;
+    paramsParcel.domains = domains;
+    paramsParcel.tlsName = tlsHostname;
+    paramsParcel.tlsServers = tlsServers;
+    paramsParcel.tlsFingerprints = {};
+    paramsParcel.caCertificate = caCert;
+
+    // Note, do not remove this otherwise the ResolverTest#ConnectTlsServerTimeout won't pass in M4
+    // module.
+    // TODO: remove after 2020-01 rolls out.
+    paramsParcel.tlsConnectTimeoutMs = 1000;
+
+    return paramsParcel;
+}
+
+bool DnsResponderClient::GetResolverInfo(aidl::android::net::IDnsResolver* dnsResolverService,
+                                         unsigned netId, std::vector<std::string>* servers,
+                                         std::vector<std::string>* domains,
+                                         std::vector<std::string>* tlsServers, res_params* params,
+                                         std::vector<ResolverStats>* stats,
+                                         int* waitForPendingReqTimeoutCount) {
+    using aidl::android::net::IDnsResolver;
+    std::vector<int32_t> params32;
+    std::vector<int32_t> stats32;
+    std::vector<int32_t> waitForPendingReqTimeoutCount32{0};
+    auto rv = dnsResolverService->getResolverInfo(netId, servers, domains, tlsServers, &params32,
+                                                  &stats32, &waitForPendingReqTimeoutCount32);
+
+    if (!rv.isOk() || params32.size() != static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT)) {
+        return false;
+    }
+    *params = res_params{
+            .sample_validity =
+                    static_cast<uint16_t>(params32[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY]),
+            .success_threshold =
+                    static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD]),
+            .min_samples =
+                    static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES]),
+            .max_samples =
+                    static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES]),
+            .base_timeout_msec = params32[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
+            .retry_count = params32[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT],
+    };
+    *waitForPendingReqTimeoutCount = waitForPendingReqTimeoutCount32[0];
+    return ResolverStats::decodeAll(stats32, stats);
+}
+
+bool DnsResponderClient::isRemoteVersionSupported(
+        aidl::android::net::IDnsResolver* dnsResolverService, int requiredVersion) {
+    int remoteVersion = 0;
+    if (!dnsResolverService->getInterfaceVersion(&remoteVersion).isOk()) {
+        LOG(FATAL) << "Can't get 'dnsresolver' remote version";
+    }
+    if (remoteVersion < requiredVersion) {
+        LOG(WARNING) << StringPrintf("Remote version: %d < Required version: %d", remoteVersion,
+                                     requiredVersion);
+        return false;
+    }
+    return true;
+}
+
+bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& servers,
+                                                const std::vector<std::string>& domains,
+                                                const std::vector<int>& params) {
+    const auto& resolverParams =
+            makeResolverParamsParcel(TEST_NETID, params, servers, domains, "", {}, "");
+    const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+    return rv.isOk();
+}
+
+bool DnsResponderClient::SetResolversWithTls(const std::vector<std::string>& servers,
+                                             const std::vector<std::string>& domains,
+                                             const std::vector<int>& params,
+                                             const std::vector<std::string>& tlsServers,
+                                             const std::string& name) {
+    const auto& resolverParams = makeResolverParamsParcel(TEST_NETID, params, servers, domains,
+                                                          name, tlsServers, kCaCert);
+    const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+    if (!rv.isOk()) LOG(ERROR) << "SetResolversWithTls() -> " << rv.getMessage();
+    return rv.isOk();
+}
+
+bool DnsResponderClient::SetResolversFromParcel(const ResolverParamsParcel& resolverParams) {
+    const auto rv = mDnsResolvSrv->setResolverConfiguration(resolverParams);
+    if (!rv.isOk()) LOG(ERROR) << "SetResolversFromParcel() -> " << rv.getMessage();
+    return rv.isOk();
+}
+
+ResolverParamsParcel DnsResponderClient::GetDefaultResolverParamsParcel() {
+    return makeResolverParamsParcel(TEST_NETID, kDefaultParams, kDefaultServers,
+                                    kDefaultSearchDomains, {} /* tlsHostname */, kDefaultServers,
+                                    kCaCert);
+}
+
+void DnsResponderClient::SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
+                                         std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+                                         std::vector<std::string>* servers) {
+    const char* listenSrv = "53";
+    dns->resize(numServers);
+    servers->resize(numServers);
+    for (unsigned i = 0; i < numServers; ++i) {
+        auto& server = (*servers)[i];
+        auto& d = (*dns)[i];
+        server = StringPrintf("127.0.0.%u", i + 100);
+        d = std::make_unique<test::DNSResponder>(server, listenSrv, ns_rcode::ns_r_servfail);
+        for (const auto& mapping : mappings) {
+            d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
+            d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
+        }
+        d->startServer();
+    }
+}
+
+int DnsResponderClient::SetupOemNetwork() {
+    mNetdSrv->networkDestroy(TEST_NETID);
+    mDnsResolvSrv->destroyNetworkCache(TEST_NETID);
+    auto ret = mNetdSrv->networkCreatePhysical(TEST_NETID, INetd::PERMISSION_NONE);
+    if (!ret.isOk()) {
+        fprintf(stderr, "Creating physical network %d failed, %s\n", TEST_NETID, ret.getMessage());
+        return -1;
+    }
+    ret = mDnsResolvSrv->createNetworkCache(TEST_NETID);
+    if (!ret.isOk()) {
+        fprintf(stderr, "Creating network cache %d failed, %s\n", TEST_NETID, ret.getMessage());
+        return -1;
+    }
+    setNetworkForProcess(TEST_NETID);
+    if ((unsigned)TEST_NETID != getNetworkForProcess()) {
+        return -1;
+    }
+    return TEST_NETID;
+}
+
+void DnsResponderClient::TearDownOemNetwork(int oemNetId) {
+    if (oemNetId != -1) {
+        mNetdSrv->networkDestroy(oemNetId);
+        mDnsResolvSrv->destroyNetworkCache(oemNetId);
+    }
+}
+
+void DnsResponderClient::SetUp() {
+    // binder setup
+    ndk::SpAIBinder netdBinder = ndk::SpAIBinder(AServiceManager_getService("netd"));
+    mNetdSrv = INetd::fromBinder(netdBinder);
+    if (mNetdSrv.get() == nullptr) {
+        LOG(FATAL) << "Can't connect to service 'netd'. Missing root privileges? uid=" << getuid();
+    }
+
+    ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(AServiceManager_getService("dnsresolver"));
+    mDnsResolvSrv = IDnsResolver::fromBinder(resolvBinder);
+    if (mDnsResolvSrv.get() == nullptr) {
+        LOG(FATAL) << "Can't connect to service 'dnsresolver'. Missing root privileges? uid="
+                   << getuid();
+    }
+
+    // Ensure resolutions go via proxy.
+    setenv(ANDROID_DNS_MODE, "", 1);
+    mOemNetId = SetupOemNetwork();
+}
+
+void DnsResponderClient::TearDown() {
+    TearDownOemNetwork(mOemNetId);
+}
diff --git a/tests/dns_responder/dns_responder_client_ndk.h b/tests/dns_responder/dns_responder_client_ndk.h
new file mode 100644
index 0000000..092d414
--- /dev/null
+++ b/tests/dns_responder/dns_responder_client_ndk.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <aidl/android/net/IDnsResolver.h>
+#include <aidl/android/net/INetd.h>
+#include "ResolverStats.h"  // TODO: stop depending on this internal header
+#include "dns_responder.h"
+#include "dns_tls_certificate.h"
+#include "params.h"
+
+inline const std::vector<std::string> kDefaultServers = {"127.0.0.3"};
+inline const std::vector<std::string> kDefaultSearchDomains = {"example.com"};
+inline const std::vector<int> kDefaultParams = {
+        300,      // sample validity in seconds
+        25,       // success threshod in percent
+        8,    8,  // {MIN,MAX}_SAMPLES
+        1000,     // BASE_TIMEOUT_MSEC
+        2,        // retry count
+};
+
+#define SKIP_IF_REMOTE_VERSION_LESS_THAN(service, version)                                         \
+    do {                                                                                           \
+        if (!DnsResponderClient::isRemoteVersionSupported(service, version)) {                     \
+            std::cerr << "    Skip test. Remote version is too old, required version: " << version \
+                      << std::endl;                                                                \
+            return;                                                                                \
+        }                                                                                          \
+    } while (0)
+
+// TODO: Remove dns_responder_client_ndk.{h,cpp} after replacing the binder usage of
+// dns_responder_client.*
+class DnsResponderClient {
+  public:
+    struct Mapping {
+        std::string host;
+        std::string entry;
+        std::string ip4;
+        std::string ip6;
+    };
+
+    virtual ~DnsResponderClient() = default;
+
+    static void SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
+                              std::vector<Mapping>* mappings);
+
+    // This function is deprecated. Please use SetResolversFromParcel() instead.
+    bool SetResolversForNetwork(const std::vector<std::string>& servers = kDefaultServers,
+                                const std::vector<std::string>& domains = kDefaultSearchDomains,
+                                const std::vector<int>& params = kDefaultParams);
+
+    // This function is deprecated. Please use SetResolversFromParcel() instead.
+    bool SetResolversWithTls(const std::vector<std::string>& servers,
+                             const std::vector<std::string>& searchDomains,
+                             const std::vector<int>& params, const std::string& name) {
+        // Pass servers as both network-assigned and TLS servers.  Tests can
+        // determine on which server and by which protocol queries arrived.
+        return SetResolversWithTls(servers, searchDomains, params, servers, name);
+    }
+
+    // This function is deprecated. Please use SetResolversFromParcel() instead.
+    bool SetResolversWithTls(const std::vector<std::string>& servers,
+                             const std::vector<std::string>& searchDomains,
+                             const std::vector<int>& params,
+                             const std::vector<std::string>& tlsServers, const std::string& name);
+
+    bool SetResolversFromParcel(const aidl::android::net::ResolverParamsParcel& resolverParams);
+
+    static bool isRemoteVersionSupported(aidl::android::net::IDnsResolver* dnsResolverService,
+                                         int enabledVersion);
+
+    static bool GetResolverInfo(aidl::android::net::IDnsResolver* dnsResolverService,
+                                unsigned netId, std::vector<std::string>* servers,
+                                std::vector<std::string>* domains,
+                                std::vector<std::string>* tlsServers, res_params* params,
+                                std::vector<android::net::ResolverStats>* stats,
+                                int* waitForPendingReqTimeoutCount);
+
+    // Return a default resolver configuration for opportunistic mode.
+    static aidl::android::net::ResolverParamsParcel GetDefaultResolverParamsParcel();
+
+    static void SetupDNSServers(unsigned numServers, const std::vector<Mapping>& mappings,
+                                std::vector<std::unique_ptr<test::DNSResponder>>* dns,
+                                std::vector<std::string>* servers);
+
+    static aidl::android::net::ResolverParamsParcel makeResolverParamsParcel(
+            int netId, const std::vector<int>& params, const std::vector<std::string>& servers,
+            const std::vector<std::string>& domains, const std::string& tlsHostname,
+            const std::vector<std::string>& tlsServers, const std::string& caCert = "");
+
+    int SetupOemNetwork();
+
+    void TearDownOemNetwork(int oemNetId);
+
+    virtual void SetUp();
+    virtual void TearDown();
+
+    aidl::android::net::IDnsResolver* resolvService() const { return mDnsResolvSrv.get(); }
+    aidl::android::net::INetd* netdService() const { return mNetdSrv.get(); }
+
+  private:
+    std::shared_ptr<aidl::android::net::INetd> mNetdSrv;
+    std::shared_ptr<aidl::android::net::IDnsResolver> mDnsResolvSrv;
+    int mOemNetId = -1;
+};
diff --git a/tests/dns_responder/dns_tls_certificate.h b/tests/dns_responder/dns_tls_certificate.h
new file mode 100644
index 0000000..79a4b0f
--- /dev/null
+++ b/tests/dns_responder/dns_tls_certificate.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+// Currently the hostname of TLS server must match the CN filed on the server's certificate.
+// Inject a test CA whose hostname is "example.com" for DNS-OVER-TLS tests.
+static constexpr char kDefaultPrivateDnsHostName[] = "example.com";
+static constexpr char kDefaultIncorrectPrivateDnsHostName[] = "www.example.com";
+
+/*
+ * test cert, key, and rootca files can be generated using openssl with
+ * the following steps:
+ *
+ * 1. Create CA certificate:
+ * $ openssl genrsa 2048 > ca_key.pem
+ * $ openssl req -new -sha256 -x509 -nodes -days 3650 -key ca_key.pem -out ca_certificate.pem -subj
+ * '/C=/ST=/L=/CN=/emailAddress='
+ *
+ * 2. Create private key:
+ * $ openssl req -sha256 -newkey rsa:2048 -days 3650 -nodes -keyout server_key.pem -out
+ * server_req.pem -subj '/C=/ST=/L=/CN=example.com/emailAddress='
+ * $ openssl rsa -in server_key.pem -out server_key.pem
+ *
+ * 3. Create server certificate:
+ * $ openssl x509 -sha256 -req -in server_req.pem -days 3650 -CA
+ * ca_certificate.pem -CAkey ca_key.pem -set_serial 01 -out server_certificate.pem
+ *
+ * 4. Verify the certificate:
+ * $ openssl verify -CAfile ca_certificate.pem server_certificate.pem
+ */
+// ca_certificate.pem
+static constexpr char kCaCert[] = R"(
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcmgAwIBAgIUQUHZnWhL6M4qcS+I0lLkMyqf3VMwDQYJKoZIhvcNAQEL
+BQAwADAeFw0xOTA2MTAwODM3MzlaFw0yOTA2MDcwODM3MzlaMAAwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCapRbBg6dRT4id4DxmlyktomE8gpm4W+VA
+ZOivhKat4CvGfVjVIAUYxV7LOGREkkT8Qhn5/gU0lShsnURzEDWY+IjMDDw+kRAm
+iFAlMRnCobTp/tenseNRB2tDuUhkRbzaT6qaidPbKy099p909gxf4YqsgY2NfsY2
+JkideqIkVq2hvLehsu3BgiK06TGUgxOHfj74vx7iGyujq1v38J1hlox5vj/svJF6
+jVdDw8p2UkJbO2N9u3al0nNSMG+MCgd3cvKUySTnjedYXsYB0WyH/JZn//KDq6o+
+as6eQVHuH1fu+8XPzBNATlkHzy+YAs7T+UWbkoa1F8wIElVQg66lAgMBAAGjUzBR
+MB0GA1UdDgQWBBShu/e54D3VdqdLOWo9Ou5hbjaIojAfBgNVHSMEGDAWgBShu/e5
+4D3VdqdLOWo9Ou5hbjaIojAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4IBAQBFkEGqqzzdQlhP5m1kzh+SiUCwekzSump0CSk0JAXAdeLNbWs3H+pE1/hM
+Fx7oFonoX5O6pi68JfcIP0u7wNuZkKqubUl4gG6aHDkAad2oeTov0Be7EKt8Ekwf
+tmFWVQQrF9otlG3Stn4vmE5zVNGQXDgRsNKPekSo0XJz757q5WgblauB71Rekvio
+TCUXXt3jf3SuovcUFjgBkaohikBRbLiPWZrW4y0XUsgBKI6sLtiSZOPiNevY2xAR
+y7mCSmi4wP7vtUQ5G8znkAMKoo0FzyfjSogGQeREUM8Oe9Mmh/D39sq/D4TsiAxt
+Pwl59DlzlHHJhmOL+SCGciBX4X7p
+-----END CERTIFICATE-----
+)";
+
+// server_key.pem
+static constexpr char kPrivatekey[] = R"(
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4FX+j8yyohxS8
+OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIqnEDbxvcWBrv7
+NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7gu1gn+CnaDYu
+pAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8jhBqhYbQK7ZFL
+mIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOEbAI1QF+tEn1b
+naLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABAoIBACDLLF9wumviLYH6
+9g3IoZMEFpGgo+dEbAEnxnQA+9DDCNy1yGCaJ+8n2ZhwJboLkXAFwWXh07HGq3mQ
+AMo2I7ZPzzkWxVJqaubwCo1s2TUgOb71TDLgZLdJxwnmVRHfS650L3/7gC9yZxON
+RSiWTLVSb5gziLMJ1PD8E/nvwAxaJDlT6vzqwRbnHBkQoumTmds2ecLJd2/6pfl4
+bMhtIKA3ULqnJlqlRt6ds/pWU9ttmXEX52uaGhzaF7PRomOW5pKR6CyBzNCn/RNF
+ZPIINW1TVWss9NMZsJLdIzs7Oon5gQYil9rU2uiA5ZUanYDIL9DOMrfAM3hfUuFq
+ZOhfBAECgYEA36CT81EkdDE7pum/kIuCG3wDEls+xNbWmF76IJAxnolJzKvJsdJA
+0za/l1Qe3/bRYHZWKc7by45LFYefOsS29jqBnBBMLurI7OLtcXqkTSSm11AfsDDI
+gw4bKs81TYdHhnbIDGeApfSWOGXgDM/j4N3stuvY1lOIocXqKMomZVMCgYEA1ZHD
+jtxeAmCqzJHIJ4DOY0Y2RR3Bq3ue/mc8gmV9wDyJMMvBpvOoRkUjwbKZToi4Px30
+5fn6SCRtOKfEL0b9LV7JFsMr84Zoj8kjtnE0BdNfQqdE/uWltpATl6YUPlzqZTGs
+HBGVpsNCzYkjFu9m/zIiryCHY8Uut3VEZmNJjTsCgYEAgADBTzAuBpg7xeHcdhd0
+xNiqRXKXLkKvGQ6ca9E9pbp91LqsO63Wz09yQWO0PIxh8q4pycqPQyfS0KMNwKzi
+8XQxxiwJ/30Cv51xPlht/X4yReKmEMsLqwCDCnEK2LLLfSs2fOst11B2QBgINC03
+CfrdySKcvqmX9sl7rBdx/OMCgYB9t2o4RDwKhkDEXuRFbKsRARmdIeEJQqHa+4ZA
+8+FMMdZIJQj/b9qUUsqzkKBx/EUI0meAoN/Va6vnd8oiUlViSbNxdL4AghQ2353o
+HUcUTtJ6d+BDc4dSqgj+ccLk2ukXXGAFvcwr+DDwsFM5gv9MJYUJNcq8ziurzpnO
+848uVQKBgEmyAa2jt1qNpAvxU0MakJIuKhQl2b6/54EKi9WKqIMs1+rKk6O/Ck3n
++tEWqHhZ4uCRmvTgpOM821l4fTHsoJ8IGWV0mwfk95pEL+g/eBLExR4etMqaW9uz
+x8vnVTKNzZsAVgRcemcLqzuyuMg+/ZnH+YNMzMl0Nbkt+kE3FhfM
+-----END RSA PRIVATE KEY-----
+)";
+
+// server_certificate.pem
+static constexpr char kCertificate[] = R"(
+-----BEGIN CERTIFICATE-----
+MIICijCCAXICAQEwDQYJKoZIhvcNAQELBQAwADAeFw0xOTA2MTAwODM3MzlaFw0y
+OTA2MDcwODM3MzlaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4
+FX+j8yyohxS8OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIq
+nEDbxvcWBrv7NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7
+gu1gn+CnaDYupAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8j
+hBqhYbQK7ZFLmIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOE
+bAI1QF+tEn1bnaLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABMA0GCSqG
+SIb3DQEBCwUAA4IBAQAuI2NjdWiD2lwmRraW6C7VBF+Sf+9QlzTVzSjuDbPkkYIo
+YWpeYsEeFO5NlxxXl77iu4MqznSAOK8GCiNDCCulDNWRhd5lcO1dVHLcIFYKZ+xv
+6IuH3vh60qJ2hoZbalwflnMQklqh3745ZyOH79dzKTFvlWyNJ2hQgP9bZ2g8F4od
+dS7aOwvx3DCv46b7vBJMKd53ZCdHubfFebDcGxc60DUR0fLSI/o1MJgriODZ1SX7
+sxwzrxvbJW0T+gJOL0C0lE6D84F9oL2u3ef17fC5u1bRd/77pzjTM+dQe7sZspCz
+iboTujdUqo+NSdWgwPUTGTYQg/1i9Qe0vjc0YplY
+-----END CERTIFICATE-----
+)";
\ No newline at end of file
diff --git a/tests/dns_responder/dns_tls_frontend.cpp b/tests/dns_responder/dns_tls_frontend.cpp
index 6399842..dfd4ec4 100644
--- a/tests/dns_responder/dns_tls_frontend.cpp
+++ b/tests/dns_responder/dns_tls_frontend.cpp
@@ -32,82 +32,12 @@
 #include <android-base/logging.h>
 #include <netdutils/InternetAddresses.h>
 #include <netdutils/SocketOption.h>
+#include "dns_tls_certificate.h"
 
 using android::netdutils::enableSockopt;
 using android::netdutils::ScopedAddrinfo;
 
 namespace {
-/*
- * test cert, key, and rootca files can be generated using openssl with
- * the following commands:
- *
- * Create CA certificate:
- * $ openssl genrsa 2048 > ca-key.pem
- * $ openssl req -new -sha256 -x509 -nodes -days 3650 -key ca_key.pem -out ca_certificate.pem -subj
- * '/C=/ST=/L=/CN=/emailAddress='
- *
- * Create server certificate:
- * $ openssl req -sha256 -newkey rsa:2048 -days 3650 -nodes -keyout serve_key.pem -out
- * server_req.pem -subj '/C=/ST=/L=/CN=example.com/emailAddress='
- * $ openssl rsa -in server_key.pem
- * -out server_key.pem $ openssl x509 -sha256 -req -in server_req.pem -days 3650 -CA
- * ca_certificate.pem -CAkey ca_key.pem -set_serial 01 -out server_certificate.pem
- *
- * Verify the certificate:
- * $ openssl verify -CAfile ca_certificate.pem server_certificate.pem
- */
-// server_certificate.pem
-static const char kCertificate[] = R"(
-const std::string kCertificate =
------BEGIN CERTIFICATE-----
-MIICijCCAXICAQEwDQYJKoZIhvcNAQELBQAwADAeFw0xOTA2MTAwODM3MzlaFw0y
-OTA2MDcwODM3MzlaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4
-FX+j8yyohxS8OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIq
-nEDbxvcWBrv7NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7
-gu1gn+CnaDYupAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8j
-hBqhYbQK7ZFLmIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOE
-bAI1QF+tEn1bnaLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABMA0GCSqG
-SIb3DQEBCwUAA4IBAQAuI2NjdWiD2lwmRraW6C7VBF+Sf+9QlzTVzSjuDbPkkYIo
-YWpeYsEeFO5NlxxXl77iu4MqznSAOK8GCiNDCCulDNWRhd5lcO1dVHLcIFYKZ+xv
-6IuH3vh60qJ2hoZbalwflnMQklqh3745ZyOH79dzKTFvlWyNJ2hQgP9bZ2g8F4od
-dS7aOwvx3DCv46b7vBJMKd53ZCdHubfFebDcGxc60DUR0fLSI/o1MJgriODZ1SX7
-sxwzrxvbJW0T+gJOL0C0lE6D84F9oL2u3ef17fC5u1bRd/77pzjTM+dQe7sZspCz
-iboTujdUqo+NSdWgwPUTGTYQg/1i9Qe0vjc0YplY
------END CERTIFICATE-----
-)";
-
-// server_key.pem
-static const char kPrivatekey[] = R"(
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAuo/v4VuY0Ees5HRx+NwTGm/bgToUFjq9R4z4FX+j8yyohxS8
-OxQZzpKu8JJytyPPi+SnXqZB25usGBPJHapD1Q5YYCIZF9EBztIqnEDbxvcWBrv7
-NDDhPMQ6v5YFhAIUN3a1yBESBWQOWsNkwJw04Wc4agZrhhnG/vS7gu1gn+CnaDYu
-pAmHrGS9cSV/B9ZCpLhis2JxmULgdz6ZBee/x8dHHFd1Qeb/+G8jhBqhYbQK7ZFL
-mIO3DXrlP/ONXJ8IE2+PPDloiotkY5ar/1ZbRQS9fSKM9J6pipOEbAI1QF+tEn1b
-naLfJfoMHIcb0p5xr04OALUZOGw4iVfxulMRIQIDAQABAoIBACDLLF9wumviLYH6
-9g3IoZMEFpGgo+dEbAEnxnQA+9DDCNy1yGCaJ+8n2ZhwJboLkXAFwWXh07HGq3mQ
-AMo2I7ZPzzkWxVJqaubwCo1s2TUgOb71TDLgZLdJxwnmVRHfS650L3/7gC9yZxON
-RSiWTLVSb5gziLMJ1PD8E/nvwAxaJDlT6vzqwRbnHBkQoumTmds2ecLJd2/6pfl4
-bMhtIKA3ULqnJlqlRt6ds/pWU9ttmXEX52uaGhzaF7PRomOW5pKR6CyBzNCn/RNF
-ZPIINW1TVWss9NMZsJLdIzs7Oon5gQYil9rU2uiA5ZUanYDIL9DOMrfAM3hfUuFq
-ZOhfBAECgYEA36CT81EkdDE7pum/kIuCG3wDEls+xNbWmF76IJAxnolJzKvJsdJA
-0za/l1Qe3/bRYHZWKc7by45LFYefOsS29jqBnBBMLurI7OLtcXqkTSSm11AfsDDI
-gw4bKs81TYdHhnbIDGeApfSWOGXgDM/j4N3stuvY1lOIocXqKMomZVMCgYEA1ZHD
-jtxeAmCqzJHIJ4DOY0Y2RR3Bq3ue/mc8gmV9wDyJMMvBpvOoRkUjwbKZToi4Px30
-5fn6SCRtOKfEL0b9LV7JFsMr84Zoj8kjtnE0BdNfQqdE/uWltpATl6YUPlzqZTGs
-HBGVpsNCzYkjFu9m/zIiryCHY8Uut3VEZmNJjTsCgYEAgADBTzAuBpg7xeHcdhd0
-xNiqRXKXLkKvGQ6ca9E9pbp91LqsO63Wz09yQWO0PIxh8q4pycqPQyfS0KMNwKzi
-8XQxxiwJ/30Cv51xPlht/X4yReKmEMsLqwCDCnEK2LLLfSs2fOst11B2QBgINC03
-CfrdySKcvqmX9sl7rBdx/OMCgYB9t2o4RDwKhkDEXuRFbKsRARmdIeEJQqHa+4ZA
-8+FMMdZIJQj/b9qUUsqzkKBx/EUI0meAoN/Va6vnd8oiUlViSbNxdL4AghQ2353o
-HUcUTtJ6d+BDc4dSqgj+ccLk2ukXXGAFvcwr+DDwsFM5gv9MJYUJNcq8ziurzpnO
-848uVQKBgEmyAa2jt1qNpAvxU0MakJIuKhQl2b6/54EKi9WKqIMs1+rKk6O/Ck3n
-+tEWqHhZ4uCRmvTgpOM821l4fTHsoJ8IGWV0mwfk95pEL+g/eBLExR4etMqaW9uz
-x8vnVTKNzZsAVgRcemcLqzuyuMg+/ZnH+YNMzMl0Nbkt+kE3FhfM
------END RSA PRIVATE KEY-----
-)";
-
 static bssl::UniquePtr<X509> stringToX509Certs(const char* certs) {
     bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(certs, strlen(certs)));
     return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
@@ -364,11 +294,14 @@
     return true;
 }
 
-bool DnsTlsFrontend::waitForQueries(int number, int timeoutMs) const {
+// TODO: use a condition variable instead of polling
+// TODO: also clear queries_ to eliminate potential race conditions
+bool DnsTlsFrontend::waitForQueries(int expected_count) const {
     constexpr int intervalMs = 20;
+    constexpr int timeoutMs = 5000;
     int limit = timeoutMs / intervalMs;
     for (int count = 0; count <= limit; ++count) {
-        bool done = queries_ >= number;
+        bool done = queries_ >= expected_count;
         // Always sleep at least one more interval after we are done, to wait for
         // any immediate post-query actions that the client may take (such as
         // marking this server as reachable during validation).
diff --git a/tests/dns_responder/dns_tls_frontend.h b/tests/dns_responder/dns_tls_frontend.h
index b4455de..1b34607 100644
--- a/tests/dns_responder/dns_tls_frontend.h
+++ b/tests/dns_responder/dns_tls_frontend.h
@@ -39,8 +39,10 @@
  */
 class DnsTlsFrontend {
   public:
-    DnsTlsFrontend(const std::string& listen_address, const std::string& listen_service,
-                   const std::string& backend_address, const std::string& backend_service)
+    DnsTlsFrontend(const std::string& listen_address = kDefaultListenAddr,
+                   const std::string& listen_service = kDefaultListenService,
+                   const std::string& backend_address = kDefaultBackendAddr,
+                   const std::string& backend_service = kDefaultBackendService)
         : listen_address_(listen_address),
           listen_service_(listen_service),
           backend_address_(backend_address),
@@ -51,12 +53,19 @@
     bool running() const { return socket_ != -1; }
     bool startServer();
     bool stopServer();
+
     int queries() const { return queries_; }
     void clearQueries() { queries_ = 0; }
-    bool waitForQueries(int number, int timeoutMs) const;
+    bool waitForQueries(int expected_count) const;
+
     void set_chain_length(int length) { chain_length_ = length; }
     void setHangOnHandshakeForTesting(bool hangOnHandshake) { hangOnHandshake_ = hangOnHandshake; }
 
+    static constexpr char kDefaultListenAddr[] = "127.0.0.3";
+    static constexpr char kDefaultListenService[] = "853";
+    static constexpr char kDefaultBackendAddr[] = "127.0.0.3";
+    static constexpr char kDefaultBackendService[] = "53";
+
   private:
     void requestHandler();
     bool handleOneRequest(SSL* ssl);
diff --git a/dnsresolver_binder_test.cpp b/tests/dnsresolver_binder_test.cpp
similarity index 71%
rename from dnsresolver_binder_test.cpp
rename to tests/dnsresolver_binder_test.cpp
index 97c136f..1c6527f 100644
--- a/dnsresolver_binder_test.cpp
+++ b/tests/dnsresolver_binder_test.cpp
@@ -23,33 +23,27 @@
 #include <iostream>
 #include <vector>
 
+#include <aidl/android/net/IDnsResolver.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android/net/IDnsResolver.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <gmock/gmock-matchers.h>
 #include <gtest/gtest.h>
 #include <netdutils/Stopwatch.h>
 
-#include "tests/dns_metrics_listener/base_metrics_listener.h"
-#include "tests/dns_metrics_listener/test_metrics.h"
+#include "dns_metrics_listener/base_metrics_listener.h"
+#include "dns_metrics_listener/test_metrics.h"
 
 #include "ResolverStats.h"
 #include "dns_responder.h"
-#include "dns_responder_client.h"
+#include "dns_responder_client_ndk.h"
 
-namespace binder = android::binder;
-
-using android::IBinder;
-using android::IServiceManager;
-using android::ProcessState;
-using android::sp;
-using android::String16;
-using android::String8;
-using android::net::IDnsResolver;
-using android::net::ResolverParamsParcel;
+using aidl::android::net::IDnsResolver;
+using aidl::android::net::ResolverParamsParcel;
+using aidl::android::net::metrics::INetdEventListener;
+using android::base::StringPrintf;
 using android::net::ResolverStats;
-using android::net::metrics::INetdEventListener;
 using android::net::metrics::TestOnDnsEvent;
 using android::netdutils::Stopwatch;
 
@@ -60,11 +54,9 @@
 class DnsResolverBinderTest : public ::testing::Test {
   public:
     DnsResolverBinderTest() {
-        sp<IServiceManager> sm = android::defaultServiceManager();
-        sp<IBinder> binder = sm->getService(String16("dnsresolver"));
-        if (binder != nullptr) {
-            mDnsResolver = android::interface_cast<IDnsResolver>(binder);
-        }
+        ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(AServiceManager_getService("dnsresolver"));
+
+        mDnsResolver = IDnsResolver::fromBinder(resolvBinder);
         // This could happen when the test isn't running as root, or if netd isn't running.
         assert(nullptr != mDnsResolver.get());
         // Create cache for test
@@ -77,7 +69,7 @@
     }
 
   protected:
-    sp<IDnsResolver> mDnsResolver;
+    std::shared_ptr<aidl::android::net::IDnsResolver> mDnsResolver;
 };
 
 class TimedOperation : public Stopwatch {
@@ -91,43 +83,6 @@
     std::string mName;
 };
 
-namespace {
-
-// TODO: Convert tests to ResolverParamsParcel and delete this stub.
-ResolverParamsParcel makeResolverParamsParcel(int netId, const std::vector<int>& params,
-                                              const std::vector<std::string>& servers,
-                                              const std::vector<std::string>& domains,
-                                              const std::string& tlsHostname,
-                                              const std::vector<std::string>& tlsServers) {
-    using android::net::IDnsResolver;
-    ResolverParamsParcel paramsParcel;
-
-    paramsParcel.netId = netId;
-    paramsParcel.sampleValiditySeconds = params[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY];
-    paramsParcel.successThreshold = params[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD];
-    paramsParcel.minSamples = params[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES];
-    paramsParcel.maxSamples = params[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES];
-    if (params.size() > IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC) {
-        paramsParcel.baseTimeoutMsec = params[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC];
-    } else {
-        paramsParcel.baseTimeoutMsec = 0;
-    }
-    if (params.size() > IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT) {
-        paramsParcel.retryCount = params[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT];
-    } else {
-        paramsParcel.retryCount = 0;
-    }
-    paramsParcel.servers = servers;
-    paramsParcel.domains = domains;
-    paramsParcel.tlsName = tlsHostname;
-    paramsParcel.tlsServers = tlsServers;
-    paramsParcel.tlsFingerprints = {};
-
-    return paramsParcel;
-}
-
-}  // namespace
-
 TEST_F(DnsResolverBinderTest, IsAlive) {
     TimedOperation t("isAlive RPC");
     bool isAlive = false;
@@ -136,26 +91,23 @@
 }
 
 TEST_F(DnsResolverBinderTest, RegisterEventListener_NullListener) {
-    android::binder::Status status = mDnsResolver->registerEventListener(
-            android::interface_cast<INetdEventListener>(nullptr));
+    ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(nullptr);
     ASSERT_FALSE(status.isOk());
-    ASSERT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    ASSERT_EQ(EINVAL, status.getServiceSpecificError());
 }
 
 TEST_F(DnsResolverBinderTest, RegisterEventListener_DuplicateSubscription) {
     class DummyListener : public android::net::metrics::BaseMetricsListener {};
 
     // Expect to subscribe successfully.
-    android::sp<DummyListener> dummyListener = new DummyListener();
-    android::binder::Status status = mDnsResolver->registerEventListener(
-            android::interface_cast<INetdEventListener>(dummyListener));
-    ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+    std::shared_ptr<DummyListener> dummyListener = ndk::SharedRefBase::make<DummyListener>();
+    ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(dummyListener);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
 
     // Expect to subscribe failed with registered listener instance.
-    status = mDnsResolver->registerEventListener(
-            android::interface_cast<INetdEventListener>(dummyListener));
+    status = mDnsResolver->registerEventListener(dummyListener);
     ASSERT_FALSE(status.isOk());
-    ASSERT_EQ(EEXIST, status.serviceSpecificErrorCode());
+    ASSERT_EQ(EEXIST, status.getServiceSpecificError());
 }
 
 // TODO: Move this test to resolv_integration_test.cpp
@@ -178,7 +130,7 @@
 
     // Start the Binder thread pool.
     // TODO: Consider doing this once if there has another event listener unit test.
-    android::ProcessState::self()->startThreadPool();
+    ABinderProcess_startThreadPool();
 
     // Setup network.
     // TODO: Setup device configuration and DNS responser server as resolver test does.
@@ -204,10 +156,10 @@
     dns.clearQueries();
 
     // Register event listener.
-    android::sp<TestOnDnsEvent> testOnDnsEvent = new TestOnDnsEvent(expectedResults);
-    android::binder::Status status = mDnsResolver->registerEventListener(
-            android::interface_cast<INetdEventListener>(testOnDnsEvent));
-    ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+    std::shared_ptr<TestOnDnsEvent> testOnDnsEvent =
+            ndk::SharedRefBase::make<TestOnDnsEvent>(expectedResults);
+    ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(testOnDnsEvent);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
 
     // DNS queries.
     // Once all expected events of expectedResults are received by the listener, the unit test will
@@ -277,18 +229,18 @@
     for (size_t i = 0; i < std::size(kTlsTestData); i++) {
         const auto& td = kTlsTestData[i];
 
-        const auto resolverParams = makeResolverParamsParcel(
+        const auto resolverParams = DnsResponderClient::makeResolverParamsParcel(
                 TEST_NETID, test_params, LOCALLY_ASSIGNED_DNS, {}, td.tlsName, td.servers);
-        binder::Status status = mDnsResolver->setResolverConfiguration(resolverParams);
+        ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(resolverParams);
 
         if (td.expectedReturnCode == 0) {
-            SCOPED_TRACE(String8::format("test case %zu should have passed", i));
-            SCOPED_TRACE(status.toString8());
-            EXPECT_EQ(0, status.exceptionCode());
+            SCOPED_TRACE(StringPrintf("test case %zu should have passed", i));
+            SCOPED_TRACE(status.getMessage());
+            EXPECT_EQ(0, status.getServiceSpecificError());
         } else {
-            SCOPED_TRACE(String8::format("test case %zu should have failed", i));
-            EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
-            EXPECT_EQ(td.expectedReturnCode, status.serviceSpecificErrorCode());
+            SCOPED_TRACE(StringPrintf("test case %zu should have failed", i));
+            EXPECT_EQ(EX_SERVICE_SPECIFIC, status.getExceptionCode());
+            EXPECT_EQ(td.expectedReturnCode, status.getServiceSpecificError());
         }
     }
 }
@@ -303,10 +255,10 @@
             100,     // BASE_TIMEOUT_MSEC
             3,       // retry count
     };
-    const auto resolverParams =
-            makeResolverParamsParcel(TEST_NETID, testParams, servers, domains, "", {});
-    binder::Status status = mDnsResolver->setResolverConfiguration(resolverParams);
-    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    const auto resolverParams = DnsResponderClient::makeResolverParamsParcel(
+            TEST_NETID, testParams, servers, domains, "", {});
+    ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(resolverParams);
+    EXPECT_TRUE(status.isOk()) << status.getMessage();
 
     std::vector<std::string> res_servers;
     std::vector<std::string> res_domains;
@@ -318,7 +270,7 @@
                                            &params32, &stats32,
                                            &wait_for_pending_req_timeout_count32);
 
-    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    EXPECT_TRUE(status.isOk()) << status.getMessage();
     EXPECT_EQ(servers.size(), res_servers.size());
     EXPECT_EQ(domains.size(), res_domains.size());
     EXPECT_EQ(0U, res_tls_servers.size());
@@ -354,7 +306,7 @@
 
     // create it again, expect a EEXIST.
     EXPECT_EQ(EEXIST,
-              mDnsResolver->createNetworkCache(ANOTHER_TEST_NETID).serviceSpecificErrorCode());
+              mDnsResolver->createNetworkCache(ANOTHER_TEST_NETID).getServiceSpecificError());
 
     // destroy it.
     EXPECT_TRUE(mDnsResolver->destroyNetworkCache(ANOTHER_TEST_NETID).isOk());
@@ -369,9 +321,16 @@
     EXPECT_TRUE(mDnsResolver->destroyNetworkCache(ANOTHER_TEST_NETID).isOk());
 }
 
+TEST_F(DnsResolverBinderTest, FlushNetworkCache) {
+    SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsResolver.get(), 4);
+    // cache has beed created in DnsResolverBinderTest constructor
+    EXPECT_TRUE(mDnsResolver->flushNetworkCache(TEST_NETID).isOk());
+    EXPECT_EQ(ENONET, mDnsResolver->flushNetworkCache(-1).getServiceSpecificError());
+}
+
 TEST_F(DnsResolverBinderTest, setLogSeverity) {
     // Expect fail
-    EXPECT_EQ(EINVAL, mDnsResolver->setLogSeverity(-1).serviceSpecificErrorCode());
+    EXPECT_EQ(EINVAL, mDnsResolver->setLogSeverity(-1).getServiceSpecificError());
 
     // Test set different log level
     EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_VERBOSE).isOk());
diff --git a/tests/golddata.proto b/tests/golddata.proto
new file mode 100644
index 0000000..05133fc
--- /dev/null
+++ b/tests/golddata.proto
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+package android.net;
+
+// Used to indicate which call is invoked to send DNS lookups.
+enum CallType {
+    CALL_GETADDRINFO = 0;
+    CALL_GETHOSTBYNAME = 1;
+    CALL_GETHOSTBYADDR = 2;
+    CALL_GETNAMEINFO = 3;
+    CALL_RES_NSEND = 4;
+}
+
+// Values from bionic/libc/include/sys/socket.h
+enum AddressFamily {
+    option allow_alias = true;  // for AF_ROUTE = AF_NETLINK
+
+    GT_AF_UNSPEC = 0;
+    GT_AF_UNIX = 1;
+    GT_AF_LOCAL = 1;
+    GT_AF_INET = 2;
+    GT_AF_AX25 = 3;
+    GT_AF_IPX = 4;
+    GT_AF_APPLETALK = 5;
+    GT_AF_NETROM = 6;
+    GT_AF_BRIDGE = 7;
+    GT_AF_ATMPVC= 8;
+    GT_AF_X25 = 9;
+    GT_AF_INET6 = 10;
+    GT_AF_ROSE = 11;
+    GT_AF_DECnet = 12;
+    GT_AF_NETBEUI = 13;
+    GT_AF_SECURITY = 14;
+    GT_AF_KEY = 15;
+    GT_AF_NETLINK = 16;
+    GT_AF_ROUTE = 16;  // AF_NETLINK
+    GT_AF_PACKET = 17;
+    GT_AF_ASH = 18;
+    GT_AF_ECONET = 19;
+    GT_AF_ATMSVC = 20;
+    GT_AF_RDS = 21;
+    GT_AF_SNA = 22;
+    GT_AF_IRDA = 23;
+    GT_AF_PPPOX = 24;
+    GT_AF_WANPIPE = 25;
+    GT_AF_LLC = 26;
+    GT_AF_CAN = 29;
+    GT_AF_TIPC = 30;
+    GT_AF_BLUETOOTH = 31;
+    GT_AF_IUCV = 32;
+    GT_AF_RXRPC = 33;
+    GT_AF_ISDN = 34;
+    GT_AF_PHONET = 35;
+    GT_AF_IEEE802154 = 36;
+    GT_AF_CAIF = 37;
+    GT_AF_ALG = 38;
+    GT_AF_NFC = 39;
+    GT_AF_VSOCK = 40;
+    GT_AF_KCM = 41;
+    GT_AF_QIPCRTR = 42;
+    GT_AF_MAX = 43;
+}
+
+// Values from bionic/libc/include/sys/socket.h
+enum SocketType {
+    GT_SOCK_ANY = 0;  // See man getaddrinfo for more detail
+    GT_SOCK_STREAM = 1;
+    GT_SOCK_DGRAM = 2;
+    GT_SOCK_RAW = 3;
+    GT_SOCK_RDM = 4;
+    GT_SOCK_SEQPACKET = 5;
+    GT_SOCK_DCCP = 6;
+    GT_SOCK_PACKET = 10;
+}
+
+// Values from bionic/libc/kernel/uapi/linux/in.h
+enum ProtocolType {
+    GT_IPPROTO_IP = 0;
+    GT_IPPROTO_ICMP = 1;
+    GT_IPPROTO_IGMP = 2;
+    GT_IPPROTO_IPIP = 4;
+    GT_IPPROTO_TCP = 6;
+    GT_IPPROTO_EGP = 8;
+    GT_IPPROTO_PUP = 12;
+    GT_IPPROTO_UDP = 17;
+    GT_IPPROTO_IDP = 22;
+    GT_IPPROTO_TP = 29;
+    GT_IPPROTO_DCCP = 33;
+    GT_IPPROTO_IPV6 = 41;
+    GT_IPPROTO_RSVP = 46;
+    GT_IPPROTO_GRE = 47;
+    GT_IPPROTO_ESP = 50;
+    GT_IPPROTO_AH = 51;
+    GT_IPPROTO_MTP = 92;
+    GT_IPPROTO_BEETPH = 94;
+    GT_IPPROTO_ENCAP = 98;
+    GT_IPPROTO_PIM = 103;
+    GT_IPPROTO_COMP = 108;
+    GT_IPPROTO_SCTP = 132;
+    GT_IPPROTO_UDPLITE = 136;
+    GT_IPPROTO_MPLS = 137;
+    GT_IPPROTO_RAW = 255;
+    GT_IPPROTO_MAX = 256;
+}
+
+// The return value of the DNS resolver for each DNS lookups.
+// Values from bionic/libc/include/netdb.h
+// Values from system/netd/resolv/include/netd_resolv/resolv.h
+enum ReturnCodeType {
+    GT_EAI_NO_ERROR = 0;
+    GT_EAI_ADDRFAMILY = 1;
+    GT_EAI_AGAIN = 2;
+    GT_EAI_BADFLAGS = 3;
+    GT_EAI_FAIL = 4;
+    GT_EAI_FAMILY = 5;
+    GT_EAI_MEMORY = 6;
+    GT_EAI_NODATA = 7;
+    GT_EAI_NONAME = 8;
+    GT_EAI_SERVICE = 9;
+    GT_EAI_SOCKTYPE = 10;
+    GT_EAI_SYSTEM = 11;
+    GT_EAI_BADHINTS = 12;
+    GT_EAI_PROTOCOL = 13;
+    GT_EAI_OVERFLOW = 14;
+    GT_RESOLV_TIMEOUT = 255;
+    GT_EAI_MAX = 256;
+}
+
+// Describes the test configuration and expected result for gold test.
+// The unit test files a DNS query by the predefined |config|. Expect that the resolver sends the
+// query packet as the predefined packet in |packet_mapping.query|. When the DNS responser receives
+// the query packet, it returns the corresponding response packet from |packet_mapping.response|.
+// Finally, the unit test checks the return values if they are the same as |result|. Currently,
+// support getaddrinfo only.
+// TODO: Support gethostbyname, gethostbyaddr, and getnameinfo.
+message GoldTest {
+    // The configuration of various DNS query calls.
+    message Config {
+        // The arguments used to send a DNS query by call type CALL_GETADDRINFO.
+        message AddrInfo {
+            string host = 1;
+            AddressFamily family = 2;
+            SocketType socktype = 3;
+            ProtocolType protocol = 4;
+            int32 ai_flags = 5;
+        }
+
+        // The arguments used to send a DNS query by call type CALL_GETHOSTBYNAME.
+        message HostByName {
+            string host = 1;
+            AddressFamily family = 2;
+        }
+
+        // The call is used to send DNS lookups.
+        CallType call = 1;
+
+        // The arguments are used by the call.
+        oneof Arg {
+            // The arguments of call type CALL_GETADDRINFO.
+            AddrInfo addrinfo = 2;
+            HostByName hostbyname = 3;
+        }
+    };
+
+    // The result is expected in DNS lookups.
+    message Result {
+        ReturnCodeType return_code = 1;
+        repeated string addresses = 2;
+    };
+
+    // Describes how the DNS responser handles and responses the DNS lookup packets.
+    message PacketMapping {
+      bytes query = 1;
+      bytes response = 2;
+    }
+
+    // Configs used to send a DNS query via a DNS query API.
+    Config config = 1;
+
+    // Expected return values from DNS query API.
+    Result result = 2;
+
+    // Used to build the packet mapping (query, response) in DNS responser. See also
+    // addMappingBinaryPacket() in
+    // packages/modules/DnsResolver/tests/dns_responder/dns_responder.cpp.
+    repeated PacketMapping packet_mapping = 3;
+}
\ No newline at end of file
diff --git a/tests/resolv_gold_test.cpp b/tests/resolv_gold_test.cpp
new file mode 100644
index 0000000..5216268
--- /dev/null
+++ b/tests/resolv_gold_test.cpp
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "resolv_gold_test"
+
+#include <Fwmark.h>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gmock/gmock-matchers.h>
+#include <google/protobuf/text_format.h>
+#include <gtest/gtest.h>
+
+#include "PrivateDnsConfiguration.h"
+#include "getaddrinfo.h"
+#include "gethnamaddr.h"
+#include "golddata.pb.h"
+#include "resolv_cache.h"
+#include "resolv_test_utils.h"
+#include "tests/dns_responder/dns_responder.h"
+#include "tests/dns_responder/dns_responder_client_ndk.h"
+#include "tests/dns_responder/dns_tls_certificate.h"
+#include "tests/dns_responder/dns_tls_frontend.h"
+
+namespace android::net {
+
+using android::base::StringPrintf;
+using android::netdutils::ScopedAddrinfo;
+using std::chrono::milliseconds;
+
+enum class DnsProtocol { CLEARTEXT, TLS };
+
+// The buffer size of resolv_gethostbyname().
+// TODO: Consider moving to packages/modules/DnsResolver/tests/resolv_test_utils.h.
+constexpr unsigned int MAXPACKET = 8 * 1024;
+
+const std::string kTestDataPath = android::base::GetExecutableDirectory() + "/testdata/";
+const std::vector<std::string> kGoldFilesGetAddrInfo = {
+        "getaddrinfo.topsite.google.pbtxt",    "getaddrinfo.topsite.youtube.pbtxt",
+        "getaddrinfo.topsite.amazon.pbtxt",    "getaddrinfo.topsite.yahoo.pbtxt",
+        "getaddrinfo.topsite.facebook.pbtxt",  "getaddrinfo.topsite.reddit.pbtxt",
+        "getaddrinfo.topsite.wikipedia.pbtxt", "getaddrinfo.topsite.ebay.pbtxt",
+        "getaddrinfo.topsite.netflix.pbtxt",   "getaddrinfo.topsite.bing.pbtxt"};
+const std::vector<std::string> kGoldFilesGetAddrInfoTls = {"getaddrinfo.tls.topsite.google.pbtxt"};
+const std::vector<std::string> kGoldFilesGetHostByName = {"gethostbyname.topsite.youtube.pbtxt"};
+const std::vector<std::string> kGoldFilesGetHostByNameTls = {
+        "gethostbyname.tls.topsite.youtube.pbtxt"};
+
+// Fixture test class definition.
+class TestBase : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        // Create cache for test
+        resolv_create_cache_for_net(TEST_NETID);
+    }
+
+    void TearDown() override {
+        // Clear TLS configuration for test
+        gPrivateDnsConfiguration.clear(TEST_NETID);
+        // Delete cache for test
+        resolv_delete_cache_for_net(TEST_NETID);
+    }
+
+    void SetResolverConfiguration(const std::vector<std::string>& servers,
+                                  const std::vector<std::string>& domains,
+                                  const std::vector<std::string>& tlsServers = {},
+                                  const std::string& tlsHostname = "",
+                                  const std::string& caCert = "") {
+        // Determine the DNS configuration steps from setResolverConfiguration() in
+        // packages/modules/DnsResolver/ResolverController.cpp. The gold test just needs to setup
+        // simply DNS and DNS-over-TLS server configuration. Some implementation in
+        // setResolverConfiguration() are not required. For example, limiting TLS server amount is
+        // not necessary for gold test because gold test has only one TLS server for testing
+        // so far.
+        Fwmark fwmark;
+        fwmark.netId = TEST_NETID;
+        fwmark.explicitlySelected = true;
+        fwmark.protectedFromVpn = true;
+        fwmark.permission = PERMISSION_SYSTEM;
+        ASSERT_EQ(gPrivateDnsConfiguration.set(TEST_NETID, fwmark.intValue, tlsServers, tlsHostname,
+                                               caCert),
+                  0);
+        ASSERT_EQ(resolv_set_nameservers(TEST_NETID, servers, domains, kParams), 0);
+    }
+
+    void SetResolvers() { SetResolverConfiguration(kDefaultServers, kDefaultSearchDomains); }
+
+    void SetResolversWithTls() {
+        // Pass servers as both network-assigned and TLS servers. Tests can
+        // determine on which server and by which protocol queries arrived.
+        // See also DnsClient::SetResolversWithTls() in
+        // packages/modules/DnsResolver/tests/dns_responder/dns_responder_client.h.
+        SetResolverConfiguration(kDefaultServers, kDefaultSearchDomains, kDefaultServers,
+                                 kDefaultPrivateDnsHostName, kCaCert);
+    }
+
+    bool WaitForPrivateDnsValidation(const std::string& serverAddr) {
+        constexpr milliseconds retryIntervalMs{20};
+        constexpr milliseconds timeoutMs{3000};
+        android::base::Timer t;
+        while (t.duration() < timeoutMs) {
+            const auto& validatedServers =
+                    gPrivateDnsConfiguration.getStatus(TEST_NETID).validatedServers();
+            for (const auto& server : validatedServers) {
+                if (serverAddr == ToString(&server.ss)) return true;
+            }
+            std::this_thread::sleep_for(retryIntervalMs);
+        }
+        return false;
+    }
+
+    GoldTest ToProto(const std::string& file) {
+        // Convert the testing configuration from .pbtxt file to proto.
+        std::string file_content;
+        const std::string file_name = kTestDataPath + file;
+        EXPECT_TRUE(android::base::ReadFileToString(file_name, &file_content))
+                << "Failed to read " << file_name << ": " << strerror(errno);
+        android::net::GoldTest goldtest;
+        EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(file_content, &goldtest));
+        return goldtest;
+    }
+
+    void SetupMappings(const android::net::GoldTest& goldtest, test::DNSResponder& dns) {
+        for (const auto& m : goldtest.packet_mapping()) {
+            // Convert string to bytes because .proto type "bytes" is "string" type in C++.
+            // See also the section "Scalar Value Types" in "Language Guide (proto3)".
+            // TODO: Use C++20 std::span in addMappingBinaryPacket. It helps to take both
+            // std::string and std::vector without conversions.
+            dns.addMappingBinaryPacket(
+                    std::vector<uint8_t>(m.query().begin(), m.query().end()),
+                    std::vector<uint8_t>(m.response().begin(), m.response().end()));
+        }
+    }
+
+    android_net_context GetNetContext(const DnsProtocol protocol) {
+        return protocol == DnsProtocol::TLS ? kNetcontextTls : kNetcontext;
+    }
+
+    template <class AddressType>
+    void VerifyAddress(const android::net::GoldTest& goldtest, const AddressType& result) {
+        if (goldtest.result().return_code() != GT_EAI_NO_ERROR) {
+            EXPECT_EQ(result, nullptr);
+        } else {
+            ASSERT_NE(result, nullptr);
+            const auto& addresses = goldtest.result().addresses();
+            EXPECT_THAT(ToStrings(result), ::testing::UnorderedElementsAreArray(addresses));
+        }
+    }
+
+    void VerifyGetAddrInfo(const android::net::GoldTest& goldtest, const DnsProtocol protocol) {
+        ASSERT_TRUE(goldtest.config().has_addrinfo());
+        const auto& args = goldtest.config().addrinfo();
+        const addrinfo hints = {
+                // Clear the flag AI_ADDRCONFIG to avoid flaky test because AI_ADDRCONFIG looks at
+                // whether connectivity is available. It makes that the resolver may send only A
+                // or AAAA DNS query per connectivity even AF_UNSPEC has been assigned. See also
+                // have_ipv6() and have_ipv4() in packages/modules/DnsResolver/getaddrinfo.cpp.
+                // TODO: Consider keeping the configuration flag AI_ADDRCONFIG once the unit
+                // test can treat the IPv4 and IPv6 connectivity.
+                .ai_flags = args.ai_flags() & ~AI_ADDRCONFIG,
+                .ai_family = args.family(),
+                .ai_socktype = args.socktype(),
+                .ai_protocol = args.protocol(),
+        };
+        addrinfo* res = nullptr;
+        const android_net_context netcontext = GetNetContext(protocol);
+        NetworkDnsEventReported event;
+        const int rv =
+                resolv_getaddrinfo(args.host().c_str(), nullptr, &hints, &netcontext, &res, &event);
+        ScopedAddrinfo result(res);
+        ASSERT_EQ(rv, goldtest.result().return_code());
+        VerifyAddress(goldtest, result);
+    }
+
+    void VerifyGetHostByName(const android::net::GoldTest& goldtest, const DnsProtocol protocol) {
+        ASSERT_TRUE(goldtest.config().has_hostbyname());
+        const auto& args = goldtest.config().hostbyname();
+        hostent* hp = nullptr;
+        hostent hbuf;
+        char tmpbuf[MAXPACKET];
+        const android_net_context netcontext = GetNetContext(protocol);
+        NetworkDnsEventReported event;
+        const int rv = resolv_gethostbyname(args.host().c_str(), args.family(), &hbuf, tmpbuf,
+                                            sizeof(tmpbuf), &netcontext, &hp, &event);
+        ASSERT_EQ(rv, goldtest.result().return_code());
+        VerifyAddress(goldtest, hp);
+    }
+
+    void VerifyResolver(const android::net::GoldTest& goldtest, const test::DNSResponder& dns,
+                        const test::DnsTlsFrontend& tls, const DnsProtocol protocol) {
+        size_t queries;
+        std::string name;
+
+        // Verify DNS query calls and results by proto. Then, determine expected query times and
+        // queried name for checking server query status later.
+        switch (const auto calltype = goldtest.config().call()) {
+            case android::net::CallType::CALL_GETADDRINFO:
+                ASSERT_TRUE(goldtest.config().has_addrinfo());
+                ASSERT_NO_FATAL_FAILURE(VerifyGetAddrInfo(goldtest, protocol));
+                queries = goldtest.config().addrinfo().family() == AF_UNSPEC ? 2U : 1U;
+                name = goldtest.config().addrinfo().host();
+                break;
+            case android::net::CallType::CALL_GETHOSTBYNAME:
+                ASSERT_TRUE(goldtest.config().has_hostbyname());
+                ASSERT_NO_FATAL_FAILURE(VerifyGetHostByName(goldtest, protocol));
+                queries = 1U;
+                name = goldtest.config().hostbyname().host();
+                break;
+            default:
+                FAIL() << "Unsupported call type: " << calltype;
+        }
+
+        // Verify DNS server query status.
+        EXPECT_EQ(GetNumQueries(dns, name.c_str()), queries);
+        if (protocol == DnsProtocol::TLS) EXPECT_EQ(tls.queries(), static_cast<int>(queries));
+    }
+
+    static constexpr res_params kParams = {
+            .sample_validity = 300,
+            .success_threshold = 25,
+            .min_samples = 8,
+            .max_samples = 8,
+            .base_timeout_msec = 1000,
+            .retry_count = 2,
+    };
+    static constexpr android_net_context kNetcontext = {
+            .app_netid = TEST_NETID,
+            .app_mark = MARK_UNSET,
+            .dns_netid = TEST_NETID,
+            .dns_mark = MARK_UNSET,
+            .uid = NET_CONTEXT_INVALID_UID,
+    };
+    static constexpr android_net_context kNetcontextTls = {
+            .app_netid = TEST_NETID,
+            .app_mark = MARK_UNSET,
+            .dns_netid = TEST_NETID,
+            .dns_mark = MARK_UNSET,
+            .uid = NET_CONTEXT_INVALID_UID,
+            // Set TLS flags. See also maybeFixupNetContext() in
+            // packages/modules/DnsResolver/DnsProxyListener.cpp.
+            .flags = NET_CONTEXT_FLAG_USE_DNS_OVER_TLS | NET_CONTEXT_FLAG_USE_EDNS,
+    };
+};
+class ResolvGetAddrInfo : public TestBase {};
+
+// Fixture tests.
+TEST_F(ResolvGetAddrInfo, RemovePacketMapping) {
+    test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+    ASSERT_TRUE(dns.startServer());
+    ASSERT_NO_FATAL_FAILURE(SetResolvers());
+
+    dns.addMappingBinaryPacket(kHelloExampleComQueryV4, kHelloExampleComResponseV4);
+
+    addrinfo* res = nullptr;
+    const addrinfo hints = {.ai_family = AF_INET};
+    NetworkDnsEventReported event;
+    int rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+    ScopedAddrinfo result(res);
+    ASSERT_NE(result, nullptr);
+    ASSERT_EQ(rv, 0);
+    EXPECT_EQ(ToString(result), kHelloExampleComAddrV4);
+
+    // Remove existing DNS record.
+    dns.removeMappingBinaryPacket(kHelloExampleComQueryV4);
+
+    // Expect to have no answer in DNS query result.
+    rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+    result.reset(res);
+    ASSERT_EQ(result, nullptr);
+    ASSERT_EQ(rv, EAI_NODATA);
+}
+
+TEST_F(ResolvGetAddrInfo, ReplacePacketMapping) {
+    test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+    ASSERT_TRUE(dns.startServer());
+    ASSERT_NO_FATAL_FAILURE(SetResolvers());
+
+    // Register the record which uses IPv4 address 1.2.3.4.
+    dns.addMappingBinaryPacket(kHelloExampleComQueryV4, kHelloExampleComResponseV4);
+
+    // Expect that the DNS query returns IPv4 address 1.2.3.4.
+    addrinfo* res = nullptr;
+    const addrinfo hints = {.ai_family = AF_INET};
+    NetworkDnsEventReported event;
+    int rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+    ScopedAddrinfo result(res);
+    ASSERT_NE(result, nullptr);
+    ASSERT_EQ(rv, 0);
+    EXPECT_EQ(ToString(result), "1.2.3.4");
+
+    // Replace the registered record with a record which uses new IPv4 address 5.6.7.8.
+    std::vector<uint8_t> newHelloExampleComResponseV4 = {
+            /* Header */
+            0x00, 0x00, /* Transaction ID: 0x0000 */
+            0x81, 0x80, /* Flags: qr rd ra */
+            0x00, 0x01, /* Questions: 1 */
+            0x00, 0x01, /* Answer RRs: 1 */
+            0x00, 0x00, /* Authority RRs: 0 */
+            0x00, 0x00, /* Additional RRs: 0 */
+            /* Queries */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+            0x00, 0x01,                   /* Type: A */
+            0x00, 0x01,                   /* Class: IN */
+            /* Answers */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+            0x00, 0x01,                   /* Type: A */
+            0x00, 0x01,                   /* Class: IN */
+            0x00, 0x00, 0x00, 0x00,       /* Time to live: 0 */
+            0x00, 0x04,                   /* Data length: 4 */
+            0x05, 0x06, 0x07, 0x08        /* Address: 5.6.7.8 */
+    };
+    dns.addMappingBinaryPacket(kHelloExampleComQueryV4, newHelloExampleComResponseV4);
+
+    // Expect that DNS query returns new IPv4 address 5.6.7.8.
+    rv = resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontext, &res, &event);
+    result.reset(res);
+    ASSERT_NE(result, nullptr);
+    ASSERT_EQ(rv, 0);
+    EXPECT_EQ(ToString(result), "5.6.7.8");
+}
+
+TEST_F(ResolvGetAddrInfo, BasicTlsQuery) {
+    test::DNSResponder dns;
+    dns.addMapping(kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4);
+    dns.addMapping(kHelloExampleCom, ns_type::ns_t_aaaa, kHelloExampleComAddrV6);
+    ASSERT_TRUE(dns.startServer());
+
+    test::DnsTlsFrontend tls;
+    ASSERT_TRUE(tls.startServer());
+    ASSERT_NO_FATAL_FAILURE(SetResolversWithTls());
+    EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address()));
+
+    dns.clearQueries();
+    addrinfo* res = nullptr;
+    // If the socket type is not specified, every address will appear twice, once for
+    // SOCK_STREAM and one for SOCK_DGRAM. Just pick one because the addresses for
+    // the second query of different socket type are responded by the cache.
+    const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
+    NetworkDnsEventReported event;
+    const int rv =
+            resolv_getaddrinfo(kHelloExampleCom, nullptr, &hints, &kNetcontextTls, &res, &event);
+    ScopedAddrinfo result(res);
+    ASSERT_EQ(rv, 0);
+    EXPECT_EQ(GetNumQueries(dns, kHelloExampleCom), 2U);
+    const std::vector<std::string> result_strs = ToStrings(result);
+    EXPECT_THAT(result_strs, testing::UnorderedElementsAreArray(
+                                     {kHelloExampleComAddrV4, kHelloExampleComAddrV6}));
+    EXPECT_EQ(tls.queries(), 3);
+}
+
+// Parameterized test class definition.
+using GoldTestParamType = std::tuple<DnsProtocol, std::string /* filename */>;
+class ResolvGoldTest : public TestBase, public ::testing::WithParamInterface<GoldTestParamType> {
+  public:
+    // Generate readable string for test name from test parameters.
+    static std::string Name(::testing::TestParamInfo<GoldTestParamType> info) {
+        const auto& [protocol, file] = info.param;
+        std::string name = StringPrintf(
+                "%s_%s", protocol == DnsProtocol::CLEARTEXT ? "CLEARTEXT" : "TLS", file.c_str());
+        std::replace_if(
+                std::begin(name), std::end(name), [](char ch) { return !std::isalnum(ch); }, '_');
+        return name;
+    }
+};
+
+// GetAddrInfo tests.
+INSTANTIATE_TEST_SUITE_P(GetAddrInfo, ResolvGoldTest,
+                         ::testing::Combine(::testing::Values(DnsProtocol::CLEARTEXT),
+                                            ::testing::ValuesIn(kGoldFilesGetAddrInfo)),
+                         ResolvGoldTest::Name);
+INSTANTIATE_TEST_SUITE_P(GetAddrInfoTls, ResolvGoldTest,
+                         ::testing::Combine(::testing::Values(DnsProtocol::TLS),
+                                            ::testing::ValuesIn(kGoldFilesGetAddrInfoTls)),
+                         ResolvGoldTest::Name);
+
+// GetHostByName tests.
+INSTANTIATE_TEST_SUITE_P(GetHostByName, ResolvGoldTest,
+                         ::testing::Combine(::testing::Values(DnsProtocol::CLEARTEXT),
+                                            ::testing::ValuesIn(kGoldFilesGetHostByName)),
+                         ResolvGoldTest::Name);
+INSTANTIATE_TEST_SUITE_P(GetHostByNameTls, ResolvGoldTest,
+                         ::testing::Combine(::testing::Values(DnsProtocol::TLS),
+                                            ::testing::ValuesIn(kGoldFilesGetHostByNameTls)),
+                         ResolvGoldTest::Name);
+
+TEST_P(ResolvGoldTest, GoldData) {
+    const auto& [protocol, file] = GetParam();
+
+    // Setup DNS server configuration.
+    test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+    ASSERT_TRUE(dns.startServer());
+    test::DnsTlsFrontend tls;
+
+    if (protocol == DnsProtocol::CLEARTEXT) {
+        ASSERT_NO_FATAL_FAILURE(SetResolvers());
+    } else if (protocol == DnsProtocol::TLS) {
+        ASSERT_TRUE(tls.startServer());
+        ASSERT_NO_FATAL_FAILURE(SetResolversWithTls());
+        EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address()));
+        tls.clearQueries();
+    }
+
+    // Read test configuration from proto text file to proto.
+    const auto goldtest = ToProto(file);
+
+    // Register packet mappings (query, response) from proto.
+    SetupMappings(goldtest, dns);
+
+    // Verify the resolver by proto.
+    VerifyResolver(goldtest, dns, tls, protocol);
+}
+
+}  // namespace android::net
diff --git a/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
similarity index 76%
rename from resolv_integration_test.cpp
rename to tests/resolv_integration_test.cpp
index 27e21d2..6ea88da 100644
--- a/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -19,6 +19,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <android/multinetwork.h>  // ResNsendFlags
@@ -33,9 +34,10 @@
 #include <netdutils/InternetAddresses.h>
 #include <netdutils/NetworkConstants.h>  // SHA256_SIZE
 #include <netdutils/ResponseCode.h>
+#include <netdutils/Slice.h>
 #include <netdutils/SocketOption.h>
+#include <netdutils/Stopwatch.h>
 #include <netinet/in.h>
-#include <openssl/base64.h>
 #include <poll.h> /* poll */
 #include <private/android_filesystem_config.h>
 #include <resolv.h>
@@ -51,16 +53,18 @@
 #include <numeric>
 #include <thread>
 
+#include <aidl/android/net/IDnsResolver.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include "NetdClient.h"
 #include "ResolverStats.h"
-#include "android/net/IDnsResolver.h"
-#include "binder/IServiceManager.h"
-#include "netd_resolv/params.h"  // MAXNS
-#include "netid_client.h"        // NETID_UNSET
+#include "netid_client.h"  // NETID_UNSET
+#include "params.h"        // MAXNS
 #include "test_utils.h"
 #include "tests/dns_metrics_listener/dns_metrics_listener.h"
 #include "tests/dns_responder/dns_responder.h"
-#include "tests/dns_responder/dns_responder_client.h"
+#include "tests/dns_responder/dns_responder_client_ndk.h"
+#include "tests/dns_responder/dns_tls_certificate.h"
 #include "tests/dns_responder/dns_tls_frontend.h"
 #include "tests/resolv_test_utils.h"
 
@@ -71,26 +75,26 @@
 // Use maximum reserved appId for applications to avoid conflict with existing uids.
 static const int TEST_UID = 99999;
 
-// Currently the hostname of TLS server must match the CN filed on the server's certificate.
-// Inject a test CA whose hostname is "example.com" for DNS-OVER-TLS tests.
-static const std::string kDefaultPrivateDnsHostName = "example.com";
-static const std::string kDefaultIncorrectPrivateDnsHostName = "www.example.com";
-
 // Semi-public Bionic hook used by the NDK (frameworks/base/native/android/net.c)
 // Tested here for convenience.
 extern "C" int android_getaddrinfofornet(const char* hostname, const char* servname,
                                          const addrinfo* hints, unsigned netid, unsigned mark,
                                          struct addrinfo** result);
 
+using aidl::android::net::IDnsResolver;
+using aidl::android::net::INetd;
+using aidl::android::net::ResolverParamsParcel;
 using android::base::ParseInt;
 using android::base::StringPrintf;
 using android::base::unique_fd;
-using android::net::INetd;
 using android::net::ResolverStats;
 using android::net::metrics::DnsMetricsListener;
 using android::netdutils::enableSockopt;
+using android::netdutils::makeSlice;
 using android::netdutils::ResponseCode;
 using android::netdutils::ScopedAddrinfo;
+using android::netdutils::Stopwatch;
+using android::netdutils::toHex;
 
 // TODO: move into libnetdutils?
 namespace {
@@ -104,6 +108,13 @@
     return ScopedAddrinfo(result);
 }
 
+std::pair<ScopedAddrinfo, int> safe_getaddrinfo_time_taken(const char* node, const char* service,
+                                                           const addrinfo& hints) {
+    Stopwatch s;
+    ScopedAddrinfo result = safe_getaddrinfo(node, service, &hints);
+    return {std::move(result), s.timeTakenUs() / 1000};
+}
+
 }  // namespace
 
 class ResolverTest : public ::testing::Test {
@@ -113,49 +124,45 @@
         // Note that |mDnsClient| is not used for getting binder service in this static function.
         // The reason is that wants to keep |mDnsClient| as a non-static data member. |mDnsClient|
         // which sets up device network configuration could be independent from every test.
-        // TODO: Perhaps add a static function in resolv_test_utils.{cpp,h} to get binder service.
-        auto resolvBinder =
-                android::defaultServiceManager()->getService(android::String16("dnsresolver"));
-        auto resolvService = android::interface_cast<android::net::IDnsResolver>(resolvBinder);
+        // TODO: Perhaps add a static function in resolv_test_binder_utils.{cpp,h} to get binder
+        // service.
+
+        AIBinder* binder = AServiceManager_getService("dnsresolver");
+        ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(binder);
+        auto resolvService = aidl::android::net::IDnsResolver::fromBinder(resolvBinder);
         ASSERT_NE(nullptr, resolvService.get());
 
         // Subscribe the death recipient to the service IDnsResolver for detecting Netd death.
-        sResolvDeathRecipient = new ResolvDeathRecipient();
-        ASSERT_EQ(android::NO_ERROR, resolvBinder->linkToDeath(sResolvDeathRecipient));
-
-        // Subscribe the DNS listener for verifying DNS metrics event contents.
-        sDnsMetricsListener = new DnsMetricsListener(TEST_NETID /*monitor specific network*/);
-        ASSERT_TRUE(resolvService->registerEventListener(sDnsMetricsListener).isOk());
-
-        // Start the binder thread pool for listening DNS metrics events and receiving death
-        // recipient.
-        android::ProcessState::self()->startThreadPool();
-    }
-
-  protected:
-    struct DnsRecord {
-        std::string host_name;  // host name
-        ns_type type;           // record type
-        std::string addr;       // ipv4/v6 address
-    };
-
-    class ResolvDeathRecipient : public android::IBinder::DeathRecipient {
-      public:
-        ~ResolvDeathRecipient() override = default;
-
         // GTEST assertion macros are not invoked for generating a test failure in the death
         // recipient because the macros can't indicate failed test if Netd died between tests.
         // Moreover, continuing testing may have no meaningful after Netd death. Therefore, the
         // death recipient aborts process by GTEST_LOG_(FATAL) once Netd died.
-        void binderDied(const android::wp<android::IBinder>& /*who*/) override {
+        sResolvDeathRecipient = AIBinder_DeathRecipient_new([](void*) {
             constexpr char errorMessage[] = "Netd died";
             LOG(ERROR) << errorMessage;
             GTEST_LOG_(FATAL) << errorMessage;
-        }
-    };
+        });
+        ASSERT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, sResolvDeathRecipient, nullptr));
 
+        // Subscribe the DNS listener for verifying DNS metrics event contents.
+        sDnsMetricsListener = ndk::SharedRefBase::make<DnsMetricsListener>(
+                TEST_NETID /*monitor specific network*/);
+        ASSERT_TRUE(resolvService->registerEventListener(sDnsMetricsListener).isOk());
+
+        // Start the binder thread pool for listening DNS metrics events and receiving death
+        // recipient.
+        ABinderProcess_startThreadPool();
+    }
+    static void TearDownTestCase() { AIBinder_DeathRecipient_delete(sResolvDeathRecipient); }
+
+  protected:
     void SetUp() { mDnsClient.SetUp(); }
-    void TearDown() { mDnsClient.TearDown(); }
+    void TearDown() {
+        // Ensure the dump works at the end of each test.
+        DumpResolverService();
+
+        mDnsClient.TearDown();
+    }
 
     void StartDns(test::DNSResponder& dns, const std::vector<DnsRecord>& records) {
         for (const auto& r : records) {
@@ -166,6 +173,14 @@
         dns.clearQueries();
     }
 
+    void DumpResolverService() {
+        unique_fd fd(open("/dev/null", O_WRONLY));
+        EXPECT_EQ(mDnsClient.resolvService()->dump(fd, nullptr, 0), 0);
+
+        const char* querylogCmd[] = {"querylog"};  // Keep it sync with DnsQueryLog::DUMP_KEYWORD.
+        EXPECT_EQ(mDnsClient.resolvService()->dump(fd, querylogCmd, std::size(querylogCmd)), 0);
+    }
+
     bool WaitForNat64Prefix(ExpectNat64PrefixStatus status,
                             std::chrono::milliseconds timeout = std::chrono::milliseconds(1000)) {
         return sDnsMetricsListener->waitForNat64Prefix(status, timeout);
@@ -184,17 +199,17 @@
     // may temporarily hold lots of dead listeners until the unit test process terminates.
     // TODO: Perhaps add an unregistering listener binder call or fork a listener process which
     // could be terminated earlier.
-    static android::sp<DnsMetricsListener> sDnsMetricsListener;  // Initialized in SetUpTestCase.
+    static std::shared_ptr<DnsMetricsListener>
+            sDnsMetricsListener;  // Initialized in SetUpTestCase.
 
     // Use a shared static death recipient to monitor the service death. The static death
     // recipient could monitor the death not only during the test but also between tests.
-    static android::sp<ResolvDeathRecipient>
-            sResolvDeathRecipient;  // Initialized in SetUpTestCase.
+    static AIBinder_DeathRecipient* sResolvDeathRecipient;  // Initialized in SetUpTestCase.
 };
 
 // Initialize static member of class.
-android::sp<DnsMetricsListener> ResolverTest::sDnsMetricsListener;
-android::sp<ResolverTest::ResolvDeathRecipient> ResolverTest::sResolvDeathRecipient;
+std::shared_ptr<DnsMetricsListener> ResolverTest::sDnsMetricsListener;
+AIBinder_DeathRecipient* ResolverTest::sResolvDeathRecipient;
 
 TEST_F(ResolverTest, GetHostByName) {
     constexpr char nonexistent_host_name[] = "nonexistent.example.com.";
@@ -392,7 +407,6 @@
 }
 
 TEST_F(ResolverTest, BinderSerialization) {
-    using android::net::IDnsResolver;
     std::vector<int> params_offsets = {
             IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY,
             IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD,
@@ -410,9 +424,7 @@
 }
 
 TEST_F(ResolverTest, GetHostByName_Binder) {
-    using android::net::IDnsResolver;
-
-    std::vector<std::string> domains = { "example.com" };
+    std::vector<std::string> domains = {"example.com"};
     std::vector<std::unique_ptr<test::DNSResponder>> dns;
     std::vector<std::string> servers;
     std::vector<DnsResponderClient::Mapping> mappings;
@@ -442,9 +454,9 @@
     res_params res_params;
     std::vector<ResolverStats> res_stats;
     int wait_for_pending_req_timeout_count;
-    ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
-                                &res_tls_servers, &res_params, &res_stats,
-                                &wait_for_pending_req_timeout_count));
+    ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+            mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+            &res_params, &res_stats, &wait_for_pending_req_timeout_count));
     EXPECT_EQ(servers.size(), res_servers.size());
     EXPECT_EQ(domains.size(), res_domains.size());
     EXPECT_EQ(0U, res_tls_servers.size());
@@ -488,7 +500,7 @@
     // Could be A or AAAA
     std::string result_str = ToString(result);
     EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
-        << ", result_str='" << result_str << "'";
+            << ", result_str='" << result_str << "'";
 
     // Verify that the name is cached.
     size_t old_found = found;
@@ -498,8 +510,7 @@
     EXPECT_LE(1U, found);
     EXPECT_EQ(old_found, found);
     result_str = ToString(result);
-    EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
-        << result_str;
+    EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4") << result_str;
 
     // Change the DNS resolver, ensure that queries are still cached.
     ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr2}));
@@ -516,7 +527,7 @@
     // Could be A or AAAA
     result_str = ToString(result);
     EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
-        << ", result_str='" << result_str << "'";
+            << ", result_str='" << result_str << "'";
 }
 
 TEST_F(ResolverTest, GetAddrInfoV4) {
@@ -559,7 +570,6 @@
     const addrinfo hints = {
             .ai_family = AF_UNSPEC,
             .ai_socktype = SOCK_PACKET,
-            .ai_protocol = ANY,
     };
     addrinfo* result = nullptr;
     // This is a valid hint, but the query won't be sent because the socket type is
@@ -633,9 +643,9 @@
         res_params res_params;
         std::vector<ResolverStats> res_stats;
         int wait_for_pending_req_timeout_count;
-        ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers,
-                                    &res_domains, &res_tls_servers, &res_params, &res_stats,
-                                    &wait_for_pending_req_timeout_count));
+        ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+                mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
+                &res_tls_servers, &res_params, &res_stats, &wait_for_pending_req_timeout_count));
         EXPECT_EQ(0, wait_for_pending_req_timeout_count);
     });
 
@@ -725,7 +735,7 @@
 
 TEST_F(ResolverTest, MultidomainResolution) {
     constexpr char host_name[] = "nihao.example2.com.";
-    std::vector<std::string> searchDomains = { "example1.com", "example2.com", "example3.com" };
+    std::vector<std::string> searchDomains = {"example1.com", "example2.com", "example3.com"};
 
     test::DNSResponder dns("127.0.0.6");
     StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.3"}});
@@ -775,10 +785,10 @@
     StartDns(dns0, {{host_name, ns_type::ns_t_aaaa, "2001:db8::5"}});
     StartDns(dns1, {{host_name, ns_type::ns_t_aaaa, "2001:db8::6"}});
 
-    std::vector<std::string> servers = { listen_addr0, listen_addr1 };
+    std::vector<std::string> servers = {listen_addr0, listen_addr1};
     // <sample validity in s> <success threshold in percent> <min samples> <max samples>
     int sample_count = 8;
-    const std::vector<int> params = { 300, 25, sample_count, sample_count };
+    const std::vector<int> params = {300, 25, sample_count, sample_count};
     ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, kDefaultSearchDomains, params));
 
     // Repeatedly perform resolutions for non-existing domains until MAXNSSAMPLES resolutions have
@@ -868,11 +878,11 @@
     StartDns(dns1, {{host_name, ns_type::ns_t_aaaa, "2001:db8::6"}});
     StartDns(dns2, {{host_name, ns_type::ns_t_aaaa, "2001:db8::7"}});
 
-    const std::vector<std::string> servers = { listen_addr0, listen_addr1, listen_addr2 };
+    const std::vector<std::string> servers = {listen_addr0, listen_addr1, listen_addr2};
     std::vector<std::thread> threads(10);
     for (std::thread& thread : threads) {
-       thread = std::thread([this, &servers]() {
-            unsigned delay = arc4random_uniform(1*1000*1000); // <= 1s
+        thread = std::thread([this, &servers]() {
+            unsigned delay = arc4random_uniform(1 * 1000 * 1000);  // <= 1s
             usleep(delay);
             std::vector<std::string> serverSubset;
             for (const auto& server : servers) {
@@ -902,14 +912,238 @@
     res_params res_params;
     std::vector<ResolverStats> res_stats;
     int wait_for_pending_req_timeout_count;
-    ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
-                                &res_tls_servers, &res_params, &res_stats,
-                                &wait_for_pending_req_timeout_count));
+    ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+            mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+            &res_params, &res_stats, &wait_for_pending_req_timeout_count));
     EXPECT_EQ(0, wait_for_pending_req_timeout_count);
 }
 
+TEST_F(ResolverTest, SkipBadServersDueToInternalError) {
+    constexpr char listen_addr1[] = "fe80::1";
+    constexpr char listen_addr2[] = "255.255.255.255";
+    constexpr char listen_addr3[] = "127.0.0.3";
+
+    test::DNSResponder dns(listen_addr3);
+    ASSERT_TRUE(dns.startServer());
+
+    ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+    parcel.servers = {listen_addr1, listen_addr2, listen_addr3};
+
+    // Bad servers can be distinguished after two attempts.
+    parcel.minSamples = 2;
+    ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+    // Start querying five times.
+    for (int i = 0; i < 5; i++) {
+        std::string hostName = StringPrintf("hello%d.com.", i);
+        dns.addMapping(hostName, ns_type::ns_t_a, "1.2.3.4");
+        const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+        EXPECT_TRUE(safe_getaddrinfo(hostName.c_str(), nullptr, &hints) != nullptr);
+    }
+
+    std::vector<std::string> res_servers;
+    std::vector<std::string> res_domains;
+    std::vector<std::string> res_tls_servers;
+    res_params res_params;
+    std::vector<ResolverStats> res_stats;
+    int wait_for_pending_req_timeout_count;
+    ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+            mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+            &res_params, &res_stats, &wait_for_pending_req_timeout_count));
+
+    // Verify the result by means of the statistics.
+    EXPECT_EQ(res_stats[0].successes, 0);
+    EXPECT_EQ(res_stats[1].successes, 0);
+    EXPECT_EQ(res_stats[2].successes, 5);
+    EXPECT_EQ(res_stats[0].internal_errors, 2);
+    EXPECT_EQ(res_stats[1].internal_errors, 2);
+    EXPECT_EQ(res_stats[2].internal_errors, 0);
+}
+
+TEST_F(ResolverTest, SkipBadServersDueToTimeout) {
+    constexpr char listen_addr1[] = "127.0.0.3";
+    constexpr char listen_addr2[] = "127.0.0.4";
+
+    // Set dns1 non-responsive and dns2 workable.
+    test::DNSResponder dns1(listen_addr1, test::kDefaultListenService, static_cast<ns_rcode>(-1));
+    test::DNSResponder dns2(listen_addr2);
+    dns1.setResponseProbability(0.0);
+    ASSERT_TRUE(dns1.startServer());
+    ASSERT_TRUE(dns2.startServer());
+
+    ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+    parcel.servers = {listen_addr1, listen_addr2};
+
+    // Bad servers can be distinguished after two attempts.
+    parcel.minSamples = 2;
+    ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
+
+    // Start querying five times.
+    for (int i = 0; i < 5; i++) {
+        std::string hostName = StringPrintf("hello%d.com.", i);
+        dns1.addMapping(hostName, ns_type::ns_t_a, "1.2.3.4");
+        dns2.addMapping(hostName, ns_type::ns_t_a, "1.2.3.5");
+        const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+        EXPECT_TRUE(safe_getaddrinfo(hostName.c_str(), nullptr, &hints) != nullptr);
+    }
+
+    std::vector<std::string> res_servers;
+    std::vector<std::string> res_domains;
+    std::vector<std::string> res_tls_servers;
+    res_params res_params;
+    std::vector<ResolverStats> res_stats;
+    int wait_for_pending_req_timeout_count;
+    ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+            mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+            &res_params, &res_stats, &wait_for_pending_req_timeout_count));
+
+    // Verify the result by means of the statistics as well as the query counts.
+    EXPECT_EQ(res_stats[0].successes, 0);
+    EXPECT_EQ(res_stats[1].successes, 5);
+    EXPECT_EQ(res_stats[0].timeouts, 2);
+    EXPECT_EQ(res_stats[1].timeouts, 0);
+    EXPECT_EQ(dns1.queries().size(), 2U);
+    EXPECT_EQ(dns2.queries().size(), 5U);
+}
+
+TEST_F(ResolverTest, GetAddrInfoFromCustTable_InvalidInput) {
+    constexpr char hostnameNoip[] = "noip.example.com.";
+    constexpr char hostnameInvalidip[] = "invalidip.example.com.";
+    const std::vector<aidl::android::net::ResolverHostsParcel> invalidCustHosts = {
+            {"", hostnameNoip},
+            {"wrong IP", hostnameInvalidip},
+    };
+    test::DNSResponder dns;
+    StartDns(dns, {});
+    auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+    resolverParams.experimentalOptions.hosts = invalidCustHosts;
+    ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+    for (const auto& hostname : {hostnameNoip, hostnameInvalidip}) {
+        // The query won't get data from customized table because of invalid customized table
+        // and DNSResponder also has no records. hostnameNoip has never registered and
+        // hostnameInvalidip has registered but wrong IP.
+        const addrinfo hints = {.ai_family = AF_UNSPEC};
+        ScopedAddrinfo result = safe_getaddrinfo(hostname, nullptr, &hints);
+        ASSERT_TRUE(result == nullptr);
+        EXPECT_EQ(4U, GetNumQueries(dns, hostname));
+    }
+}
+
+TEST_F(ResolverTest, GetAddrInfoFromCustTable) {
+    constexpr char hostnameV4[] = "v4only.example.com.";
+    constexpr char hostnameV6[] = "v6only.example.com.";
+    constexpr char hostnameV4V6[] = "v4v6.example.com.";
+    constexpr char custAddrV4[] = "1.2.3.4";
+    constexpr char custAddrV6[] = "::1.2.3.4";
+    constexpr char dnsSvAddrV4[] = "1.2.3.5";
+    constexpr char dnsSvAddrV6[] = "::1.2.3.5";
+    const std::vector<aidl::android::net::ResolverHostsParcel> custHostV4 = {
+            {custAddrV4, hostnameV4},
+    };
+    const std::vector<aidl::android::net::ResolverHostsParcel> custHostV6 = {
+            {custAddrV6, hostnameV6},
+    };
+    const std::vector<aidl::android::net::ResolverHostsParcel> custHostV4V6 = {
+            {custAddrV4, hostnameV4V6},
+            {custAddrV6, hostnameV4V6},
+    };
+    const std::vector<DnsRecord> dnsSvHostV4 = {
+            {hostnameV4, ns_type::ns_t_a, dnsSvAddrV4},
+    };
+    const std::vector<DnsRecord> dnsSvHostV6 = {
+            {hostnameV6, ns_type::ns_t_aaaa, dnsSvAddrV6},
+    };
+    const std::vector<DnsRecord> dnsSvHostV4V6 = {
+            {hostnameV4V6, ns_type::ns_t_a, dnsSvAddrV4},
+            {hostnameV4V6, ns_type::ns_t_aaaa, dnsSvAddrV6},
+    };
+    struct TestConfig {
+        const std::string name;
+        const std::vector<aidl::android::net::ResolverHostsParcel> customizedHosts;
+        const std::vector<DnsRecord> dnsserverHosts;
+        const std::vector<std::string> queryResult;
+        std::string asParameters() const {
+            return StringPrintf("name: %s, customizedHosts: %s, dnsserverHosts: %s", name.c_str(),
+                                customizedHosts.empty() ? "No" : "Yes",
+                                dnsserverHosts.empty() ? "No" : "Yes");
+        }
+    } testConfigs[]{
+            // clang-format off
+            {hostnameV4,    {},            {},             {}},
+            {hostnameV4,    {},            dnsSvHostV4,    {dnsSvAddrV4}},
+            {hostnameV4,    custHostV4,    {},             {custAddrV4}},
+            {hostnameV4,    custHostV4,    dnsSvHostV4,    {custAddrV4}},
+            {hostnameV6,    {},            {},             {}},
+            {hostnameV6,    {},            dnsSvHostV6,    {dnsSvAddrV6}},
+            {hostnameV6,    custHostV6,    {},             {custAddrV6}},
+            {hostnameV6,    custHostV6,    dnsSvHostV6,    {custAddrV6}},
+            {hostnameV4V6,  {},            {},             {}},
+            {hostnameV4V6,  {},            dnsSvHostV4V6,  {dnsSvAddrV4, dnsSvAddrV6}},
+            {hostnameV4V6,  custHostV4V6,  {},             {custAddrV4, custAddrV6}},
+            {hostnameV4V6,  custHostV4V6,  dnsSvHostV4V6,  {custAddrV4, custAddrV6}},
+            // clang-format on
+    };
+
+    for (const auto& config : testConfigs) {
+        SCOPED_TRACE(config.asParameters());
+
+        test::DNSResponder dns;
+        StartDns(dns, config.dnsserverHosts);
+
+        auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+        resolverParams.experimentalOptions.hosts = config.customizedHosts;
+        ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+        const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
+        ScopedAddrinfo result = safe_getaddrinfo(config.name.c_str(), nullptr, &hints);
+        if (config.customizedHosts.empty() && config.dnsserverHosts.empty()) {
+            ASSERT_TRUE(result == nullptr);
+            EXPECT_EQ(2U, GetNumQueries(dns, config.name.c_str()));
+        } else {
+            ASSERT_TRUE(result != nullptr);
+            EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray(config.queryResult));
+            EXPECT_EQ(config.customizedHosts.empty() ? 2U : 0U,
+                      GetNumQueries(dns, config.name.c_str()));
+        }
+
+        EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+    }
+}
+
+TEST_F(ResolverTest, GetAddrInfoFromCustTable_Modify) {
+    constexpr char hostnameV4V6[] = "v4v6.example.com.";
+    constexpr char custAddrV4[] = "1.2.3.4";
+    constexpr char custAddrV6[] = "::1.2.3.4";
+    constexpr char dnsSvAddrV4[] = "1.2.3.5";
+    constexpr char dnsSvAddrV6[] = "::1.2.3.5";
+    const std::vector<DnsRecord> dnsSvHostV4V6 = {
+            {hostnameV4V6, ns_type::ns_t_a, dnsSvAddrV4},
+            {hostnameV4V6, ns_type::ns_t_aaaa, dnsSvAddrV6},
+    };
+    const std::vector<aidl::android::net::ResolverHostsParcel> custHostV4V6 = {
+            {custAddrV4, hostnameV4V6},
+            {custAddrV6, hostnameV4V6},
+    };
+    test::DNSResponder dns;
+    StartDns(dns, dnsSvHostV4V6);
+    auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+
+    resolverParams.experimentalOptions.hosts = custHostV4V6;
+    ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+    const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
+    ScopedAddrinfo result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
+    ASSERT_TRUE(result != nullptr);
+    EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
+    EXPECT_EQ(0U, GetNumQueries(dns, hostnameV4V6));
+
+    resolverParams.experimentalOptions.hosts = {};
+    ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+    result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
+    ASSERT_TRUE(result != nullptr);
+    EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray({dnsSvAddrV4, dnsSvAddrV6}));
+    EXPECT_EQ(2U, GetNumQueries(dns, hostnameV4V6));
+}
+
 TEST_F(ResolverTest, EmptySetup) {
-    using android::net::IDnsResolver;
     std::vector<std::string> servers;
     std::vector<std::string> domains;
     ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains));
@@ -919,9 +1153,9 @@
     res_params res_params;
     std::vector<ResolverStats> res_stats;
     int wait_for_pending_req_timeout_count;
-    ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
-                                &res_tls_servers, &res_params, &res_stats,
-                                &wait_for_pending_req_timeout_count));
+    ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+            mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+            &res_params, &res_stats, &wait_for_pending_req_timeout_count));
     EXPECT_EQ(0U, res_servers.size());
     EXPECT_EQ(0U, res_domains.size());
     EXPECT_EQ(0U, res_tls_servers.size());
@@ -941,8 +1175,8 @@
     constexpr char listen_addr[] = "127.0.0.13";
     constexpr char host_name1[] = "test13.domain1.org.";
     constexpr char host_name2[] = "test13.domain2.org.";
-    std::vector<std::string> servers = { listen_addr };
-    std::vector<std::string> domains = { "domain1.org" };
+    std::vector<std::string> servers = {listen_addr};
+    std::vector<std::string> domains = {"domain1.org"};
 
     const std::vector<DnsRecord> records = {
             {host_name1, ns_type::ns_t_aaaa, "2001:db8::13"},
@@ -960,7 +1194,7 @@
     EXPECT_EQ("2001:db8::13", ToString(result));
 
     // Test that changing the domain search path on its own works.
-    domains = { "domain2.org" };
+    domains = {"domain2.org"};
     ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, domains));
     dns.clearQueries();
 
@@ -973,7 +1207,7 @@
 
 namespace {
 
-std::vector<std::string> getResolverDomains(android::net::IDnsResolver* dnsResolverService,
+std::vector<std::string> getResolverDomains(aidl::android::net::IDnsResolver* dnsResolverService,
                                             unsigned netId) {
     std::vector<std::string> res_servers;
     std::vector<std::string> res_domains;
@@ -981,8 +1215,9 @@
     res_params res_params;
     std::vector<ResolverStats> res_stats;
     int wait_for_pending_req_timeout_count;
-    GetResolverInfo(dnsResolverService, netId, &res_servers, &res_domains, &res_tls_servers,
-                    &res_params, &res_stats, &wait_for_pending_req_timeout_count);
+    DnsResponderClient::GetResolverInfo(dnsResolverService, netId, &res_servers, &res_domains,
+                                        &res_tls_servers, &res_params, &res_stats,
+                                        &wait_for_pending_req_timeout_count);
     return res_domains;
 }
 
@@ -1105,9 +1340,9 @@
     res_params res_params;
     std::vector<ResolverStats> res_stats;
     int wait_for_pending_req_timeout_count;
-    ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
-                                &res_tls_servers, &res_params, &res_stats,
-                                &wait_for_pending_req_timeout_count));
+    ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+            mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+            &res_params, &res_stats, &wait_for_pending_req_timeout_count));
 
     // Check the size of the stats and its contents.
     EXPECT_EQ(static_cast<size_t>(MAXNS), res_servers.size());
@@ -1155,9 +1390,9 @@
     res_params res_params;
     std::vector<ResolverStats> res_stats;
     int wait_for_pending_req_timeout_count;
-    ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
-                                &res_tls_servers, &res_params, &res_stats,
-                                &wait_for_pending_req_timeout_count));
+    ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+            mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains, &res_tls_servers,
+            &res_params, &res_stats, &wait_for_pending_req_timeout_count));
 
     EXPECT_EQ(1, res_stats[0].timeouts);
     EXPECT_EQ(1, res_stats[1].errors);
@@ -1171,7 +1406,7 @@
 
     test::DNSResponder dns;
     StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.3"}});
-    std::vector<std::string> servers = { listen_addr };
+    std::vector<std::string> servers = {listen_addr};
 
     // There's nothing listening on this address, so validation will either fail or
     /// hang.  Either way, queries will continue to flow to the DNSResponder.
@@ -1199,14 +1434,14 @@
 
     test::DNSResponder dns;
     StartDns(dns, records);
-    std::vector<std::string> servers = { listen_addr };
+    std::vector<std::string> servers = {listen_addr};
 
     // Bind the specified private DNS socket but don't respond to any client sockets yet.
     int s = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
     ASSERT_TRUE(s >= 0);
     struct sockaddr_in tlsServer = {
-        .sin_family = AF_INET,
-        .sin_port = htons(853),
+            .sin_family = AF_INET,
+            .sin_port = htons(853),
     };
     ASSERT_TRUE(inet_pton(AF_INET, listen_addr, &tlsServer.sin_addr));
     ASSERT_TRUE(enableSockopt(s, SOL_SOCKET, SO_REUSEPORT).ok());
@@ -1219,7 +1454,7 @@
 
     struct sockaddr_storage cliaddr;
     socklen_t sin_size = sizeof(cliaddr);
-    int new_fd = accept4(s, reinterpret_cast<struct sockaddr *>(&cliaddr), &sin_size, SOCK_CLOEXEC);
+    int new_fd = accept4(s, reinterpret_cast<struct sockaddr*>(&cliaddr), &sin_size, SOCK_CLOEXEC);
     ASSERT_TRUE(new_fd > 0);
 
     // We've received the new file descriptor but not written to it or closed, so the
@@ -1261,7 +1496,7 @@
 
     test::DNSResponder dns;
     StartDns(dns, records);
-    std::vector<std::string> servers = { listen_addr };
+    std::vector<std::string> servers = {listen_addr};
 
     test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
     ASSERT_TRUE(tls.startServer());
@@ -1273,7 +1508,7 @@
     EXPECT_EQ("1.2.3.1", ToString(result));
 
     // Wait for query to get counted.
-    EXPECT_TRUE(tls.waitForQueries(2, 5000));
+    EXPECT_TRUE(tls.waitForQueries(2));
 
     // Stop the TLS server.  Since we're in opportunistic mode, queries will
     // fall back to the locally-assigned (clear text) nameservers.
@@ -1285,8 +1520,8 @@
     EXPECT_EQ("1.2.3.2", ToString(result));
     const auto queries = dns.queries();
     EXPECT_EQ(1U, queries.size());
-    EXPECT_EQ("tls2.example.com.", queries[0].first);
-    EXPECT_EQ(ns_t_a, queries[0].second);
+    EXPECT_EQ("tls2.example.com.", queries[0].name);
+    EXPECT_EQ(ns_t_a, queries[0].type);
 
     // Reset the resolvers without enabling TLS.  Queries should still be routed
     // to the UDP endpoint.
@@ -1318,7 +1553,7 @@
     StartDns(dns1, records1);
     StartDns(dns2, records2);
 
-    std::vector<std::string> servers = { listen_addr1, listen_addr2 };
+    std::vector<std::string> servers = {listen_addr1, listen_addr2};
 
     test::DnsTlsFrontend tls1(listen_addr1, listen_tls, listen_addr1, listen_udp);
     test::DnsTlsFrontend tls2(listen_addr2, listen_tls, listen_addr2, listen_udp);
@@ -1334,7 +1569,7 @@
     EXPECT_EQ("1.2.3.1", ToString(result));
 
     // Wait for query to get counted.
-    EXPECT_TRUE(tls1.waitForQueries(2, 5000));
+    EXPECT_TRUE(tls1.waitForQueries(2));
     // No new queries should have reached tls2.
     EXPECT_EQ(1, tls2.queries());
 
@@ -1345,7 +1580,7 @@
     EXPECT_EQ("1.2.3.4", ToString(result));
 
     // Wait for query to get counted.
-    EXPECT_TRUE(tls2.waitForQueries(2, 5000));
+    EXPECT_TRUE(tls2.waitForQueries(2));
 
     // No additional queries should have reached the insecure servers.
     EXPECT_EQ(2U, dns1.queries().size());
@@ -1363,7 +1598,7 @@
 
     test::DNSResponder dns;
     StartDns(dns, {{host_name, ns_type::ns_t_a, "1.2.3.1"}});
-    std::vector<std::string> servers = { listen_addr };
+    std::vector<std::string> servers = {listen_addr};
 
     test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
     ASSERT_TRUE(tls.startServer());
@@ -1393,7 +1628,7 @@
 
     test::DNSResponder dns;
     StartDns(dns, records);
-    std::vector<std::string> servers = { listen_addr };
+    std::vector<std::string> servers = {listen_addr};
 
     test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
     ASSERT_TRUE(tls.startServer());
@@ -1409,9 +1644,9 @@
     // Could be A or AAAA
     std::string result_str = ToString(result);
     EXPECT_TRUE(result_str == "1.2.3.4" || result_str == "::1.2.3.4")
-        << ", result_str='" << result_str << "'";
+            << ", result_str='" << result_str << "'";
     // Wait for both A and AAAA queries to get counted.
-    EXPECT_TRUE(tls.waitForQueries(3, 5000));
+    EXPECT_TRUE(tls.waitForQueries(3));
 
     // Clear TLS bit.
     ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
@@ -1434,7 +1669,7 @@
     const char cleartext_addr[] = "127.0.0.53";
     const char cleartext_port[] = "53";
     const char tls_port[] = "853";
-    const std::vector<std::string> servers = { cleartext_addr };
+    const std::vector<std::string> servers = {cleartext_addr};
 
     test::DNSResponder dns(cleartext_addr);
     ASSERT_TRUE(dns.startServer());
@@ -1442,15 +1677,14 @@
     test::DnsTlsFrontend tls(cleartext_addr, tls_port, cleartext_addr, cleartext_port);
     ASSERT_TRUE(tls.startServer());
 
+    // clang-format off
     struct TestConfig {
         const std::string mode;
         const bool withWorkingTLS;
         const std::string method;
 
         std::string asHostName() const {
-            return StringPrintf("%s.%s.%s.",
-                                mode.c_str(),
-                                withWorkingTLS ? "tlsOn" : "tlsOff",
+            return StringPrintf("%s.%s.%s.", mode.c_str(), withWorkingTLS ? "tlsOn" : "tlsOff",
                                 method.c_str());
         }
     } testConfigs[]{
@@ -1473,6 +1707,7 @@
         {OPPORTUNISTIC, false, GETADDRINFOFORNET},
         {STRICT,        false, GETADDRINFOFORNET},
     };
+    // clang-format on
 
     for (const auto& config : testConfigs) {
         const std::string testHostName = config.asHostName();
@@ -1496,21 +1731,19 @@
         if (config.mode == OFF) {
             ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers, kDefaultSearchDomains,
                                                           kDefaultParams));
-        } else if (config.mode == OPPORTUNISTIC) {
+        } else /* OPPORTUNISTIC or STRICT */ {
+            const char* tls_hostname = (config.mode == STRICT) ? kDefaultPrivateDnsHostName : "";
             ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
-                                                       kDefaultParams, ""));
+                                                       kDefaultParams, tls_hostname));
 
             // Wait for the validation event. If the server is running, the validation should
-            // be successful; otherwise, the validation should be failed.
+            // succeed; otherwise, the validation should fail.
             EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), config.withWorkingTLS));
-        } else if (config.mode == STRICT) {
-            ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains,
-                                                       kDefaultParams, kDefaultPrivateDnsHostName));
-
-            // Wait for the validation event.
-            EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), config.withWorkingTLS));
+            if (config.withWorkingTLS) {
+                EXPECT_TRUE(tls.waitForQueries(1));
+                tls.clearQueries();
+            }
         }
-        tls.clearQueries();
 
         const hostent* h_result = nullptr;
         ScopedAddrinfo ai_result;
@@ -1534,7 +1767,7 @@
             // Could be A or AAAA
             const std::string result_str = ToString(ai_result);
             EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
-                << ", result_str='" << result_str << "'";
+                    << ", result_str='" << result_str << "'";
         } else if (config.method == GETADDRINFOFORNET) {
             addrinfo* raw_ai_result = nullptr;
             EXPECT_EQ(0, android_getaddrinfofornet(host_name, /*servname=*/nullptr,
@@ -1546,7 +1779,7 @@
             // Could be A or AAAA
             const std::string result_str = ToString(ai_result);
             EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
-                << ", result_str='" << result_str << "'";
+                    << ", result_str='" << result_str << "'";
         }
 
         EXPECT_EQ(0, tls.queries());
@@ -1559,7 +1792,7 @@
 
 TEST_F(ResolverTest, StrictMode_NoTlsServers) {
     constexpr char cleartext_addr[] = "127.0.0.53";
-    const std::vector<std::string> servers = { cleartext_addr };
+    const std::vector<std::string> servers = {cleartext_addr};
     constexpr char host_name[] = "strictmode.notlsips.example.com.";
     const std::vector<DnsRecord> records = {
             {host_name, ns_type::ns_t_a, "1.2.3.4"},
@@ -1604,12 +1837,12 @@
     int ancount, n = 0;
     ns_rr rr;
 
-    if (ns_initparse((const uint8_t*) buf, bufLen, &handle) >= 0) {
+    if (ns_initparse((const uint8_t*)buf, bufLen, &handle) >= 0) {
         ancount = ns_msg_count(handle, ns_s_an);
         if (ns_parserr(&handle, ns_s_an, n, &rr) == 0) {
             const uint8_t* rdata = ns_rr_rdata(rr);
             char buffer[INET6_ADDRSTRLEN];
-            if (inet_ntop(ipType, (const char*) rdata, buffer, sizeof(buffer))) {
+            if (inet_ntop(ipType, (const char*)rdata, buffer, sizeof(buffer))) {
                 return buffer;
             }
         }
@@ -1630,7 +1863,7 @@
             .sun_path = "/dev/socket/dnsproxyd",
     };
 
-    if (TEMP_FAILURE_RETRY(connect(s, (const struct sockaddr*) &proxy_addr, sizeof(proxy_addr))) !=
+    if (TEMP_FAILURE_RETRY(connect(s, (const struct sockaddr*)&proxy_addr, sizeof(proxy_addr))) !=
         0) {
         close(s);
         return -1;
@@ -2146,13 +2379,14 @@
     const char CLEARTEXT_ADDR[] = "127.0.0.53";
     const char CLEARTEXT_PORT[] = "53";
     const char TLS_PORT[] = "853";
-    const std::vector<std::string> servers = { CLEARTEXT_ADDR };
+    const std::vector<std::string> servers = {CLEARTEXT_ADDR};
 
     test::DNSResponder dns(CLEARTEXT_ADDR, CLEARTEXT_PORT, ns_rcode::ns_r_servfail);
     ASSERT_TRUE(dns.startServer());
 
     test::DnsTlsFrontend tls(CLEARTEXT_ADDR, TLS_PORT, CLEARTEXT_ADDR, CLEARTEXT_PORT);
 
+    // clang-format off
     static const struct TestConfig {
         std::string mode;
         std::string method;
@@ -2208,6 +2442,7 @@
             //{OPPORTUNISTIC_TLS, GETADDRINFO,   Edns::DROP,   EXPECT_FAILURE},
             //{STRICT,            GETADDRINFO,   Edns::DROP,   EXPECT_FAILURE},
     };
+    // clang-format on
 
     for (const auto& config : testConfigs) {
         const std::string testHostName = config.asHostName();
@@ -2515,6 +2750,7 @@
     EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
     EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
 
+    // clang-format off
     static const struct TestConfig {
         std::string name;
         std::string addr;
@@ -2527,6 +2763,7 @@
         {MULTICAST,         ADDR_MULTICAST},
         {LIMITED_BROADCAST, ADDR_LIMITED_BROADCAST}
     };
+    // clang-format on
 
     for (const auto& config : testConfigs) {
         const std::string testHostName = config.asHostName();
@@ -2619,6 +2856,7 @@
     EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
     EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
 
+    // clang-format off
     // If node is null, return address is listed by libc/getaddrinfo.c as follows.
     // - passive socket -> anyaddr (0.0.0.0 or ::)
     // - non-passive socket -> localhost (127.0.0.1 or ::1)
@@ -2635,6 +2873,7 @@
         {0 /* non-passive */, ADDR_LOCALHOST_V4, ADDR_LOCALHOST_V6},
         {AI_PASSIVE,          ADDR_ANYADDR_V4,   ADDR_ANYADDR_V6}
     };
+    // clang-format on
 
     for (const auto& config : testConfigs) {
         SCOPED_TRACE(config.asParameters());
@@ -2799,7 +3038,7 @@
     // Expect no DNS queries; localhost is resolved via /etc/hosts.
     EXPECT_EQ(0U, GetNumQueries(dns, host_name));
 
-    ASSERT_EQ(sizeof(in6_addr), (unsigned) result->h_length);
+    ASSERT_EQ(sizeof(in6_addr), (unsigned)result->h_length);
     ASSERT_EQ(AF_INET6, result->h_addrtype);
     std::string result_str = ToString(result);
     EXPECT_EQ(result_str, host_addr);
@@ -2863,6 +3102,7 @@
     EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
     EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
 
+    // clang-format off
     static const struct TestConfig {
         int flag;
         int family;
@@ -2883,6 +3123,7 @@
         {0,              AF_INET6, "2001:db8::102:304", "v4v6.example.com"},
         {0,              AF_INET6, "2001:db8::506:708", "2001:db8::506:708"}, // unmapped
     };
+    // clang-format on
 
     // Reverse IPv4/IPv6 DNS query. Prefix should have no effect on it.
     for (const auto& config : testConfigs) {
@@ -2896,14 +3137,14 @@
             memset(&sin, 0, sizeof(sin));
             sin.sin_family = AF_INET;
             inet_pton(AF_INET, config.addr.c_str(), &sin.sin_addr);
-            rv = getnameinfo((const struct sockaddr*) &sin, sizeof(sin), host, sizeof(host),
-                             nullptr, 0, config.flag);
+            rv = getnameinfo((const struct sockaddr*)&sin, sizeof(sin), host, sizeof(host), nullptr,
+                             0, config.flag);
             if (config.flag == NI_NAMEREQD) EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v4));
         } else if (config.family == AF_INET6) {
             memset(&sin6, 0, sizeof(sin6));
             sin6.sin6_family = AF_INET6;
             inet_pton(AF_INET6, config.addr.c_str(), &sin6.sin6_addr);
-            rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host),
+            rv = getnameinfo((const struct sockaddr*)&sin6, sizeof(sin6), host, sizeof(host),
                              nullptr, 0, config.flag);
             if (config.flag == NI_NAMEREQD) EXPECT_LE(1U, GetNumQueries(dns, ptr_addr_v6));
         }
@@ -2942,6 +3183,7 @@
     EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
     EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
 
+    // clang-format off
     static const struct TestConfig {
         bool hasSynthesizedPtrRecord;
         int flag;
@@ -2960,6 +3202,7 @@
         {true,  NI_NUMERICHOST, "64:ff9b::506:708", "64:ff9b::506:708"},
         {true,  0,              "64:ff9b::506:708", "v6synthesis.example.com"}
     };
+    // clang-format on
 
     // hasSynthesizedPtrRecord = false
     //   Synthesized PTR record doesn't exist on DNS server
@@ -2979,7 +3222,7 @@
         memset(&sin6, 0, sizeof(sin6));
         sin6.sin6_family = AF_INET6;
         inet_pton(AF_INET6, config.addr.c_str(), &sin6.sin6_addr);
-        int rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host),
+        int rv = getnameinfo((const struct sockaddr*)&sin6, sizeof(sin6), host, sizeof(host),
                              nullptr, 0, config.flag);
         ASSERT_EQ(0, rv);
         if (config.flag == NI_NAMEREQD) {
@@ -3004,6 +3247,7 @@
     constexpr char listen_addr[] = "::1";
 
     test::DNSResponder dns(listen_addr);
+
     StartDns(dns, {{dns64_name, ns_type::ns_t_aaaa, "64:ff9b::192.0.0.170"}});
     const std::vector<std::string> servers = {listen_addr};
     ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
@@ -3018,7 +3262,7 @@
     char host[NI_MAXHOST];
     struct sockaddr_in6 sin6 = {.sin6_family = AF_INET6};
     inet_pton(AF_INET6, host_addr, &sin6.sin6_addr);
-    int rv = getnameinfo((const struct sockaddr*) &sin6, sizeof(sin6), host, sizeof(host), nullptr,
+    int rv = getnameinfo((const struct sockaddr*)&sin6, sizeof(sin6), host, sizeof(host), nullptr,
                          0, NI_NAMEREQD);
     ASSERT_EQ(0, rv);
     // Expect no DNS queries; localhost is resolved via /etc/hosts.
@@ -3148,13 +3392,13 @@
     EXPECT_TRUE(mDnsClient.resolvService()->startPrefix64Discovery(TEST_NETID).isOk());
     EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
 
+    // clang-format off
     static const struct TestConfig {
         std::string name;
         std::string addr;
 
         std::string asHostName() const {
-            return StringPrintf("%s.example.com.",
-                                name.c_str());
+            return StringPrintf("%s.example.com.", name.c_str());
         }
     } testConfigs[]{
         {THIS_NETWORK,      ADDR_THIS_NETWORK},
@@ -3163,6 +3407,7 @@
         {MULTICAST,         ADDR_MULTICAST},
         {LIMITED_BROADCAST, ADDR_LIMITED_BROADCAST}
     };
+    // clang-format on
 
     for (const auto& config : testConfigs) {
         const std::string testHostName = config.asHostName();
@@ -3197,6 +3442,7 @@
     // Setup OPPORTUNISTIC mode and wait for the validation complete.
     ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, ""));
     EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
+    EXPECT_TRUE(tls.waitForQueries(1));
     tls.clearQueries();
 
     // Start NAT64 prefix discovery and wait for it complete.
@@ -3204,8 +3450,8 @@
     EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
 
     // Verify it bypassed TLS even though there's a TLS server available.
-    EXPECT_EQ(0, tls.queries());
-    EXPECT_EQ(1U, GetNumQueries(dns, dns64_name));
+    EXPECT_EQ(0, tls.queries()) << dns.dumpQueries();
+    EXPECT_EQ(1U, GetNumQueries(dns, dns64_name)) << dns.dumpQueries();
 
     // Restart the testing network to reset the cache.
     mDnsClient.TearDown();
@@ -3216,6 +3462,7 @@
     ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams,
                                                kDefaultPrivateDnsHostName));
     EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
+    EXPECT_TRUE(tls.waitForQueries(1));
     tls.clearQueries();
 
     // Start NAT64 prefix discovery and wait for it to complete.
@@ -3223,8 +3470,8 @@
     EXPECT_TRUE(WaitForNat64Prefix(EXPECT_FOUND));
 
     // Verify it bypassed TLS despite STRICT mode.
-    EXPECT_EQ(0, tls.queries());
-    EXPECT_EQ(1U, GetNumQueries(dns, dns64_name));
+    EXPECT_EQ(0, tls.queries()) << dns.dumpQueries();
+    EXPECT_EQ(1U, GetNumQueries(dns, dns64_name)) << dns.dumpQueries();
 }
 
 namespace {
@@ -3279,8 +3526,8 @@
     return true;
 }
 
-android::net::UidRangeParcel makeUidRangeParcel(int start, int stop) {
-    android::net::UidRangeParcel res;
+aidl::android::net::UidRangeParcel makeUidRangeParcel(int start, int stop) {
+    aidl::android::net::UidRangeParcel res;
     res.start = start;
     res.stop = stop;
 
@@ -3440,30 +3687,51 @@
     EXPECT_TRUE(netdService->firewallEnableChildChain(INetd::FIREWALL_CHAIN_STANDBY, false).isOk());
 }
 
+namespace {
+
+const std::string kDotConnectTimeoutMsFlag(
+        "persist.device_config.netd_native.dot_connect_timeout_ms");
+
+class ScopedSystemProperties {
+  public:
+    explicit ScopedSystemProperties(const std::string& key, const std::string& value)
+        : mStoredKey(key) {
+        mStoredValue = android::base::GetProperty(key, "");
+        android::base::SetProperty(key, value);
+    }
+    ~ScopedSystemProperties() { android::base::SetProperty(mStoredKey, mStoredValue); }
+
+  private:
+    std::string mStoredKey;
+    std::string mStoredValue;
+};
+
+}  // namespace
+
 TEST_F(ResolverTest, ConnectTlsServerTimeout) {
-    constexpr char listen_addr[] = "127.0.0.3";
-    constexpr char listen_udp[] = "53";
-    constexpr char listen_tls[] = "853";
-    constexpr char host_name[] = "tls.example.com.";
-    const std::vector<std::string> servers = {listen_addr};
+    constexpr int expectedTimeout = 1000;
+    constexpr char hostname1[] = "query1.example.com.";
+    constexpr char hostname2[] = "query2.example.com.";
     const std::vector<DnsRecord> records = {
-            {host_name, ns_type::ns_t_a, "1.2.3.4"},
+            {hostname1, ns_type::ns_t_a, "1.2.3.4"},
+            {hostname2, ns_type::ns_t_a, "1.2.3.5"},
     };
 
     test::DNSResponder dns;
     StartDns(dns, records);
-
-    test::DnsTlsFrontend tls(listen_addr, listen_tls, listen_addr, listen_udp);
+    test::DnsTlsFrontend tls;
     ASSERT_TRUE(tls.startServer());
 
-    // Opportunistic mode.
-    ASSERT_TRUE(mDnsClient.SetResolversWithTls(servers, kDefaultSearchDomains, kDefaultParams, {}));
+    // The resolver will adjust the timeout value to 1000ms since the value is too small.
+    ScopedSystemProperties scopedSystemProperties(kDotConnectTimeoutMsFlag, "100");
 
-    // Wait for the server being marked as validated so that PrivateDnsStatus::validatedServers()
-    // won't return empty list.
+    // Set up resolver to opportunistic mode with the default configuration.
+    const ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+    ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel));
     EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address(), true));
-    dns.clearQueries();
+    EXPECT_TRUE(tls.waitForQueries(1));
     tls.clearQueries();
+    dns.clearQueries();
 
     // The server becomes unresponsive to the handshake request.
     tls.setHangOnHandshakeForTesting(true);
@@ -3472,17 +3740,470 @@
     //   1. Connect to the private DNS server.
     //   2. SSL handshake times out.
     //   3. Fallback to UDP transport, and then get the answer.
-    const auto start = std::chrono::steady_clock::now();
-    addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
-    ScopedAddrinfo result = safe_getaddrinfo("tls", nullptr, &hints);
-    const auto end = std::chrono::steady_clock::now();
+    const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+    auto [result, timeTakenMs] = safe_getaddrinfo_time_taken(hostname1, nullptr, hints);
 
-    EXPECT_TRUE(result != nullptr);
+    EXPECT_NE(nullptr, result);
     EXPECT_EQ(0, tls.queries());
-    EXPECT_EQ(1U, GetNumQueries(dns, host_name));
-    EXPECT_EQ("1.2.3.4", ToString(result));
+    EXPECT_EQ(1U, GetNumQueries(dns, hostname1));
+    EXPECT_EQ(records.at(0).addr, ToString(result));
 
-    // 3000ms is a loose upper bound. Theoretically, it takes a bit more than 1000ms.
-    EXPECT_GE(3000, std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count());
-    EXPECT_LE(1000, std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count());
+    // A loose upper bound is set by adding 2000ms buffer time. Theoretically, getaddrinfo()
+    // should just take a bit more than expetTimeout milliseconds.
+    EXPECT_GE(timeTakenMs, expectedTimeout);
+    EXPECT_LE(timeTakenMs, expectedTimeout + 2000);
+
+    // Set the server to be responsive. Verify that the resolver will attempt to reconnect
+    // to the server and then get the result within the timeout.
+    tls.setHangOnHandshakeForTesting(false);
+    std::tie(result, timeTakenMs) = safe_getaddrinfo_time_taken(hostname2, nullptr, hints);
+
+    EXPECT_NE(nullptr, result);
+    EXPECT_EQ(1, tls.queries());
+    EXPECT_EQ(1U, GetNumQueries(dns, hostname2));
+    EXPECT_EQ(records.at(1).addr, ToString(result));
+
+    EXPECT_LE(timeTakenMs, expectedTimeout);
+}
+
+TEST_F(ResolverTest, FlushNetworkCache) {
+    SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsClient.resolvService(), 4);
+    test::DNSResponder dns;
+    StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+    ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+    const hostent* result = gethostbyname("hello");
+    EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, kHelloExampleCom));
+
+    // get result from cache
+    result = gethostbyname("hello");
+    EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, kHelloExampleCom));
+
+    EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+
+    result = gethostbyname("hello");
+    EXPECT_EQ(2U, GetNumQueriesForType(dns, ns_type::ns_t_a, kHelloExampleCom));
+}
+
+TEST_F(ResolverTest, FlushNetworkCache_random) {
+    SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsClient.resolvService(), 4);
+    constexpr int num_flush = 10;
+    constexpr int num_queries = 20;
+    test::DNSResponder dns;
+    StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+    ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+    const addrinfo hints = {.ai_family = AF_INET};
+
+    std::thread t([this]() {
+        for (int i = 0; i < num_flush; ++i) {
+            unsigned delay = arc4random_uniform(10 * 1000);  // 10ms
+            usleep(delay);
+            EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+        }
+    });
+
+    for (int i = 0; i < num_queries; ++i) {
+        ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+        EXPECT_TRUE(result != nullptr);
+        EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+    }
+    t.join();
+}
+
+// flush cache while one query is wait-for-response, another is pending.
+TEST_F(ResolverTest, FlushNetworkCache_concurrent) {
+    SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsClient.resolvService(), 4);
+    const char* listen_addr1 = "127.0.0.9";
+    const char* listen_addr2 = "127.0.0.10";
+    test::DNSResponder dns1(listen_addr1);
+    test::DNSResponder dns2(listen_addr2);
+    StartDns(dns1, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+    StartDns(dns2, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+    addrinfo hints = {.ai_family = AF_INET};
+
+    // step 1: set server#1 into deferred responding mode
+    dns1.setDeferredResp(true);
+    std::thread t1([&listen_addr1, &hints, this]() {
+        ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr1}));
+        // step 3: query
+        ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+        // step 9: check result
+        EXPECT_TRUE(result != nullptr);
+        EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+    });
+
+    // step 2: wait for the query to reach the server
+    while (GetNumQueries(dns1, kHelloExampleCom) == 0) {
+        usleep(1000);  // 1ms
+    }
+
+    std::thread t2([&listen_addr2, &hints, &dns2, this]() {
+        ASSERT_TRUE(mDnsClient.SetResolversForNetwork({listen_addr2}));
+        // step 5: query (should be blocked in resolver)
+        ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+        // step 7: check result
+        EXPECT_TRUE(result != nullptr);
+        EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+        EXPECT_EQ(1U, GetNumQueriesForType(dns2, ns_type::ns_t_a, kHelloExampleCom));
+    });
+
+    // step 4: wait a bit for the 2nd query to enter pending state
+    usleep(100 * 1000);  // 100ms
+    // step 6: flush cache (will unblock pending queries)
+    EXPECT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+    t2.join();
+
+    // step 8: resume server#1
+    dns1.setDeferredResp(false);
+    t1.join();
+
+    // step 10: verify if result is correctly cached
+    dns2.clearQueries();
+    ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+    EXPECT_EQ(0U, GetNumQueries(dns2, kHelloExampleCom));
+    EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+}
+
+// TODO: Perhaps to have a boundary conditions test for TCP and UDP.
+TEST_F(ResolverTest, TcpQueryWithOversizePayload) {
+    test::DNSResponder dns;
+    StartDns(dns, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
+    ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+    int fd = dns_open_proxy();
+    ASSERT_TRUE(fd > 0);
+
+    // Sending DNS query over TCP once the packet sizes exceed 512 bytes.
+    // The raw data is combined with Question section and Additional section
+    // Question section : query "hello.example.com", type A, class IN
+    // Additional section : type OPT (41), Option PADDING, Option Length 546
+    // Padding option which allows DNS clients and servers to artificially
+    // increase the size of a DNS message by a variable number of bytes.
+    // See also RFC7830, section 3
+    const std::string query =
+            "+c0BAAABAAAAAAABBWhlbGxvB2V4YW1wbGUDY29tAAABAAEAACkgAAAAgAACJgAMAiIAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+    const std::string cmd =
+            "resnsend " + std::to_string(TEST_NETID) + " 0 " /* ResNsendFlags */ + query + '\0';
+    ssize_t rc = TEMP_FAILURE_RETRY(write(fd, cmd.c_str(), cmd.size()));
+    EXPECT_EQ(rc, static_cast<ssize_t>(cmd.size()));
+    expectAnswersValid(fd, AF_INET, kHelloExampleComAddrV4);
+    EXPECT_EQ(1U, GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom));
+    EXPECT_EQ(0U, GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom));
+}
+
+TEST_F(ResolverTest, TruncatedRspMode) {
+    constexpr char listen_addr[] = "127.0.0.4";
+    constexpr char listen_addr2[] = "127.0.0.5";
+    constexpr char listen_srv[] = "53";
+
+    test::DNSResponder dns(listen_addr, listen_srv, static_cast<ns_rcode>(-1));
+    test::DNSResponder dns2(listen_addr2, listen_srv, static_cast<ns_rcode>(-1));
+    // dns supports UDP only, dns2 support UDP and TCP
+    dns.setResponseProbability(0.0, IPPROTO_TCP);
+    StartDns(dns, kLargeCnameChainRecords);
+    StartDns(dns2, kLargeCnameChainRecords);
+
+    const struct TestConfig {
+        const std::optional<int32_t> tcMode;
+        const bool ret;
+        const unsigned numQueries;
+        std::string asParameters() const {
+            return StringPrintf("tcMode: %d, ret: %s, numQueries: %u", tcMode.value_or(-1),
+                                ret ? "true" : "false", numQueries);
+        }
+    } testConfigs[]{
+            // clang-format off
+            {std::nullopt,                                      true,  0}, /* mode unset */
+            {aidl::android::net::IDnsResolver::TC_MODE_DEFAULT, true,  0}, /* default mode */
+            {aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP, true,  1}, /* alternative mode */
+            {-666,                                              false, 1}, /* invalid input */
+            // clang-format on
+    };
+
+    for (const auto& config : testConfigs) {
+        SCOPED_TRACE(config.asParameters());
+
+        ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
+        parcel.servers = {listen_addr, listen_addr2};
+        if (config.tcMode) {
+            parcel.experimentalOptions.tcMode = config.tcMode.value();
+        }
+        ASSERT_EQ(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk(), config.ret);
+
+        const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+        ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+        ASSERT_TRUE(result != nullptr);
+        EXPECT_EQ(ToString(result), kHelloExampleComAddrV4);
+        // TC_MODE_DEFAULT: resolver retries on TCP-only on each name server.
+        // TC_MODE_UDP_TCP: resolver retries on TCP on the same server, falls back to UDP from next.
+        ASSERT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom), 1U);
+        ASSERT_EQ(GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom), 1U);
+        ASSERT_EQ(GetNumQueriesForProtocol(dns2, IPPROTO_UDP, kHelloExampleCom), config.numQueries);
+        ASSERT_EQ(GetNumQueriesForProtocol(dns2, IPPROTO_TCP, kHelloExampleCom), 1U);
+
+        dns.clearQueries();
+        dns2.clearQueries();
+        ASSERT_TRUE(mDnsClient.resolvService()->flushNetworkCache(TEST_NETID).isOk());
+    }
+}
+
+// Parameterized tests.
+// TODO: Merge the existing tests as parameterized test if possible.
+// TODO: Perhaps move parameterized tests to an independent file.
+enum class CallType { GETADDRINFO, GETHOSTBYNAME };
+class ResolverParameterizedTest : public ResolverTest,
+                                  public testing::WithParamInterface<CallType> {
+  protected:
+    void VerifyQueryHelloExampleComV4(const test::DNSResponder& dns, const CallType calltype,
+                                      const bool verifyNumQueries = true) {
+        if (calltype == CallType::GETADDRINFO) {
+            const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
+            ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
+            ASSERT_TRUE(result != nullptr);
+            EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+        } else if (calltype == CallType::GETHOSTBYNAME) {
+            const hostent* result = gethostbyname("hello");
+            ASSERT_TRUE(result != nullptr);
+            ASSERT_EQ(4, result->h_length);
+            ASSERT_FALSE(result->h_addr_list[0] == nullptr);
+            EXPECT_EQ(kHelloExampleComAddrV4, ToString(result));
+            EXPECT_TRUE(result->h_addr_list[1] == nullptr);
+        } else {
+            FAIL() << "Unsupported call type: " << static_cast<uint32_t>(calltype);
+        }
+        if (verifyNumQueries) EXPECT_EQ(1U, GetNumQueries(dns, kHelloExampleCom));
+    }
+};
+
+INSTANTIATE_TEST_SUITE_P(QueryCallTest, ResolverParameterizedTest,
+                         testing::Values(CallType::GETADDRINFO, CallType::GETHOSTBYNAME),
+                         [](const testing::TestParamInfo<CallType>& info) {
+                             switch (info.param) {
+                                 case CallType::GETADDRINFO:
+                                     return "GetAddrInfo";
+                                 case CallType::GETHOSTBYNAME:
+                                     return "GetHostByName";
+                                 default:
+                                     return "InvalidParameter";  // Should not happen.
+                             }
+                         });
+
+TEST_P(ResolverParameterizedTest, AuthoritySectionAndAdditionalSection) {
+    // DNS response may have more information in authority section and additional section.
+    // Currently, getanswer() of packages/modules/DnsResolver/getaddrinfo.cpp doesn't parse the
+    // content of authority section and additional section. Test these sections if they crash
+    // the resolver, just in case. See also RFC 1035 section 4.1.
+    const auto& calltype = GetParam();
+    test::DNSHeader header(kDefaultDnsHeader);
+
+    // Create a DNS response which has a authoritative nameserver record in authority
+    // section and its relevant address record in additional section.
+    //
+    // Question
+    //   hello.example.com.     IN      A
+    // Answer
+    //   hello.example.com.     IN      A   1.2.3.4
+    // Authority:
+    //   hello.example.com.     IN      NS  ns1.example.com.
+    // Additional:
+    //   ns1.example.com.       IN      A   5.6.7.8
+    //
+    // A response may have only question, answer, and authority section. Current testing response
+    // should be able to cover this condition.
+
+    // Question section.
+    test::DNSQuestion question{
+            .qname = {.name = kHelloExampleCom},
+            .qtype = ns_type::ns_t_a,
+            .qclass = ns_c_in,
+    };
+    header.questions.push_back(std::move(question));
+
+    // Answer section.
+    test::DNSRecord recordAnswer{
+            .name = {.name = kHelloExampleCom},
+            .rtype = ns_type::ns_t_a,
+            .rclass = ns_c_in,
+            .ttl = 0,  // no cache
+    };
+    EXPECT_TRUE(test::DNSResponder::fillRdata(kHelloExampleComAddrV4, recordAnswer));
+    header.answers.push_back(std::move(recordAnswer));
+
+    // Authority section.
+    test::DNSRecord recordAuthority{
+            .name = {.name = kHelloExampleCom},
+            .rtype = ns_type::ns_t_ns,
+            .rclass = ns_c_in,
+            .ttl = 0,  // no cache
+    };
+    EXPECT_TRUE(test::DNSResponder::fillRdata("ns1.example.com.", recordAuthority));
+    header.authorities.push_back(std::move(recordAuthority));
+
+    // Additional section.
+    test::DNSRecord recordAdditional{
+            .name = {.name = "ns1.example.com."},
+            .rtype = ns_type::ns_t_a,
+            .rclass = ns_c_in,
+            .ttl = 0,  // no cache
+    };
+    EXPECT_TRUE(test::DNSResponder::fillRdata("5.6.7.8", recordAdditional));
+    header.additionals.push_back(std::move(recordAdditional));
+
+    // Start DNS server.
+    test::DNSResponder dns(test::DNSResponder::MappingType::DNS_HEADER);
+    dns.addMappingDnsHeader(kHelloExampleCom, ns_type::ns_t_a, header);
+    ASSERT_TRUE(dns.startServer());
+    ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+    dns.clearQueries();
+
+    // Expect that get the address and the resolver doesn't crash.
+    VerifyQueryHelloExampleComV4(dns, calltype);
+}
+
+TEST_P(ResolverParameterizedTest, MessageCompression) {
+    const auto& calltype = GetParam();
+
+    // The response with compressed domain name by a pointer. See RFC 1035 section 4.1.4.
+    //
+    // Ignoring the other fields of the message, the domain name of question section and answer
+    // section are presented as:
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 12 |           5           |           h           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 14 |           e           |           l           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 16 |           l           |           o           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 18 |           7           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 20 |           x           |           a           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 22 |           m           |           p           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 24 |           l           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 26 |           3           |           c           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 28 |           o           |           m           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 30 |           0           |          ...          |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 35 | 1  1|                12                       |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    const std::vector<uint8_t> kResponseAPointer = {
+            /* Header */
+            0x00, 0x00, /* Transaction ID: 0x0000 */
+            0x81, 0x80, /* Flags: qr rd ra */
+            0x00, 0x01, /* Questions: 1 */
+            0x00, 0x01, /* Answer RRs: 1 */
+            0x00, 0x00, /* Authority RRs: 0 */
+            0x00, 0x00, /* Additional RRs: 0 */
+            /* Queries */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+            0x00, 0x01,                   /* Type: A */
+            0x00, 0x01,                   /* Class: IN */
+            /* Answers */
+            0xc0, 0x0c,             /* Name: hello.example.com (a pointer) */
+            0x00, 0x01,             /* Type: A */
+            0x00, 0x01,             /* Class: IN */
+            0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+            0x00, 0x04,             /* Data length: 4 */
+            0x01, 0x02, 0x03, 0x04  /* Address: 1.2.3.4 */
+    };
+
+    // The response with compressed domain name by a sequence of labels ending with a pointer. See
+    // RFC 1035 section 4.1.4.
+    //
+    // Ignoring the other fields of the message, the domain name of question section and answer
+    // section are presented as:
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 12 |           5           |           h           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 14 |           e           |           l           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 16 |           l           |           o           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 18 |           7           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 20 |           x           |           a           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 22 |           m           |           p           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 24 |           l           |           e           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 26 |           3           |           c           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 28 |           o           |           m           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 30 |           0           |          ...          |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 35 |           5           |           h           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 37 |           e           |           l           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 39 |           l           |           o           |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // 41 | 1  1|                18                       |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    const std::vector<uint8_t> kResponseLabelEndingWithAPointer = {
+            /* Header */
+            0x00, 0x00, /* Transaction ID: 0x0000 */
+            0x81, 0x80, /* Flags: qr rd ra */
+            0x00, 0x01, /* Questions: 1 */
+            0x00, 0x01, /* Answer RRs: 1 */
+            0x00, 0x00, /* Authority RRs: 0 */
+            0x00, 0x00, /* Additional RRs: 0 */
+            /* Queries */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+            0x00, 0x01,                   /* Type: A */
+            0x00, 0x01,                   /* Class: IN */
+            /* Answers */
+            0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xc0,
+            0x12,                   /* Name: hello.example.com (a label ending with a pointer) */
+            0x00, 0x01,             /* Type: A */
+            0x00, 0x01,             /* Class: IN */
+            0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+            0x00, 0x04,             /* Data length: 4 */
+            0x01, 0x02, 0x03, 0x04  /* Address: 1.2.3.4 */
+    };
+
+    for (const auto& response : {kResponseAPointer, kResponseLabelEndingWithAPointer}) {
+        SCOPED_TRACE(StringPrintf("Hex dump: %s", toHex(makeSlice(response)).c_str()));
+
+        test::DNSResponder dns(test::DNSResponder::MappingType::BINARY_PACKET);
+        dns.addMappingBinaryPacket(kHelloExampleComQueryV4, response);
+        StartDns(dns, {});
+        ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+        // Expect no cache because the TTL of testing responses are 0.
+        VerifyQueryHelloExampleComV4(dns, calltype);
+    }
+}
+
+TEST_P(ResolverParameterizedTest, TruncatedResponse) {
+    const auto& calltype = GetParam();
+
+    test::DNSResponder dns;
+    StartDns(dns, kLargeCnameChainRecords);
+    ASSERT_TRUE(mDnsClient.SetResolversForNetwork());
+
+    // Expect UDP response is truncated. The resolver retries over TCP. See RFC 1035 section 4.2.1.
+    VerifyQueryHelloExampleComV4(dns, calltype, false);
+    EXPECT_EQ(1U, GetNumQueriesForProtocol(dns, IPPROTO_UDP, kHelloExampleCom));
+    EXPECT_EQ(1U, GetNumQueriesForProtocol(dns, IPPROTO_TCP, kHelloExampleCom));
 }
diff --git a/tests/resolv_stats_test_utils.cpp b/tests/resolv_stats_test_utils.cpp
new file mode 100644
index 0000000..d31e6c0
--- /dev/null
+++ b/tests/resolv_stats_test_utils.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "resolv_stats_test_utils.h"
+
+#include <iostream>
+#include <regex>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <stats.pb.h>
+
+namespace android::net {
+
+using android::net::NetworkDnsEventReported;
+
+// The fromNetworkDnsEventReportedStr is a function to generate a protocol buffer message
+// NetworkDnsEventReported from a string. How to define the sting for NetworkDnsEventReported,
+// please refer to test case AlphabeticalHostname.
+// There are 3 proto messages(1. NetworkDnsEventReported 2. dns_query_events 3. dns_query_event)
+// The names of these 3 messages will not be verified, please do not change.
+// Each field will be parsed into corresponding message by those char pairs "{" & "}".
+// Example: (Don't need to fill Level 1 fields in NetworkDnsEventReported. Currently,
+// no verification is performed.)
+// NetworkDnsEventReported { <= construct the NetworkDnsEventReported message (Level 1)
+//  dns_query_events:(Level 2)
+//  { <= construct the dns_query_events message
+//   dns_query_event:(Level 3)
+//    {  <= construct the 1st dns_query_event message
+//     [field]: value,
+//     [field]: value,
+//    } <= finish the 1st dns_query_event message
+//   dns_query_event:(Level 3)
+//    {  <= construct 2nd dns_query_event message
+//     [field]: value,
+//     [field]: value,
+//    } <= finish the 1nd dns_query_event message
+//  } <= finish the dns_query_events message
+// } <= finish the NetworkDnsEventReported message
+
+NetworkDnsEventReported fromNetworkDnsEventReportedStr(const std::string& str) {
+    using android::base::ParseInt;
+    using android::base::Split;
+    // Remove unnecessary space
+    std::regex re(": ");
+    std::string s = std::regex_replace(str, re, ":");
+    // Using space to separate each parse line
+    static const std::regex words_regex("[^\\s]+");
+    auto words_begin = std::sregex_iterator(s.begin(), s.end(), words_regex);
+    auto words_end = std::sregex_iterator();
+    // Using strproto to identify the position of NetworkDnsEventReported proto
+    int strproto = 0;
+    NetworkDnsEventReported event;
+    DnsQueryEvent* dnsQueryEvent = nullptr;
+    for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
+        std::string match_str = (*i).str();
+        // Using "{" and "}" to identify the Start/End of each proto
+        if (match_str == "{") {
+            // strproto 1.NetworkDnsEventReported 2.dns_query_events 3.dns_query_event
+            if (++strproto == 3) {
+                dnsQueryEvent = event.mutable_dns_query_events()->add_dns_query_event();
+            }
+            continue;
+        }
+        if (match_str == "}" | match_str == "},") {
+            strproto--;
+            continue;
+        }
+        // Parsing each field of the proto and fill it into NetworkDnsEventReported event
+        static const std::regex pieces_regex("([a-zA-Z0-9_]+)\\:([0-9]+),");
+        std::smatch protoField;
+        std::regex_match(match_str, protoField, pieces_regex);
+        int value = 0;
+        LOG(DEBUG) << "Str:" << match_str << " Name:" << protoField[1]
+                   << " Value:" << protoField[2];
+        // Parsing each field of the proto NetworkDnsEventReported
+        if (strproto == 1) {
+            if (protoField[1] == "event_type" && ParseInt(protoField[2], &value)) {
+                event.set_event_type(static_cast<EventType>(value));
+            } else if (protoField[1] == "return_code" && ParseInt(protoField[2], &value)) {
+                event.set_return_code(static_cast<ReturnCode>(value));
+            } else if (protoField[1] == "latency_micros" && ParseInt(protoField[2], &value)) {
+                event.set_latency_micros(value);
+            } else if (protoField[1] == "hints_ai_flags" && ParseInt(protoField[2], &value)) {
+                event.set_hints_ai_flags(value);
+            } else if (protoField[1] == "res_nsend_flags" && ParseInt(protoField[2], &value)) {
+                event.set_res_nsend_flags(value);
+            } else if (protoField[1] == "network_type" && ParseInt(protoField[2], &value)) {
+                event.set_network_type(static_cast<NetworkType>(value));
+            } else if (protoField[1] == "private_dns_modes" && ParseInt(protoField[2], &value)) {
+                event.set_private_dns_modes(static_cast<PrivateDnsModes>(value));
+            } else if (protoField[1] == "sampling_rate_denom" && ParseInt(protoField[2], &value)) {
+                event.set_sampling_rate_denom(value);
+            }
+        }
+        // Parsing each field of the proto DnsQueryEvent
+        if (strproto == 3) {
+            if (dnsQueryEvent == nullptr) continue;
+            if (protoField[1] == "rcode" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_rcode(static_cast<NsRcode>(value));
+            } else if (protoField[1] == "type" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_type(static_cast<NsType>(value));
+            } else if (protoField[1] == "cache_hit" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_cache_hit(static_cast<CacheStatus>(value));
+            } else if (protoField[1] == "ip_version" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_ip_version(static_cast<IpVersion>(value));
+            } else if (protoField[1] == "protocol" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_protocol(static_cast<Protocol>(value));
+            } else if (protoField[1] == "retry_times" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_retry_times(value);
+            } else if (protoField[1] == "dns_server_index" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_dns_server_index(value);
+            } else if (protoField[1] == "connected" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_connected(static_cast<bool>(value));
+            } else if (protoField[1] == "latency_micros" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_latency_micros(value);
+            }
+        }
+    }
+    return event;
+}
+
+void PrintTo(const DnsQueryEvents& event, std::ostream* os) {
+    *os << "query events: {\n";
+    *os << "  dns_query_event_size: " << event.dns_query_event_size() << "\n";
+    *os << "}";
+}
+
+void PrintTo(const DnsQueryEvent& event, std::ostream* os) {
+    *os << "dns query event: {\n";
+    *os << "  rcode: " << event.rcode() << "\n";
+    *os << "  ns_type: " << event.type() << "\n";
+    *os << "  cache_hit: " << event.cache_hit() << "\n";
+    *os << "  ip_version: " << event.ip_version() << "\n";
+    *os << "  protocol: " << event.protocol() << "\n";
+    *os << "  retry_times: " << event.retry_times() << "\n";
+    *os << "  dns_server_index: " << event.dns_server_index() << "\n";
+    *os << "  connected: " << event.connected() << "\n";
+    *os << "  latency_micros: " << event.latency_micros() << "\n";
+    *os << "}";
+}
+
+void PrintTo(const NetworkDnsEventReported& event, std::ostream* os) {
+    *os << "network dns event: {\n";
+    *os << "  event_type: " << event.event_type() << "\n";
+    *os << "  return_code: " << event.return_code() << "\n";
+    *os << "  latency_micros: " << event.latency_micros() << "\n";
+    *os << "  hints_ai_flags: " << event.hints_ai_flags() << "\n";
+    *os << "  res_nsend_flags: " << event.res_nsend_flags() << "\n";
+    *os << "  network_type: " << event.network_type() << "\n";
+    *os << "  private_dns_modes: " << event.private_dns_modes() << "\n";
+    *os << "  dns_query_event_size: " << event.dns_query_events().dns_query_event_size() << "\n";
+    *os << "}";
+}
+
+}  // namespace android::net
diff --git a/tests/resolv_stats_test_utils.h b/tests/resolv_stats_test_utils.h
new file mode 100644
index 0000000..cdfbe26
--- /dev/null
+++ b/tests/resolv_stats_test_utils.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <stats.pb.h>
+
+namespace android::net {
+
+android::net::NetworkDnsEventReported fromNetworkDnsEventReportedStr(const std::string& str);
+
+// Used for gmock printing our desired debug messages. (Another approach is defining operator<<())
+void PrintTo(const DnsQueryEvents& event, std::ostream* os);
+void PrintTo(const DnsQueryEvent& event, std::ostream* os);
+void PrintTo(const android::net::NetworkDnsEventReported& event, std::ostream* os);
+
+MATCHER_P(DnsQueryEventEq, other, "") {
+    return ::testing::ExplainMatchResult(
+            ::testing::AllOf(
+                    ::testing::Property("rcode", &DnsQueryEvent::rcode,
+                                        ::testing::Eq(other.rcode())),
+                    ::testing::Property("ns_type", &DnsQueryEvent::type,
+                                        ::testing::Eq(other.type())),
+                    ::testing::Property("cache_hit", &DnsQueryEvent::cache_hit,
+                                        ::testing::Eq(other.cache_hit())),
+                    ::testing::Property("ip_version", &DnsQueryEvent::ip_version,
+                                        ::testing::Eq(other.ip_version())),
+                    ::testing::Property("protocol", &DnsQueryEvent::protocol,
+                                        ::testing::Eq(other.protocol())),
+                    ::testing::Property("retry_times", &DnsQueryEvent::retry_times,
+                                        ::testing::Eq(other.retry_times())),
+                    ::testing::Property("dns_server_index", &DnsQueryEvent::dns_server_index,
+                                        ::testing::Eq(other.dns_server_index())),
+                    // Removing the latency check, because we can't predict the time.
+                    /*  ::testing::Property("latency_micros", &DnsQueryEvent::latency_micros,
+                             ::testing::Eq(other.latency_micros())),*/
+                    ::testing::Property("connected", &DnsQueryEvent::connected,
+                                        ::testing::Eq(other.connected()))),
+            arg, result_listener);
+}
+
+MATCHER_P(DnsQueryEventsEq, other, "") {
+    const int eventSize = arg.dns_query_event_size();
+    if (eventSize != other.dns_query_event_size()) {
+        *result_listener << "Expected dns query event size: " << other.dns_query_event_size()
+                         << " \n";
+        for (int i = 0; i < other.dns_query_event_size(); ++i)
+            PrintTo(other.dns_query_event(i), result_listener->stream());
+        *result_listener << "Actual dns query event size: " << eventSize << "\n";
+        for (int i = 0; i < eventSize; ++i)
+            PrintTo(arg.dns_query_event(i), result_listener->stream());
+        return false;
+    }
+
+    for (int i = 0; i < eventSize; ++i) {
+        bool result =
+                ::testing::Value(arg.dns_query_event(i), DnsQueryEventEq(other.dns_query_event(i)));
+        if (!result) {
+            *result_listener << "Expected event num: " << i << " \n";
+            PrintTo(arg.dns_query_event(i), result_listener->stream());
+            *result_listener << "Actual event num: " << i << " ";
+            PrintTo(other.dns_query_event(i), result_listener->stream());
+            return false;
+        }
+    }
+    return true;
+}
+
+MATCHER_P(NetworkDnsEventEq, other, "") {
+    return ::testing::ExplainMatchResult(
+            ::testing::AllOf(
+                    // Removing following fields check, because we can't verify those fields in unit
+                    // test.
+                    /*
+                    ::testing::Property("event_type",
+                                        &android::net::NetworkDnsEventReported::event_type,
+                                        ::testing::Eq(other.event_type())),
+                    ::testing::Property("return_code",
+                                        &android::net::NetworkDnsEventReported::return_code,
+                                        ::testing::Eq(other.return_code())),
+                    ::testing::Property("latency_micros",
+                       &android::net::NetworkDnsEventReported::latency_micros,
+                              ::testing::Eq(other.latency_micros())),
+                    ::testing::Property("hints_ai_flags",
+                                        &android::net::NetworkDnsEventReported::hints_ai_flags,
+                                        ::testing::Eq(other.hints_ai_flags())),
+                    ::testing::Property("res_nsend_flags",
+                                        &android::net::NetworkDnsEventReported::res_nsend_flags,
+                                        ::testing::Eq(other.res_nsend_flags())),
+                    ::testing::Property("network_type",
+                                        &android::net::NetworkDnsEventReported::network_type,
+                                        ::testing::Eq(other.network_type())),
+                    ::testing::Property("private_dns_modes",
+                                        &android::net::NetworkDnsEventReported::private_dns_modes,
+                                        ::testing::Eq(other.private_dns_modes())),
+                    */
+                    ::testing::Property("dns_query_events",
+                                        &android::net::NetworkDnsEventReported::dns_query_events,
+                                        DnsQueryEventsEq(other.dns_query_events()))),
+            arg, result_listener);
+}
+
+}  // namespace android::net
diff --git a/tests/resolv_stats_test_utils_test.cpp b/tests/resolv_stats_test_utils_test.cpp
new file mode 100644
index 0000000..f6fecd8
--- /dev/null
+++ b/tests/resolv_stats_test_utils_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <resolv_stats_test_utils.h>
+#include <stats.pb.h>
+
+namespace android::net {
+
+TEST(ResolvStatsUtils, NetworkDnsEventEq) {
+    NetworkDnsEventReported event1;
+    // Following fields will not be verified during the test in proto NetworkDnsEventReported.
+    // So don't need to config those values: event_type, return_code, latency_micros,
+    // hints_ai_flags, res_nsend_flags, network_type, private_dns_modes.
+    constexpr char event2[] = R"Event(
+             NetworkDnsEventReported {
+             dns_query_events:
+             {
+               dns_query_event:[
+                {
+                 rcode: 3,
+                 type: 28,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 3,
+                 retry_times: 28,
+                 dns_server_index: 0,
+                 connected: 1,
+                 latency_micros: 5,
+                },
+                {
+                 rcode: 0,
+                 type: 1,
+                 cache_hit: 1,
+                 ip_version: 1,
+                 protocol: 1,
+                 retry_times: 56,
+                 dns_server_index: 1,
+                 connected: 0,
+                 latency_micros: 0,
+                }
+               ]
+             }
+        })Event";
+
+    // TODO: Add integration test to verify Level 1 fields of NetworkDnsEventReported.
+    // Level 1 fields, including event_type, return_code, hints_ai_flags, network_type, etc.
+    DnsQueryEvent* dnsQueryEvent1 = event1.mutable_dns_query_events()->add_dns_query_event();
+    dnsQueryEvent1->set_rcode(NS_R_NXDOMAIN);
+    dnsQueryEvent1->set_type(NS_T_AAAA);
+    dnsQueryEvent1->set_cache_hit(CS_NOTFOUND);
+    dnsQueryEvent1->set_ip_version(IV_IPV4);
+    dnsQueryEvent1->set_protocol(PROTO_DOT);
+    dnsQueryEvent1->set_retry_times(28);
+    dnsQueryEvent1->set_dns_server_index(0);
+    dnsQueryEvent1->set_connected(1);
+    dnsQueryEvent1->set_latency_micros(5);
+    DnsQueryEvent* dnsQueryEvent2 = event1.mutable_dns_query_events()->add_dns_query_event();
+    dnsQueryEvent2->set_rcode(NS_R_NO_ERROR);
+    dnsQueryEvent2->set_type(NS_T_A);
+    dnsQueryEvent2->set_cache_hit(CS_NOTFOUND);
+    dnsQueryEvent2->set_ip_version(IV_IPV4);
+    dnsQueryEvent2->set_protocol(PROTO_UDP);
+    dnsQueryEvent2->set_retry_times(56);
+    dnsQueryEvent2->set_dns_server_index(1);
+    dnsQueryEvent2->set_connected(0);
+    dnsQueryEvent2->set_latency_micros(5);
+    EXPECT_THAT(event1, NetworkDnsEventEq(fromNetworkDnsEventReportedStr(event2)));
+}
+
+}  // namespace android::net
diff --git a/tests/resolv_stress_test.cpp b/tests/resolv_stress_test.cpp
index f3b6126..9058f17 100644
--- a/tests/resolv_stress_test.cpp
+++ b/tests/resolv_stress_test.cpp
@@ -26,8 +26,8 @@
 #include <gtest/gtest.h>
 
 #include "ResolverStats.h"
-#include "dns_responder/dns_responder_client.h"
-#include "netd_resolv/params.h"  // MAX_NS
+#include "dns_responder/dns_responder_client_ndk.h"
+#include "params.h"  // MAX_NS
 #include "resolv_test_utils.h"
 
 using android::base::StringPrintf;
@@ -87,9 +87,9 @@
         res_params res_params;
         std::vector<ResolverStats> res_stats;
         int wait_for_pending_req_timeout_count;
-        ASSERT_TRUE(GetResolverInfo(mDnsClient.resolvService(), TEST_NETID, &res_servers,
-                                    &res_domains, &res_tls_servers, &res_params, &res_stats,
-                                    &wait_for_pending_req_timeout_count));
+        ASSERT_TRUE(DnsResponderClient::GetResolverInfo(
+                mDnsClient.resolvService(), TEST_NETID, &res_servers, &res_domains,
+                &res_tls_servers, &res_params, &res_stats, &wait_for_pending_req_timeout_count));
         EXPECT_EQ(0, wait_for_pending_req_timeout_count);
     }
 
diff --git a/tests/resolv_test_utils.cpp b/tests/resolv_test_utils.cpp
index 4b2a83f..31245f3 100644
--- a/tests/resolv_test_utils.cpp
+++ b/tests/resolv_test_utils.cpp
@@ -21,7 +21,6 @@
 
 #include <netdutils/InternetAddresses.h>
 
-using android::net::ResolverStats;
 using android::netdutils::ScopedAddrinfo;
 
 std::string ToString(const hostent* he) {
@@ -49,6 +48,36 @@
     return ToString(ai.get());
 }
 
+std::string ToString(const sockaddr_storage* addr) {
+    if (!addr) return "<null>";
+    char host[NI_MAXHOST];
+    int rv = getnameinfo((const sockaddr*)addr, sizeof(sockaddr_storage), host, sizeof(host),
+                         nullptr, 0, NI_NUMERICHOST);
+    if (rv != 0) return gai_strerror(rv);
+    return host;
+}
+
+std::vector<std::string> ToStrings(const hostent* he) {
+    std::vector<std::string> hosts;
+    if (he == nullptr) {
+        hosts.push_back("<null>");
+        return hosts;
+    }
+    uint32_t i = 0;
+    while (he->h_addr_list[i] != nullptr) {
+        char host[INET6_ADDRSTRLEN];
+        if (!inet_ntop(he->h_addrtype, he->h_addr_list[i], host, sizeof(host))) {
+            hosts.push_back("<invalid>");
+            return hosts;
+        } else {
+            hosts.push_back(host);
+        }
+        i++;
+    }
+    if (hosts.empty()) hosts.push_back("<invalid>");
+    return hosts;
+}
+
 std::vector<std::string> ToStrings(const addrinfo* ai) {
     std::vector<std::string> hosts;
     if (!ai) {
@@ -76,10 +105,22 @@
 }
 
 size_t GetNumQueries(const test::DNSResponder& dns, const char* name) {
-    auto queries = dns.queries();
+    std::vector<test::DNSResponder::QueryInfo> queries = dns.queries();
     size_t found = 0;
     for (const auto& p : queries) {
-        if (p.first == name) {
+        if (p.name == name) {
+            ++found;
+        }
+    }
+    return found;
+}
+
+size_t GetNumQueriesForProtocol(const test::DNSResponder& dns, const int protocol,
+                                const char* name) {
+    std::vector<test::DNSResponder::QueryInfo> queries = dns.queries();
+    size_t found = 0;
+    for (const auto& p : queries) {
+        if (p.protocol == protocol && p.name == name) {
             ++found;
         }
     }
@@ -87,42 +128,12 @@
 }
 
 size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type, const char* name) {
-    auto queries = dns.queries();
+    std::vector<test::DNSResponder::QueryInfo> queries = dns.queries();
     size_t found = 0;
     for (const auto& p : queries) {
-        if (p.second == type && p.first == name) {
+        if (p.type == type && p.name == name) {
             ++found;
         }
     }
     return found;
 }
-
-bool GetResolverInfo(android::net::IDnsResolver* dnsResolverService, unsigned netId,
-                     std::vector<std::string>* servers, std::vector<std::string>* domains,
-                     std::vector<std::string>* tlsServers, res_params* params,
-                     std::vector<ResolverStats>* stats, int* wait_for_pending_req_timeout_count) {
-    using android::net::IDnsResolver;
-    std::vector<int32_t> params32;
-    std::vector<int32_t> stats32;
-    std::vector<int32_t> wait_for_pending_req_timeout_count32{0};
-    auto rv = dnsResolverService->getResolverInfo(netId, servers, domains, tlsServers, &params32,
-                                                  &stats32, &wait_for_pending_req_timeout_count32);
-
-    if (!rv.isOk() || params32.size() != static_cast<size_t>(IDnsResolver::RESOLVER_PARAMS_COUNT)) {
-        return false;
-    }
-    *params = res_params{
-            .sample_validity =
-                    static_cast<uint16_t>(params32[IDnsResolver::RESOLVER_PARAMS_SAMPLE_VALIDITY]),
-            .success_threshold =
-                    static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_SUCCESS_THRESHOLD]),
-            .min_samples =
-                    static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MIN_SAMPLES]),
-            .max_samples =
-                    static_cast<uint8_t>(params32[IDnsResolver::RESOLVER_PARAMS_MAX_SAMPLES]),
-            .base_timeout_msec = params32[IDnsResolver::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC],
-            .retry_count = params32[IDnsResolver::RESOLVER_PARAMS_RETRY_COUNT],
-    };
-    *wait_for_pending_req_timeout_count = wait_for_pending_req_timeout_count32[0];
-    return ResolverStats::decodeAll(stats32, stats);
-}
\ No newline at end of file
diff --git a/tests/resolv_test_utils.h b/tests/resolv_test_utils.h
index 987d289..5495185 100644
--- a/tests/resolv_test_utils.h
+++ b/tests/resolv_test_utils.h
@@ -25,24 +25,65 @@
 
 #include <netdutils/InternetAddresses.h>
 
-#include "ResolverStats.h"
-
-#include "android/net/IDnsResolver.h"
 #include "dns_responder/dns_responder.h"
-#include "netd_resolv/params.h"
+
+struct DnsRecord {
+    std::string host_name;  // host name
+    ns_type type;           // record type
+    std::string addr;       // ipv4/v6 address
+};
 
 // TODO: make this dynamic and stop depending on implementation details.
 constexpr int TEST_NETID = 30;
 
-// Specifying 0 in ai_socktype or ai_protocol of struct addrinfo indicates that any type or
-// protocol can be returned by getaddrinfo().
-constexpr unsigned int ANY = 0;
-
 static constexpr char kLocalHost[] = "localhost";
 static constexpr char kLocalHostAddr[] = "127.0.0.1";
 static constexpr char kIp6LocalHost[] = "ip6-localhost";
 static constexpr char kIp6LocalHostAddr[] = "::1";
 static constexpr char kHelloExampleCom[] = "hello.example.com.";
+static constexpr char kHelloExampleComAddrV4[] = "1.2.3.4";
+static constexpr char kHelloExampleComAddrV6[] = "::1.2.3.4";
+static constexpr char kExampleComDomain[] = ".example.com";
+
+constexpr size_t kMaxmiumLabelSize = 63;  // see RFC 1035 section 2.3.4.
+
+static const std::vector<uint8_t> kHelloExampleComQueryV4 = {
+        /* Header */
+        0x00, 0x00, /* Transaction ID: 0x0000 */
+        0x01, 0x00, /* Flags: rd */
+        0x00, 0x01, /* Questions: 1 */
+        0x00, 0x00, /* Answer RRs: 0 */
+        0x00, 0x00, /* Authority RRs: 0 */
+        0x00, 0x00, /* Additional RRs: 0 */
+        /* Queries */
+        0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
+        0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+        0x00, 0x01,             /* Type: A */
+        0x00, 0x01              /* Class: IN */
+};
+
+static const std::vector<uint8_t> kHelloExampleComResponseV4 = {
+        /* Header */
+        0x00, 0x00, /* Transaction ID: 0x0000 */
+        0x81, 0x80, /* Flags: qr rd ra */
+        0x00, 0x01, /* Questions: 1 */
+        0x00, 0x01, /* Answer RRs: 1 */
+        0x00, 0x00, /* Authority RRs: 0 */
+        0x00, 0x00, /* Additional RRs: 0 */
+        /* Queries */
+        0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
+        0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+        0x00, 0x01,             /* Type: A */
+        0x00, 0x01,             /* Class: IN */
+        /* Answers */
+        0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
+        0x63, 0x6f, 0x6d, 0x00, /* Name: hello.example.com */
+        0x00, 0x01,             /* Type: A */
+        0x00, 0x01,             /* Class: IN */
+        0x00, 0x00, 0x00, 0x00, /* Time to live: 0 */
+        0x00, 0x04,             /* Data length: 4 */
+        0x01, 0x02, 0x03, 0x04  /* Address: 1.2.3.4 */
+};
 
 // Illegal hostnames
 static constexpr char kBadCharAfterPeriodHost[] = "hello.example.^com.";
@@ -64,16 +105,40 @@
         .ad = false,            // non-authenticated data is unacceptable
 };
 
+// The CNAME chain records for building a response message which exceeds 512 bytes.
+//
+// Ignoring the other fields of the message, the response message has 8 CNAMEs in 5 answer RRs
+// and each CNAME has 77 bytes as the follows. The response message at least has 616 bytes in
+// answer section and has already exceeded 512 bytes totally.
+//
+// The CNAME is presented as:
+//   0   1            64  65                          72  73          76  77
+//   +---+--........--+---+---+---+---+---+---+---+---+---+---+---+---+---+
+//   | 63| {x, .., x} | 7 | e | x | a | m | p | l | e | 3 | c | o | m | 0 |
+//   +---+--........--+---+---+---+---+---+---+---+---+---+---+---+---+---+
+//          ^-- x = {a, b, c, d}
+//
+const std::string kCnameA = std::string(kMaxmiumLabelSize, 'a') + kExampleComDomain + ".";
+const std::string kCnameB = std::string(kMaxmiumLabelSize, 'b') + kExampleComDomain + ".";
+const std::string kCnameC = std::string(kMaxmiumLabelSize, 'c') + kExampleComDomain + ".";
+const std::string kCnameD = std::string(kMaxmiumLabelSize, 'd') + kExampleComDomain + ".";
+const std::vector<DnsRecord> kLargeCnameChainRecords = {
+        {kHelloExampleCom, ns_type::ns_t_cname, kCnameA},
+        {kCnameA, ns_type::ns_t_cname, kCnameB},
+        {kCnameB, ns_type::ns_t_cname, kCnameC},
+        {kCnameC, ns_type::ns_t_cname, kCnameD},
+        {kCnameD, ns_type::ns_t_a, kHelloExampleComAddrV4},
+};
+
+// TODO: Integrate GetNumQueries relevent functions
 size_t GetNumQueries(const test::DNSResponder& dns, const char* name);
+size_t GetNumQueriesForProtocol(const test::DNSResponder& dns, const int protocol,
+                                const char* name);
 size_t GetNumQueriesForType(const test::DNSResponder& dns, ns_type type, const char* name);
 std::string ToString(const hostent* he);
 std::string ToString(const addrinfo* ai);
 std::string ToString(const android::netdutils::ScopedAddrinfo& ai);
+std::string ToString(const sockaddr_storage* addr);
+std::vector<std::string> ToStrings(const hostent* he);
 std::vector<std::string> ToStrings(const addrinfo* ai);
 std::vector<std::string> ToStrings(const android::netdutils::ScopedAddrinfo& ai);
-
-bool GetResolverInfo(android::net::IDnsResolver* dnsResolverService, unsigned netId,
-                     std::vector<std::string>* servers, std::vector<std::string>* domains,
-                     std::vector<std::string>* tlsServers, res_params* params,
-                     std::vector<android::net::ResolverStats>* stats,
-                     int* wait_for_pending_req_timeout_count);
diff --git a/tests/testdata/README.md b/tests/testdata/README.md
new file mode 100644
index 0000000..4ebd2b0
--- /dev/null
+++ b/tests/testdata/README.md
@@ -0,0 +1,157 @@
+
+# Resolv Gold Test
+The "Resolv Gold Test" targets to run automatically in presubmit, as a change
+detector to ensure that the resolver doesn't send the query or parse the
+response unexpectedly.
+
+## Build testing pbtext
+The testing pbtext is built manually so far. Fill expected API parameters to
+'config' and expected answers to 'result' in pbtext. Then, record the
+corresponding DNS query and response packets. Fill the packets with the \x
+formatting into 'query' and 'response' in pbtext. Perhaps have a mechanism
+to generate the pbtxt automatically in the future.
+
+### Using 'ping' utility to be an example for building pbtext
+Here demonstrates how the pbtext is built.
+
+1. Enable resolver debug log level to VERBOSE (0)
+```
+$ adb shell service call dnsresolver 10 i32 0
+```
+2. Ping a website
+```
+$ adb shell ping www.google.com
+```
+3. Get bugreport
+```
+$ adb bugreport
+```
+4. Search the log pattern as the follows in bugreport
+```
+# API arguments
+resolv  : logArguments: argv[0]=gethostbyname
+resolv  : logArguments: argv[1]=0
+resolv  : logArguments: argv[2]=www.google.com
+resolv  : logArguments: argv[3]=2
+
+# Query packet
+resolv  : ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29827
+resolv  : ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
+resolv  : ;; QUERY SECTION:
+resolv  : ;;    www.google.com, type = A, class = IN
+resolv  :
+resolv  : Hex dump:
+resolv  : 7483010000010000000000000377777706676f6f676c6503636f6d0000010001
+
+# Response packet
+resolv  : ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29827
+resolv  : ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+resolv  : ;; QUERY SECTION:
+resolv  : ;;    www.google.com, type = A, class = IN
+resolv  :
+resolv  : ;; ANSWER SECTION:
+resolv  : ;; www.google.com.        2m19s IN A  172.217.160.100
+resolv  :
+resolv  : Hex dump:
+resolv  : 7483818000010001000000000377777706676f6f676c6503636f6d0000010001
+resolv  : c00c000100010000008b0004acd9a064
+```
+
+5. Convert the logging into pbtext. Then, Clear the 'id' which is 0x7483 in
+this example from 'query' and 'response' because 'id' is regenerated per
+session. The follows is result pbtext.
+```
+config {
+    call: CALL_GETHOSTBYNAME
+    hostbyname {
+        host: "www.google.com."
+        family: GT_AF_INET
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "172.217.160.100"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+              "\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x8b\x00\x04\xac\xd9\xa0\x64"
+}
+```
+
+## Decode packets in pbtext
+You can invoke the [scapy](https://scapy.net) of python module to extract
+binary packet record in pbtext file. Here are the instructions and example
+for parsing packet.
+
+### Instructions
+Run the following instruction to decode.
+```
+$ python
+>>> from scapy import all as scapy
+>>> scapy.DNS("<paste_hex_string>").show2()
+```
+
+### Example
+Using 'getaddrinfo.topsite.youtube.pbtxt' to be an example here.
+
+1. Find the packet record 'query' or 'response' in .pbtext file.
+```
+query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+          "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+          "\x01"
+```
+2. Run the following instruction.
+
+Start python
+```
+$ python
+```
+Import scapy
+```
+>>> from scapy import all as scapy
+```
+Assign the binary packet to be decoded into a variable. Beware of using
+backslash '\\' for new line if required
+```
+>>> raw_packet=\
+    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77" \
+    "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00" \
+    "\x01"
+```
+Decode packet
+```
+>>> scapy.DNS(raw_packet).show2()
+###[ DNS ]###
+  id        = 0
+  qr        = 0
+  opcode    = QUERY
+  aa        = 0
+  tc        = 0
+  rd        = 1
+  ra        = 0
+  z         = 0
+  ad        = 0
+  cd        = 0
+  rcode     = ok
+  qdcount   = 1
+  ancount   = 0
+  nscount   = 0
+  arcount   = 0
+  \qd        \
+   |###[ DNS Question Record ]###
+   |  qname     = 'www.youtube.com.'
+   |  qtype     = AAAA
+   |  qclass    = IN
+  an        = None
+  ns        = None
+  ar        = None
+```
+
+## Running the tests
+Run the following instruction to test.
+```
+atest resolv_gold_test
+```
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.tls.topsite.google.pbtxt b/tests/testdata/getaddrinfo.tls.topsite.google.pbtxt
new file mode 100644
index 0000000..a4c1661
--- /dev/null
+++ b/tests/testdata/getaddrinfo.tls.topsite.google.pbtxt
@@ -0,0 +1,61 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 26 NOV 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.google.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2404:6800:4008:801::2004"
+    addresses: "172.217.27.132"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x03\x77\x77\x77"
+              "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+              "\x00\x00\x29\x20\x00\x00\x00\x80\x00\x00\x55\x00\x0c\x00\x51\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x01\x03\x77\x77\x77"
+              "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+              "\xc0\x0c\x00\x1c\x00\x01\x00\x00\x01\x2b\x00\x10\x24\x04\x68\x00"
+              "\x40\x08\x08\x01\x00\x00\x00\x00\x00\x00\x20\x04\x00\x00\x29\x02"
+              "\x00\x00\x00\x80\x00\x01\x8d\x00\x0c\x01\x89\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x03\x77\x77\x77"
+              "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+              "\x00\x00\x29\x20\x00\x00\x00\x80\x00\x00\x55\x00\x0c\x00\x51\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x01\x03\x77\x77\x77"
+              "\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+              "\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x86\x00\x04\xac\xd9\x1b\x84"
+              "\x00\x00\x29\x02\x00\x00\x00\x80\x00\x01\x99\x00\x0c\x01\x95\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.amazon.pbtxt b/tests/testdata/getaddrinfo.topsite.amazon.pbtxt
new file mode 100644
index 0000000..4b305f1
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.amazon.pbtxt
@@ -0,0 +1,45 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.amazon.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "64:ff9b::d23:9aa5"
+    addresses: "104.124.254.132"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+              "\xc0\x0c\x00\x05\x00\x01\x00\x00\x03\x45\x00\x0a\x03\x77\x77\x77"
+              "\x03\x63\x64\x6e\xc0\x10\xc0\x2c\x00\x05\x00\x01\x00\x00\x00\x06"
+              "\x00\x1f\x0e\x64\x33\x61\x67\x34\x68\x75\x6b\x6b\x68\x36\x32\x79"
+              "\x6e\x0a\x63\x6c\x6f\x75\x64\x66\x72\x6f\x6e\x74\x03\x6e\x65\x74"
+              "\x00\xc0\x42\x00\x1c\x00\x01\x00\x00\x00\x3b\x00\x10\x00\x64\xff"
+              "\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x23\x9a\xa5"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x04\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+              "\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\xbe\x00\x0a\x03\x77\x77\x77"
+              "\x03\x63\x64\x6e\xc0\x10\xc0\x2c\x00\x05\x00\x01\x00\x00\x00\x20"
+              "\x00\x1c\x03\x77\x77\x77\x06\x61\x6d\x61\x7a\x6f\x6e\x03\x63\x6f"
+              "\x6d\x07\x65\x64\x67\x65\x6b\x65\x79\x03\x6e\x65\x74\x00\xc0\x42"
+              "\x00\x05\x00\x01\x00\x00\x01\x10\x00\x18\x06\x65\x31\x35\x33\x31"
+              "\x36\x03\x65\x32\x32\x0a\x61\x6b\x61\x6d\x61\x69\x65\x64\x67\x65"
+              "\xc0\x59\xc0\x6a\x00\x01\x00\x01\x00\x00\x00\x13\x00\x04\x68\x7c"
+              "\xfe\x84"
+}
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.topsite.bing.pbtxt b/tests/testdata/getaddrinfo.topsite.bing.pbtxt
new file mode 100644
index 0000000..b7bfb50
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.bing.pbtxt
@@ -0,0 +1,48 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.bing.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2620:1ec:c11::200"
+    addresses: "204.79.197.200"
+    addresses: "13.107.21.200"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0\x0c"
+              "\x00\x05\x00\x01\x00\x00\x0b\x41\x00\x2a\x06\x61\x2d\x30\x30\x30"
+              "\x31\x0a\x61\x2d\x61\x66\x64\x65\x6e\x74\x72\x79\x03\x6e\x65\x74"
+              "\x0e\x74\x72\x61\x66\x66\x69\x63\x6d\x61\x6e\x61\x67\x65\x72\x03"
+              "\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00\x00\x00\x04\x00\x17"
+              "\x0b\x64\x75\x61\x6c\x2d\x61\x2d\x30\x30\x30\x31\x08\x61\x2d\x6d"
+              "\x73\x65\x64\x67\x65\xc0\x4f\xc0\x60\x00\x1c\x00\x01\x00\x00\x00"
+              "\x01\x00\x10\x26\x20\x01\xec\x0c\x11\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x02\x00"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x04\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x62\x69\x6e\x67\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c"
+              "\x00\x05\x00\x01\x00\x00\x0c\x69\x00\x2a\x06\x61\x2d\x30\x30\x30"
+              "\x31\x0a\x61\x2d\x61\x66\x64\x65\x6e\x74\x72\x79\x03\x6e\x65\x74"
+              "\x0e\x74\x72\x61\x66\x66\x69\x63\x6d\x61\x6e\x61\x67\x65\x72\x03"
+              "\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00\x00\x00\x0d\x00\x17"
+              "\x0b\x64\x75\x61\x6c\x2d\x61\x2d\x30\x30\x30\x31\x08\x61\x2d\x6d"
+              "\x73\x65\x64\x67\x65\xc0\x4f\xc0\x60\x00\x01\x00\x01\x00\x00\x00"
+              "\x2c\x00\x04\xcc\x4f\xc5\xc8\xc0\x60\x00\x01\x00\x01\x00\x00\x00"
+              "\x2c\x00\x04\x0d\x6b\x15\xc8"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.ebay.pbtxt b/tests/testdata/getaddrinfo.topsite.ebay.pbtxt
new file mode 100644
index 0000000..2f562f0
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.ebay.pbtxt
@@ -0,0 +1,44 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.ebay.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "64:ff9b::685d:168"
+    addresses: "104.93.1.104"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0\x0c"
+              "\x00\x05\x00\x01\x00\x00\x00\x0b\x00\x1f\x08\x73\x6c\x6f\x74\x39"
+              "\x34\x32\x38\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x07\x65\x64\x67"
+              "\x65\x6b\x65\x79\x03\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00"
+              "\x00\x07\xe6\x00\x15\x05\x65\x39\x34\x32\x38\x01\x62\x0a\x61\x6b"
+              "\x61\x6d\x61\x69\x65\x64\x67\x65\xc0\x44\xc0\x55\x00\x1c\x00\x01"
+              "\x00\x00\x00\x13\x00\x10\x00\x64\xff\x9b\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x68\x5d\x01\x68"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c"
+              "\x00\x05\x00\x01\x00\x00\x00\x76\x00\x1f\x08\x73\x6c\x6f\x74\x39"
+              "\x34\x32\x38\x04\x65\x62\x61\x79\x03\x63\x6f\x6d\x07\x65\x64\x67"
+              "\x65\x6b\x65\x79\x03\x6e\x65\x74\x00\xc0\x2a\x00\x05\x00\x01\x00"
+              "\x00\x08\x28\x00\x15\x05\x65\x39\x34\x32\x38\x01\x62\x0a\x61\x6b"
+              "\x61\x6d\x61\x69\x65\x64\x67\x65\xc0\x44\xc0\x55\x00\x01\x00\x01"
+              "\x00\x00\x00\x13\x00\x04\x68\x5d\x01\x68"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.facebook.pbtxt b/tests/testdata/getaddrinfo.topsite.facebook.pbtxt
new file mode 100644
index 0000000..9f910f2
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.facebook.pbtxt
@@ -0,0 +1,34 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "facebook.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2a03:2880:f102:83:face:b00c:0:25de"
+    addresses: "31.13.95.36"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x08\x66\x61\x63"
+              "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x08\x66\x61\x63"
+              "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0\x0c"
+              "\x00\x1c\x00\x01\x00\x00\x01\x2b\x00\x10\x2a\x03\x28\x80\xf1\x02"
+              "\x00\x83\xfa\xce\xb0\x0c\x00\x00\x25\xde"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x08\x66\x61\x63"
+              "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x08\x66\x61\x63"
+              "\x65\x62\x6f\x6f\x6b\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0\x0c"
+              "\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\x1f\x0d\x5f\x24"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.google.pbtxt b/tests/testdata/getaddrinfo.topsite.google.pbtxt
new file mode 100644
index 0000000..bc40a33
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.google.pbtxt
@@ -0,0 +1,86 @@
+# Location: Tokyo, Japan
+# Network: IIJ Mobile
+# Date: 16 AUG 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.google.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2404:6800:4004:80f::2004"
+    addresses: "172.217.26.4"
+}
+packet_mapping {
+    query:    # Header
+              "\x00\x00"         # Transaction ID: 0x0000
+              "\x01\x00"         # Flags: rd
+              "\x00\x01"         # Questions: 1
+              "\x00\x00"         # Answer RRs: 0
+              "\x00\x00"         # Authority RRs: 0
+              "\x00\x00"         # Additional RRs: 0
+              # Queries
+              "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+                                 # Name: www.google.com
+              "\x00\x1c"         # Type: AAAA
+              "\x00\x01"         # Class: IN
+    response: # Header
+              "\x00\x00"         # Transaction ID: 0x0000
+              "\x81\x80"         # Flags: qr rd ra
+              "\x00\x01"         # Questions: 1
+              "\x00\x01"         # Answer RRs: 1
+              "\x00\x00"         # Authority RRs: 0
+              "\x00\x00"         # Additional RRs: 0
+              # Queries
+              "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+                                 # Name: www.google.com
+              "\x00\x1c"         # Type: AAAA
+              "\x00\x01"         # Class: IN
+              # Answers
+              "\xc0\x0c"         # Name: www.google.com
+              "\x00\x1c"         # Type: AAAA
+              "\x00\x01"         # Class: IN
+              "\x00\x00\x00\xee" # Time to live: 238
+              "\x00\x10"         # Data length: 16
+              "\x24\x04\x68\x00\x40\x04\x08\x0f\x00\x00\x00\x00\x00\x00\x20\x04"
+                                 # Address: 2404:6800:4004:80f::2004
+}
+packet_mapping {
+    query:    # Header
+              "\x00\x00"         # Transaction ID: 0x0000
+              "\x01\x00"         # Flags: rd
+              "\x00\x01"         # Questions: 1
+              "\x00\x00"         # Answer RRs: 0
+              "\x00\x00"         # Authority RRs: 0
+              "\x00\x00"         # Additional RRs: 0
+              # Queries
+              "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+                                 # Name: www.google.com
+              "\x00\x01"         # Type: A
+              "\x00\x01"         # Class: IN
+    response: # Header
+              "\x00\x00"         # Transaction ID: 0x0000
+              "\x81\x80"         # Flags: qr rd ra
+              "\x00\x01"         # Questions: 1
+              "\x00\x01"         # Answer RRs: 1
+              "\x00\x00"         # Authority RRs: 0
+              "\x00\x00"         # Additional RRs: 0
+              # Queries
+              "\x03\x77\x77\x77\x06\x67\x6f\x6f\x67\x6c\x65\x03\x63\x6f\x6d\x00"
+                                 # Name: www.google.com
+              "\x00\x01"         # Type: A
+              "\x00\x01"         # Class: IN
+              # Answers
+              "\xc0\x0c"         # Name: www.google.com
+              "\x00\x01"         # Type: A
+              "\x00\x01"         # Class: IN
+              "\x00\x00\x00\x03" # Time to live: 3
+              "\x00\x04"         # Data length: 4
+              "\xac\xd9\x1a\x04" # Address: 172.217.26.4
+}
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.topsite.netflix.pbtxt b/tests/testdata/getaddrinfo.topsite.netflix.pbtxt
new file mode 100644
index 0000000..1b12eb4
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.netflix.pbtxt
@@ -0,0 +1,77 @@
+# Location: Tokyo, Japan
+# Network: IIJ Mobile
+# Date: 14 AUG 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.netflix.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2620:108:700f::22d1:6443"
+    addresses: "2620:108:700f::36c8:5c97"
+    addresses: "2620:108:700f::36ba:3a73"
+    addresses: "2620:108:700f::36bf:5e94"
+    addresses: "2620:108:700f::3644:8d41"
+    addresses: "2620:108:700f::36bb:ed4c"
+    addresses: "2620:108:700f::3645:f643"
+    addresses: "2620:108:700f::3694:303e"
+    addresses: "52.35.228.185"
+    addresses: "52.38.194.36"
+    addresses: "52.33.42.218"
+    addresses: "52.33.159.26"
+    addresses: "52.35.47.68"
+    addresses: "52.36.31.140"
+    addresses: "52.35.125.204"
+    addresses: "52.39.27.188"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+              "\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x0a\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+              "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\x79\x00\x0a\x03\x77\x77"
+              "\x77\x03\x67\x65\x6f\xc0\x10\xc0\x2d\x00\x05\x00\x01\x00\x00\x00"
+              "\x79\x00\x17\x03\x77\x77\x77\x09\x75\x73\x2d\x77\x65\x73\x74\x2d"
+              "\x32\x06\x70\x72\x6f\x64\x61\x61\xc0\x10\xc0\x43\x00\x1c\x00\x01"
+              "\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00"
+              "\x00\x00\x22\xd1\x64\x43\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f"
+              "\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\xc8"
+              "\x5c\x97\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20"
+              "\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\xba\x3a\x73\xc0\x43"
+              "\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f"
+              "\x00\x00\x00\x00\x00\x00\x36\xbf\x5e\x94\xc0\x43\x00\x1c\x00\x01"
+              "\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00"
+              "\x00\x00\x36\x44\x8d\x41\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f"
+              "\x00\x10\x26\x20\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\xbb"
+              "\xed\x4c\xc0\x43\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20"
+              "\x01\x08\x70\x0f\x00\x00\x00\x00\x00\x00\x36\x45\xf6\x43\xc0\x43"
+              "\x00\x1c\x00\x01\x00\x00\x00\x2f\x00\x10\x26\x20\x01\x08\x70\x0f"
+              "\x00\x00\x00\x00\x00\x00\x36\x94\x30\x3e"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x0a\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x6e\x65\x74\x66\x6c\x69\x78\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\x82\x00\x0a\x03\x77\x77"
+              "\x77\x03\x67\x65\x6f\xc0\x10\xc0\x2d\x00\x05\x00\x01\x00\x00\x00"
+              "\x82\x00\x17\x03\x77\x77\x77\x09\x75\x73\x2d\x77\x65\x73\x74\x2d"
+              "\x32\x06\x70\x72\x6f\x64\x61\x61\xc0\x10\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x23\xe4\xb9\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x26\xc2\x24\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x21\x2a\xda\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x21\x9f\x1a\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x23\x2f\x44\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x24\x1f\x8c\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x23\x7d\xcc\xc0\x43\x00\x01\x00\x01"
+              "\x00\x00\x00\x0c\x00\x04\x34\x27\x1b\xbc"
+}
\ No newline at end of file
diff --git a/tests/testdata/getaddrinfo.topsite.reddit.pbtxt b/tests/testdata/getaddrinfo.topsite.reddit.pbtxt
new file mode 100644
index 0000000..3ccf721
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.reddit.pbtxt
@@ -0,0 +1,54 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.reddit.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "64:ff9b::9765:18c"
+    addresses: "64:ff9b::9765:418c"
+    addresses: "64:ff9b::9765:818c"
+    addresses: "64:ff9b::9765:c18c"
+    addresses: "151.101.1.140"
+    addresses: "151.101.65.140"
+    addresses: "151.101.129.140"
+    addresses: "151.101.193.140"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+              "\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\x53\x00\x17\x06\x72\x65\x64"
+              "\x64\x69\x74\x03\x6d\x61\x70\x06\x66\x61\x73\x74\x6c\x79\x03\x6e"
+              "\x65\x74\x00\xc0\x2c\x00\x1c\x00\x01\x00\x00\x00\x1d\x00\x10\x00"
+              "\x64\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x97\x65\x01\x8c\xc0"
+              "\x2c\x00\x1c\x00\x01\x00\x00\x00\x1d\x00\x10\x00\x64\xff\x9b\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x97\x65\x41\x8c\xc0\x2c\x00\x1c\x00"
+              "\x01\x00\x00\x00\x1d\x00\x10\x00\x64\xff\x9b\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x97\x65\x81\x8c\xc0\x2c\x00\x1c\x00\x01\x00\x00\x00"
+              "\x1d\x00\x10\x00\x64\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x97"
+              "\x65\xc1\x8c"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x06\x72\x65\x64\x64\x69\x74\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+              "\xc0\x0c\x00\x05\x00\x01\x00\x00\x00\xc9\x00\x17\x06\x72\x65\x64"
+              "\x64\x69\x74\x03\x6d\x61\x70\x06\x66\x61\x73\x74\x6c\x79\x03\x6e"
+              "\x65\x74\x00\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+              "\x65\x01\x8c\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+              "\x65\x41\x8c\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+              "\x65\x81\x8c\xc0\x2c\x00\x01\x00\x01\x00\x00\x00\x1d\x00\x04\x97"
+              "\x65\xc1\x8c"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.wikipedia.pbtxt b/tests/testdata/getaddrinfo.topsite.wikipedia.pbtxt
new file mode 100644
index 0000000..c6415f0
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.wikipedia.pbtxt
@@ -0,0 +1,40 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.wikipedia.org."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2001:df2:e500:ed1a::1"
+    addresses: "103.102.166.224"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+              "\x1c\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+              "\x1c\x00\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x52\x6a\x00\x11\x04"
+              "\x64\x79\x6e\x61\x09\x77\x69\x6b\x69\x6d\x65\x64\x69\x61\xc0\x1a"
+              "\xc0\x2f\x00\x1c\x00\x01\x00\x00\x02\x57\x00\x10\x20\x01\x0d\xf2"
+              "\xe5\x00\xed\x1a\x00\x00\x00\x00\x00\x00\x00\x01"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+              "\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x09\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x03\x6f\x72\x67\x00\x00"
+              "\x01\x00\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x51\xa8\x00\x11\x04"
+              "\x64\x79\x6e\x61\x09\x77\x69\x6b\x69\x6d\x65\x64\x69\x61\xc0\x1a"
+              "\xc0\x2f\x00\x01\x00\x01\x00\x00\x02\x57\x00\x04\x67\x66\xa6\xe0"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.yahoo.pbtxt b/tests/testdata/getaddrinfo.topsite.yahoo.pbtxt
new file mode 100644
index 0000000..9223dad
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.yahoo.pbtxt
@@ -0,0 +1,44 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.yahoo.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2406:2000:ec:815::3"
+    addresses: "2406:2000:ec:815::4"
+    addresses: "124.108.103.104"
+    addresses: "124.108.103.103"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x1c\x00\x01\xc0"
+              "\x0c\x00\x05\x00\x01\x00\x00\x02\x29\x00\x16\x0d\x61\x74\x73\x76"
+              "\x32\x2d\x66\x70\x2d\x73\x68\x65\x64\x03\x77\x67\x31\x01\x62\xc0"
+              "\x10\xc0\x2b\x00\x1c\x00\x01\x00\x00\x00\x36\x00\x10\x24\x06\x20"
+              "\x00\x00\xec\x08\x15\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x2b\x00"
+              "\x1c\x00\x01\x00\x00\x00\x36\x00\x10\x24\x06\x20\x00\x00\xec\x08"
+              "\x15\x00\x00\x00\x00\x00\x00\x00\x04"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x03\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x05\x79\x61\x68\x6f\x6f\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\xc0"
+              "\x0c\x00\x05\x00\x01\x00\x00\x06\xe3\x00\x16\x0d\x61\x74\x73\x76"
+              "\x32\x2d\x66\x70\x2d\x73\x68\x65\x64\x03\x77\x67\x31\x01\x62\xc0"
+              "\x10\xc0\x2b\x00\x01\x00\x01\x00\x00\x00\x0e\x00\x04\x7c\x6c\x67"
+              "\x68\xc0\x2b\x00\x01\x00\x01\x00\x00\x00\x0e\x00\x04\x7c\x6c\x67"
+              "\x67"
+}
diff --git a/tests/testdata/getaddrinfo.topsite.youtube.pbtxt b/tests/testdata/getaddrinfo.topsite.youtube.pbtxt
new file mode 100644
index 0000000..6ee64e4
--- /dev/null
+++ b/tests/testdata/getaddrinfo.topsite.youtube.pbtxt
@@ -0,0 +1,49 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 23 SEP 2019
+
+config {
+    call: CALL_GETADDRINFO
+    addrinfo {
+        host: "www.youtube.com."
+        family: GT_AF_UNSPEC
+        socktype: GT_SOCK_DGRAM
+        protocol: GT_IPPROTO_IP
+        ai_flags: 1024
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "2404:6800:4012:1::200e"
+    addresses: "172.217.24.14"
+    addresses: "216.58.200.46"
+    addresses: "172.217.160.78"
+    addresses: "172.217.27.142"
+    addresses: "172.217.160.110"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+              "\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x1c\x00"
+              "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x4c\x00\x16\x0a\x79\x6f"
+              "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+              "\x65\xc0\x18\xc0\x2d\x00\x1c\x00\x01\x00\x00\x01\x18\x00\x10\x24"
+              "\x04\x68\x00\x40\x12\x00\x01\x00\x00\x00\x00\x00\x00\x20\x0e"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x2c\x00\x16\x0a\x79\x6f"
+              "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+              "\x65\xc0\x18\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+              "\xd9\x18\x0e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xd8"
+              "\x3a\xc8\x2e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+              "\xd9\xa0\x4e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+              "\xd9\x1b\x8e\xc0\x2d\x00\x01\x00\x01\x00\x00\x00\xf8\x00\x04\xac"
+              "\xd9\xa0\x6e"
+}
\ No newline at end of file
diff --git a/tests/testdata/gethostbyname.tls.topsite.youtube.pbtxt b/tests/testdata/gethostbyname.tls.topsite.youtube.pbtxt
new file mode 100644
index 0000000..5f32518
--- /dev/null
+++ b/tests/testdata/gethostbyname.tls.topsite.youtube.pbtxt
@@ -0,0 +1,48 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 26 NOV 2019
+
+config {
+    call: CALL_GETHOSTBYNAME
+    hostbyname {
+        host: "www.youtube.com."
+        family: GT_AF_INET
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "216.58.200.46"
+    addresses: "172.217.160.78"
+    addresses: "216.58.200.238"
+    addresses: "172.217.27.142"
+    addresses: "172.217.160.110"
+    addresses: "172.217.24.14"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01\x00\x00\x29\x20\x00\x00\x00\x80\x00\x00\x54\x00\x0c\x00\x50"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x07\x00\x00\x00\x01\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x24\x00\x16\x0a\x79\x6f"
+              "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+              "\x65\xc0\x18\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xd8"
+              "\x3a\xc8\x2e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\xa0\x4e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xd8"
+              "\x3a\xc8\xee\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\x1b\x8e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\xa0\x6e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\x18\x0e\x00\x00\x29\x02\x00\x00\x00\x80\x00\x01\x26\x00\x0c"
+              "\x01\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+              "\x00\x00\x00\x00"
+}
\ No newline at end of file
diff --git a/tests/testdata/gethostbyname.topsite.youtube.pbtxt b/tests/testdata/gethostbyname.topsite.youtube.pbtxt
new file mode 100644
index 0000000..d113e55
--- /dev/null
+++ b/tests/testdata/gethostbyname.topsite.youtube.pbtxt
@@ -0,0 +1,34 @@
+# Location: Taipei, Taiwan
+# Network: Wifi, GoogleGuest
+# Date: 15 OCT 2019
+
+config {
+    call: CALL_GETHOSTBYNAME
+    hostbyname {
+        host: "www.youtube.com."
+        family: GT_AF_INET
+    };
+}
+result {
+    return_code: GT_EAI_NO_ERROR
+    addresses: "216.58.200.46"
+    addresses: "172.217.160.78"
+    addresses: "172.217.27.142"
+    addresses: "172.217.160.110"
+    addresses: "172.217.24.14"
+}
+packet_mapping {
+    query:    "\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01"
+    response: "\x00\x00\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\x03\x77\x77\x77"
+              "\x07\x79\x6f\x75\x74\x75\x62\x65\x03\x63\x6f\x6d\x00\x00\x01\x00"
+              "\x01\xc0\x0c\x00\x05\x00\x01\x00\x00\x54\x5f\x00\x16\x0a\x79\x6f"
+              "\x75\x74\x75\x62\x65\x2d\x75\x69\x01\x6c\x06\x67\x6f\x6f\x67\x6c"
+              "\x65\xc0\x18\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xd8"
+              "\x3a\xc8\x2e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\xa0\x4e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\x1b\x8e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\xa0\x6e\xc0\x2d\x00\x01\x00\x01\x00\x00\x01\x2b\x00\x04\xac"
+              "\xd9\x18\x0e"
+}
\ No newline at end of file
diff --git a/util.cpp b/util.cpp
new file mode 100644
index 0000000..b8df74c
--- /dev/null
+++ b/util.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "util.h"
+
+#include <android-base/parseint.h>
+#include <server_configurable_flags/get_flags.h>
+
+using android::base::ParseInt;
+using server_configurable_flags::GetServerConfigurableFlag;
+
+socklen_t sockaddrSize(const sockaddr* sa) {
+    if (sa == nullptr) return 0;
+
+    switch (sa->sa_family) {
+        case AF_INET:
+            return sizeof(sockaddr_in);
+        case AF_INET6:
+            return sizeof(sockaddr_in6);
+        default:
+            return 0;
+    }
+}
+
+socklen_t sockaddrSize(const sockaddr_storage& ss) {
+    return sockaddrSize(reinterpret_cast<const sockaddr*>(&ss));
+}
+
+int getExperimentFlagInt(const std::string& flagName, int defaultValue) {
+    int val = defaultValue;
+    ParseInt(GetServerConfigurableFlag("netd_native", flagName, ""), &val);
+    return val;
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..43b1376
--- /dev/null
+++ b/util.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <string>
+
+#include <netinet/in.h>
+
+socklen_t sockaddrSize(const sockaddr* sa);
+socklen_t sockaddrSize(const sockaddr_storage& ss);
+
+// TODO: getExperimentFlagString
+int getExperimentFlagInt(const std::string& flagName, int defaultValue);