Merge "Fix fortify_fatal issue during DNSServiceProcessResult()"
diff --git a/Android.bp b/Android.bp
index 64831b8..32e719b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11,9 +11,9 @@
         // Override -Wno-error=implicit-fallthrough from soong
         "-Werror=implicit-fallthrough",
         "-Wnullable-to-nonnull-conversion",
+        "-Wsign-compare",
         "-Wthread-safety",
         "-Wunused-parameter",
-        "-Wsign-compare",
     ],
     tidy: true,
     tidy_checks: [
diff --git a/client/FwmarkClient.cpp b/client/FwmarkClient.cpp
index 9435528..ed1459c 100644
--- a/client/FwmarkClient.cpp
+++ b/client/FwmarkClient.cpp
@@ -25,7 +25,7 @@
 #include <sys/un.h>
 #include <unistd.h>
 
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+#include <iterator>
 
 namespace {
 
@@ -94,7 +94,7 @@
     msghdr message;
     memset(&message, 0, sizeof(message));
     message.msg_iov = iov;
-    message.msg_iovlen = ARRAY_SIZE(iov);
+    message.msg_iovlen = std::size(iov);
 
     union {
         cmsghdr cmh;
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index 68aa3c3..5586347 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -385,15 +385,16 @@
     return FwmarkClient().send(&command, -1, nullptr);
 }
 
-extern "C" int resNetworkQuery(unsigned netId, const char* dname, int ns_class, int ns_type) {
+extern "C" int resNetworkQuery(unsigned netId, const char* dname, int ns_class, int ns_type,
+                               uint32_t flags) {
     std::vector<uint8_t> buf(MAX_CMD_SIZE, 0);
     int len = res_mkquery(ns_o_query, dname, ns_class, ns_type, nullptr, 0, nullptr, buf.data(),
                           MAX_CMD_SIZE);
 
-    return resNetworkSend(netId, buf.data(), len);
+    return resNetworkSend(netId, buf.data(), len, flags);
 }
 
-extern "C" int resNetworkSend(unsigned netId, const uint8_t* msg, size_t msglen) {
+extern "C" int resNetworkSend(unsigned netId, const uint8_t* msg, size_t msglen, uint32_t) {
     // Encode
     // Base 64 encodes every 3 bytes into 4 characters, but then adds padding to the next
     // multiple of 4 and a \0
diff --git a/include/NetdClient.h b/include/NetdClient.h
index c361802..7d7a0e8 100644
--- a/include/NetdClient.h
+++ b/include/NetdClient.h
@@ -49,11 +49,11 @@
 
 int deleteTagData(uint32_t tag, uid_t uid);
 
-int resNetworkQuery(unsigned netId, const char* dname, int classType, int type);
+int resNetworkQuery(unsigned netId, const char* dname, int classType, int type, uint32_t flags);
 
 int resNetworkResult(int query_fd, int* rcode, uint8_t* answer, size_t anslen);
 
-int resNetworkSend(unsigned netId, const uint8_t* msg, size_t msglen);
+int resNetworkSend(unsigned netId, const uint8_t* msg, size_t msglen, uint32_t flags);
 
 void resNetworkCancel(int nsend_fd);
 __END_DECLS
diff --git a/libnetdbpf/include/netdbpf/bpf_shared.h b/libnetdbpf/include/netdbpf/bpf_shared.h
index bffb1a1..f824e08 100644
--- a/libnetdbpf/include/netdbpf/bpf_shared.h
+++ b/libnetdbpf/include/netdbpf/bpf_shared.h
@@ -55,38 +55,23 @@
 
 #define BPF_PATH "/sys/fs/bpf"
 
-#define BPF_EGRESS_PROG_PATH BPF_PATH "/egress_prog"
-#define BPF_INGRESS_PROG_PATH BPF_PATH "/ingress_prog"
-#define XT_BPF_INGRESS_PROG_PATH BPF_PATH "/xt_bpf_ingress_prog"
-#define XT_BPF_EGRESS_PROG_PATH BPF_PATH "/xt_bpf_egress_prog"
-#define XT_BPF_WHITELIST_PROG_PATH BPF_PATH "/xt_bpf_whitelist_prog"
-#define XT_BPF_BLACKLIST_PROG_PATH BPF_PATH "/xt_bpf_blacklist_prog"
+#define BPF_EGRESS_PROG_PATH BPF_PATH "/prog_netd_cgroupskb_egress_stats"
+#define BPF_INGRESS_PROG_PATH BPF_PATH "/prog_netd_cgroupskb_ingress_stats"
+#define XT_BPF_INGRESS_PROG_PATH BPF_PATH "/prog_netd_skfilter_ingress_xtbpf"
+#define XT_BPF_EGRESS_PROG_PATH BPF_PATH "/prog_netd_skfilter_egress_xtbpf"
+#define XT_BPF_WHITELIST_PROG_PATH BPF_PATH "/prog_netd_skfilter_whitelist_xtbpf"
+#define XT_BPF_BLACKLIST_PROG_PATH BPF_PATH "/prog_netd_skfilter_blacklist_xtbpf"
 
-#define COOKIE_TAG_MAP_PATH BPF_PATH "/traffic_cookie_tag_map"
-#define UID_COUNTERSET_MAP_PATH BPF_PATH "/traffic_uid_counterset_map"
-#define APP_UID_STATS_MAP_PATH BPF_PATH "/traffic_app_uid_stats_map"
-#define UID_STATS_MAP_PATH BPF_PATH "/traffic_uid_stats_map"
-#define TAG_STATS_MAP_PATH BPF_PATH "/traffic_tag_stats_map"
-#define IFACE_INDEX_NAME_MAP_PATH BPF_PATH "/traffic_iface_index_name_map"
-#define IFACE_STATS_MAP_PATH BPF_PATH "/traffic_iface_stats_map"
-#define CONFIGURATION_MAP_PATH BPF_PATH "/traffic_configuration_map"
-#define UID_OWNER_MAP_PATH BPF_PATH "/traffic_uid_owner_map"
-
-#define BPF_CGROUP_INGRESS_PROG_NAME "cgroup_ingress_prog"
-#define BPF_CGROUP_EGRESS_PROG_NAME "cgroup_egress_prog"
-#define XT_BPF_INGRESS_PROG_NAME "xt_ingress_prog"
-#define XT_BPF_EGRESS_PROG_NAME "xt_egress_prog"
-#define XT_BPF_WHITELIST_PROG_NAME "xt_whitelist_prog"
-#define XT_BPF_BLACKLIST_PROG_NAME "xt_blacklist_prog"
-
-#define COOKIE_TAG_MAP 0xbfceaaffffffffff
-#define UID_COUNTERSET_MAP 0xbfdceeafffffffff
-#define APP_UID_STATS_MAP 0xbfa1daafffffffff
-#define UID_STATS_MAP 0xbfdaafffffffffff
-#define TAG_STATS_MAP 0xbfaaafffffffffff
-#define IFACE_STATS_MAP 0xbf1faceaafffffff
-#define CONFIGURATION_MAP 0Xbfc0fa0affffffff
-#define UID_OWNER_MAP 0xbfbad1d1daffffff
+#define COOKIE_TAG_MAP_PATH BPF_PATH "/map_netd_cookie_tag_map"
+#define UID_COUNTERSET_MAP_PATH BPF_PATH "/map_netd_uid_counterset_map"
+#define APP_UID_STATS_MAP_PATH BPF_PATH "/map_netd_app_uid_stats_map"
+#define UID_STATS_MAP_PATH BPF_PATH "/map_netd_uid_stats_map"
+#define TAG_STATS_MAP_PATH BPF_PATH "/map_netd_tag_stats_map"
+#define IFACE_INDEX_NAME_MAP_PATH BPF_PATH "/map_netd_iface_index_name_map"
+#define IFACE_STATS_MAP_PATH BPF_PATH "/map_netd_iface_stats_map"
+#define CONFIGURATION_MAP_PATH BPF_PATH "/map_netd_configuration_map"
+#define UID_OWNER_MAP_PATH BPF_PATH "/map_netd_uid_owner_map"
+#define UID_PERMISSION_MAP_PATH BPF_PATH "/map_netd_uid_permission_map"
 
 enum UidOwnerMatchType {
     NO_MATCH,
diff --git a/libnetdutils/InternetAddressesTest.cpp b/libnetdutils/InternetAddressesTest.cpp
index 59d8210..2129ca7 100644
--- a/libnetdutils/InternetAddressesTest.cpp
+++ b/libnetdutils/InternetAddressesTest.cpp
@@ -383,24 +383,22 @@
             {"2001:db8:cafe:d00d::", 46, "2001:db8:cafc::"},
     };
 
-    int totalTests = 0;
-    for (unsigned int i = 0; i < arraysize(testExpectations); i++, totalTests++) {
+    for (const auto& expectation : testExpectations) {
         IPAddress ip;
-        EXPECT_TRUE(IPAddress::forString(testExpectations[i].ip, &ip))
-                << "Failed to parse IP address " << testExpectations[i].ip;
+        EXPECT_TRUE(IPAddress::forString(expectation.ip, &ip))
+                << "Failed to parse IP address " << expectation.ip;
 
         IPAddress ipTruncated;
-        EXPECT_TRUE(IPAddress::forString(testExpectations[i].ipTruncated, &ipTruncated))
-                << "Failed to parse IP address " << testExpectations[i].ipTruncated;
+        EXPECT_TRUE(IPAddress::forString(expectation.ipTruncated, &ipTruncated))
+                << "Failed to parse IP address " << expectation.ipTruncated;
 
-        IPPrefix prefix(ip, testExpectations[i].cidrLen);
+        IPPrefix prefix(ip, expectation.cidrLen);
 
-        EXPECT_EQ(testExpectations[i].cidrLen, prefix.length())
-                << "Unexpected cidrLen " << testExpectations[i].cidrLen;
+        EXPECT_EQ(expectation.cidrLen, prefix.length())
+                << "Unexpected cidrLen " << expectation.cidrLen;
         EXPECT_EQ(ipTruncated, prefix.ip())
                 << "Unexpected IP truncation: " << prefix.ip() << ", expected: " << ipTruncated;
     }
-    EXPECT_EQ(static_cast<int>(arraysize(testExpectations)), totalTests);
 }
 
 TEST(IPPrefixTest, GamutOfOperators) {
diff --git a/resolv/PrivateDnsConfiguration.cpp b/resolv/PrivateDnsConfiguration.cpp
index 616662e..b262a2f 100644
--- a/resolv/PrivateDnsConfiguration.cpp
+++ b/resolv/PrivateDnsConfiguration.cpp
@@ -26,15 +26,15 @@
 #include "netdutils/BackoffSequence.h"
 
 int resolv_set_private_dns_for_net(unsigned netid, uint32_t mark, const char** servers,
-                                   const unsigned numServers, const char* tlsName,
-                                   const uint8_t** fingerprints, const unsigned numFingerprint) {
+                                   const int numServers, const char* tlsName,
+                                   const uint8_t** fingerprints, const int numFingerprint) {
     std::vector<std::string> tlsServers;
-    for (unsigned i = 0; i < numServers; i++) {
+    for (int i = 0; i < numServers; i++) {
         tlsServers.push_back(std::string(servers[i]));
     }
 
     std::set<std::vector<uint8_t>> tlsFingerprints;
-    for (unsigned i = 0; i < numFingerprint; i++) {
+    for (int i = 0; i < numFingerprint; i++) {
         // Each fingerprint stored are 32(SHA256_SIZE) bytes long.
         tlsFingerprints.emplace(std::vector<uint8_t>(fingerprints[i], fingerprints[i] + 32));
     }
@@ -194,7 +194,7 @@
 
     const auto netPair = mPrivateDnsTransports.find(netId);
     if (netPair != mPrivateDnsTransports.end()) {
-        status->numServers = netPair->second.size();
+        status->numServers = static_cast<int>(netPair->second.size());
         int count = 0;
         for (const auto& serverPair : netPair->second) {
             status->serverStatus[count].ss = serverPair.first.ss;
diff --git a/resolv/getaddrinfo.cpp b/resolv/getaddrinfo.cpp
index 45c3792..860eb98 100644
--- a/resolv/getaddrinfo.cpp
+++ b/resolv/getaddrinfo.cpp
@@ -134,7 +134,8 @@
 static const struct afd* find_afd(int);
 static int ip6_str2scopeid(const char*, struct sockaddr_in6*, u_int32_t*);
 
-static struct addrinfo* getanswer(const querybuf*, int, const char*, int, const struct addrinfo*);
+static struct addrinfo* getanswer(const querybuf*, 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);
 static void _sethtent(FILE**);
@@ -143,10 +144,10 @@
 static bool files_getaddrinfo(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* ai_error);
-static int res_searchN(const char* name, res_target* target, res_state res, int* ai_error);
+static int res_queryN(const char* name, res_target* target, res_state res, int* herrno);
+static int res_searchN(const char* name, res_target* target, res_state res, int* herrno);
 static int res_querydomainN(const char* name, const char* domain, res_target* target, res_state res,
-                            int* ai_error);
+                            int* herrno);
 
 const char* const ai_errlist[] = {
         "Success",
@@ -820,13 +821,13 @@
 #define BOUNDS_CHECK(ptr, count)     \
     do {                             \
         if (eom - (ptr) < (count)) { \
-            h_errno = NO_RECOVERY;   \
+            *herrno = NO_RECOVERY;   \
             return NULL;             \
         }                            \
     } while (0)
 
 static struct addrinfo* getanswer(const querybuf* answer, int anslen, const char* qname, int qtype,
-                                  const struct addrinfo* pai) {
+                                  const struct addrinfo* pai, int* herrno) {
     struct addrinfo sentinel = {};
     struct addrinfo *cur;
     struct addrinfo ai;
@@ -871,12 +872,12 @@
     cp = answer->buf;
     BOUNDED_INCR(HFIXEDSZ);
     if (qdcount != 1) {
-        h_errno = NO_RECOVERY;
+        *herrno = NO_RECOVERY;
         return (NULL);
     }
     n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
     if ((n < 0) || !(*name_ok)(bp)) {
-        h_errno = NO_RECOVERY;
+        *herrno = NO_RECOVERY;
         return (NULL);
     }
     BOUNDED_INCR(n + QFIXEDSZ);
@@ -887,7 +888,7 @@
          */
         n = strlen(bp) + 1; /* for the \0 */
         if (n >= MAXHOSTNAMELEN) {
-            h_errno = NO_RECOVERY;
+            *herrno = NO_RECOVERY;
             return (NULL);
         }
         canonname = bp;
@@ -1003,11 +1004,11 @@
             (void) get_canonname(pai, sentinel.ai_next, qname);
         else
             (void) get_canonname(pai, sentinel.ai_next, canonname);
-        h_errno = NETDB_SUCCESS;
+        *herrno = NETDB_SUCCESS;
         return sentinel.ai_next;
     }
 
-    h_errno = NO_RECOVERY;
+    *herrno = NO_RECOVERY;
     return NULL;
 }
 
@@ -1391,13 +1392,11 @@
 
     querybuf* buf = (querybuf*) malloc(sizeof(*buf));
     if (buf == NULL) {
-        h_errno = NETDB_INTERNAL;
         return EAI_MEMORY;
     }
     querybuf* buf2 = (querybuf*) malloc(sizeof(*buf2));
     if (buf2 == NULL) {
         free(buf);
-        h_errno = NETDB_INTERNAL;
         return EAI_MEMORY;
     }
 
@@ -1466,26 +1465,26 @@
      */
     res_setnetcontext(res, netcontext);
 
-    // Pass ai_error to catch more detailed errors rather than EAI_NODATA.
-    int ai_error = EAI_NODATA;
-    if (res_searchN(name, &q, res, &ai_error) < 0) {
+    int herrno = NETDB_INTERNAL;
+    if (res_searchN(name, &q, res, &herrno) < 0) {
         free(buf);
         free(buf2);
-        return ai_error;  // TODO: Decode error from h_errno like we do below
+        // Pass herrno to catch more detailed errors rather than EAI_NODATA.
+        return herrnoToAiErrno(herrno);
     }
-    ai = getanswer(buf, q.n, q.name, q.qtype, pai);
+    ai = getanswer(buf, q.n, q.name, q.qtype, pai, &herrno);
     if (ai) {
         cur->ai_next = ai;
         while (cur && cur->ai_next) cur = cur->ai_next;
     }
     if (q.next) {
-        ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai);
+        ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai, &herrno);
         if (ai) cur->ai_next = ai;
     }
     free(buf);
     free(buf2);
     if (sentinel.ai_next == NULL) {
-        return herrnoToAiError(h_errno);
+        return herrnoToAiErrno(herrno);
     }
 
     _rfc6724_sort(&sentinel, netcontext->app_mark, netcontext->uid);
@@ -1586,11 +1585,11 @@
  * Perform preliminary check of answer, returning success only
  * if no error is indicated and the answer count is nonzero.
  * Return the size of the response on success, -1 on error.
- * Error number is left in h_errno.
+ * Error number is left in *herrno.
  *
  * Caller must parse answer and determine whether it answers the question.
  */
-static int res_queryN(const char* name, res_target* target, res_state res, int* ai_error) {
+static int res_queryN(const char* name, res_target* target, res_state res, int* herrno) {
     u_char buf[MAXPACKET];
     HEADER* hp;
     int n;
@@ -1632,15 +1631,15 @@
 #ifdef DEBUG
             if (res->options & RES_DEBUG) printf(";; res_nquery: mkquery failed\n");
 #endif
-            h_errno = NO_RECOVERY;
+            *herrno = NO_RECOVERY;
             return n;
         }
 
         n = res_nsend(res, buf, n, answer, anslen, &rcode);
-        *ai_error = rcodeToAiError(rcode);
-
         if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
-            rcode = hp->rcode; /* record most recent error */
+            // Record rcode from DNS response header only if no timeout.
+            // Keep rcode timeout for reporting later if any.
+            if (rcode != RCODE_TIMEOUT) rcode = hp->rcode; /* record most recent error */
             /* if the query choked with EDNS0, retry without EDNS0 */
             if ((res->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0 &&
                 ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) {
@@ -1664,20 +1663,26 @@
 
     if (ancount == 0) {
         switch (rcode) {
+            // Not defined in RFC.
+            case RCODE_TIMEOUT:
+                // DNS metrics monitors DNS query timeout.
+                *herrno = NETD_RESOLV_H_ERRNO_EXT_TIMEOUT;  // extended h_errno.
+                break;
+            // Defined in RFC 1035 section 4.1.1.
             case NXDOMAIN:
-                h_errno = HOST_NOT_FOUND;
+                *herrno = HOST_NOT_FOUND;
                 break;
             case SERVFAIL:
-                h_errno = TRY_AGAIN;
+                *herrno = TRY_AGAIN;
                 break;
             case NOERROR:
-                h_errno = NO_DATA;
+                *herrno = NO_DATA;
                 break;
             case FORMERR:
             case NOTIMP:
             case REFUSED:
             default:
-                h_errno = NO_RECOVERY;
+                *herrno = NO_RECOVERY;
                 break;
         }
         return -1;
@@ -1689,9 +1694,9 @@
  * Formulate a normal query, send, and retrieve answer in supplied buffer.
  * Return the size of the response on success, -1 on error.
  * If enabled, implement search rules until answer or unrecoverable failure
- * is detected.  Error code, if any, is left in h_errno.
+ * is detected.  Error code, if any, is left in *herrno.
  */
-static int res_searchN(const char* name, res_target* target, res_state res, int* ai_error) {
+static int res_searchN(const char* name, res_target* target, res_state res, int* herrno) {
     const char *cp, *const *domain;
     HEADER* hp;
     u_int dots;
@@ -1704,7 +1709,7 @@
     hp = (HEADER*) (void*) target->answer; /*XXX*/
 
     errno = 0;
-    h_errno = HOST_NOT_FOUND; /* default, if we never query */
+    *herrno = HOST_NOT_FOUND; /* default, if we never query */
     dots = 0;
     for (cp = name; *cp; cp++) dots += (*cp == '.');
     trailing_dot = 0;
@@ -1716,9 +1721,9 @@
      */
     saved_herrno = -1;
     if (dots >= res->ndots) {
-        ret = res_querydomainN(name, NULL, target, res, ai_error);
+        ret = res_querydomainN(name, NULL, target, res, herrno);
         if (ret > 0) return (ret);
-        saved_herrno = h_errno;
+        saved_herrno = *herrno;
         tried_as_is++;
     }
 
@@ -1739,7 +1744,7 @@
         _resolv_populate_res_for_net(res);
 
         for (domain = (const char* const*) res->dnsrch; *domain && !done; domain++) {
-            ret = res_querydomainN(name, *domain, target, res, ai_error);
+            ret = res_querydomainN(name, *domain, target, res, herrno);
             if (ret > 0) return ret;
 
             /*
@@ -1756,11 +1761,11 @@
              * fully-qualified.
              */
             if (errno == ECONNREFUSED) {
-                h_errno = TRY_AGAIN;
+                *herrno = TRY_AGAIN;
                 return -1;
             }
 
-            switch (h_errno) {
+            switch (*herrno) {
                 case NO_DATA:
                     got_nodata++;
                     [[fallthrough]];
@@ -1792,7 +1797,7 @@
      * name or whether it ends with a dot.
      */
     if (!tried_as_is) {
-        ret = res_querydomainN(name, NULL, target, res, ai_error);
+        ret = res_querydomainN(name, NULL, target, res, herrno);
         if (ret > 0) return ret;
     }
 
@@ -1805,11 +1810,11 @@
      * the last DNSRCH we did.
      */
     if (saved_herrno != -1)
-        h_errno = saved_herrno;
+        *herrno = saved_herrno;
     else if (got_nodata)
-        h_errno = NO_DATA;
+        *herrno = NO_DATA;
     else if (got_servfail)
-        h_errno = TRY_AGAIN;
+        *herrno = TRY_AGAIN;
     return -1;
 }
 
@@ -1818,7 +1823,7 @@
  * removing a trailing dot from name if domain is NULL.
  */
 static int res_querydomainN(const char* name, const char* domain, res_target* target, res_state res,
-                            int* ai_error) {
+                            int* herrno) {
     char nbuf[MAXDNAME];
     const char* longname = nbuf;
     size_t n, d;
@@ -1837,7 +1842,7 @@
          */
         n = strlen(name);
         if (n + 1 > sizeof(nbuf)) {
-            h_errno = NO_RECOVERY;
+            *herrno = NO_RECOVERY;
             return -1;
         }
         if (n > 0 && name[--n] == '.') {
@@ -1849,10 +1854,10 @@
         n = strlen(name);
         d = strlen(domain);
         if (n + 1 + d + 1 > sizeof(nbuf)) {
-            h_errno = NO_RECOVERY;
+            *herrno = NO_RECOVERY;
             return -1;
         }
         snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);
     }
-    return res_queryN(longname, target, res, ai_error);
+    return res_queryN(longname, target, res, herrno);
 }
diff --git a/resolv/gethnamaddr.cpp b/resolv/gethnamaddr.cpp
index 53abe12..b8609eb 100644
--- a/resolv/gethnamaddr.cpp
+++ b/resolv/gethnamaddr.cpp
@@ -135,20 +135,20 @@
 static void pad_v4v6_hostent(struct hostent* hp, char** bpp, char* ep);
 static void addrsort(char**, int, res_state);
 
-static bool _dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
-                             const android_net_context* netcontext, getnamaddr* info);
+static int _dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
+                            const android_net_context* netcontext, getnamaddr* info);
 static int _dns_gethtbyname(const char* name, int af, getnamaddr* info);
 
 static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
-                                  size_t hbuflen, int* errorp,
-                                  const android_net_context* netcontext);
+                                  size_t hbuflen, const android_net_context* netcontext);
 static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp,
-                                       char* buf, size_t buflen, int* he);
-static struct hostent* android_gethostbyaddrfornetcontext_proxy_internal(
-        const void*, socklen_t, int, struct hostent*, char*, size_t, int*,
-        const struct android_net_context*);
-static struct hostent* android_gethostbyaddrfornetcontext_proxy(
-        const void* addr, socklen_t len, int af, const struct android_net_context* netcontext);
+                                       char* buf, size_t buflen);
+static int android_gethostbyaddrfornetcontext_proxy_internal(const void*, socklen_t, int,
+                                                             struct hostent*, char*, size_t,
+                                                             const struct android_net_context*);
+static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
+                                                    const struct android_net_context* netcontext,
+                                                    hostent** hp);
 
 #ifdef DEBUG
 static void debugprintf(const char* msg, res_state res, ...) {
@@ -470,7 +470,7 @@
 }
 
 static int gethostbyname_internal_real(const char* name, int af, res_state res, hostent* hp,
-                                       char* buf, size_t buflen, int* he) {
+                                       char* buf, size_t buflen) {
     getnamaddr info;
     size_t size;
 
@@ -484,7 +484,6 @@
             size = NS_IN6ADDRSZ;
             break;
         default:
-            *he = NETDB_INTERNAL;
             errno = EAFNOSUPPORT;
             return EAI_FAMILY;
     }
@@ -526,24 +525,19 @@
         }
     }
 
-    *he = NETDB_INTERNAL;
     info.hp = hp;
     info.buf = buf;
     info.buflen = buflen;
-    info.he = he;
-    if (!_hf_gethtbyname2(name, af, &info)) {
+    if (_hf_gethtbyname2(name, af, &info)) {
         int error = _dns_gethtbyname(name, af, &info);
         if (error != 0) {
             return error;
         }
     }
-    *he = NETDB_SUCCESS;
     return 0;
 nospc:
-    *he = NETDB_INTERNAL;
     errno = ENOSPC;
-    // Bad arguments
-    return EAI_FAIL;
+    return EAI_MEMORY;
 fake:
     HENT_ARRAY(hp->h_addr_list, 1, buf, buflen);
     HENT_ARRAY(hp->h_aliases, 0, buf, buflen);
@@ -552,7 +546,6 @@
     if (size > buflen) goto nospc;
 
     if (inet_pton(af, name, buf) <= 0) {
-        *he = HOST_NOT_FOUND;
         return EAI_NODATA;
     }
     hp->h_addr_list[0] = buf;
@@ -561,21 +554,19 @@
     buflen -= size;
     HENT_SCOPY(hp->h_name, name, buf, buflen);
     if (res->options & RES_USE_INET6) map_v4v6_hostent(hp, &buf, buf + buflen);
-    *he = NETDB_SUCCESS;
     return 0;
 }
 
 // very similar in proxy-ness to android_getaddrinfo_proxy
 static int gethostbyname_internal(const char* name, int af, res_state res, hostent* hp, char* hbuf,
-                                  size_t hbuflen, int* errorp,
-                                  const android_net_context* netcontext) {
+                                  size_t hbuflen, const android_net_context* netcontext) {
     res_setnetcontext(res, netcontext);
-    return gethostbyname_internal_real(name, af, res, hp, hbuf, hbuflen, errorp);
+    return gethostbyname_internal_real(name, af, res, hp, hbuf, hbuflen);
 }
 
-static struct hostent* android_gethostbyaddrfornetcontext_real(
-        const void* addr, socklen_t len, int af, struct hostent* hp, char* buf, size_t buflen,
-        int* he, const struct android_net_context* netcontext) {
+static int android_gethostbyaddrfornetcontext_real(const void* addr, socklen_t len, int af,
+                                                   struct hostent* hp, char* buf, size_t buflen,
+                                                   const struct android_net_context* netcontext) {
     const u_char* uaddr = (const u_char*) addr;
     socklen_t size;
     struct getnamaddr info;
@@ -585,8 +576,7 @@
     if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
         (IN6_IS_ADDR_LINKLOCAL((const struct in6_addr*) addr) ||
          IN6_IS_ADDR_SITELOCAL((const struct in6_addr*) addr))) {
-        *he = HOST_NOT_FOUND;
-        return NULL;
+        return EAI_NODATA;
     }
     if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
         (IN6_IS_ADDR_V4MAPPED((const struct in6_addr*) addr) ||
@@ -606,33 +596,29 @@
             break;
         default:
             errno = EAFNOSUPPORT;
-            *he = NETDB_INTERNAL;
-            return NULL;
+            return EAI_FAMILY;
     }
     if (size != len) {
         errno = EINVAL;
-        *he = NETDB_INTERNAL;
-        return NULL;
+        // TODO: Consider to remap error code without relying on errno.
+        return EAI_SYSTEM;
     }
     info.hp = hp;
     info.buf = buf;
     info.buflen = buflen;
-    info.he = he;
-    *he = NETDB_INTERNAL;
-    if (!_hf_gethtbyaddr(uaddr, len, af, &info)) {
-        if (!_dns_gethtbyaddr(uaddr, len, af, netcontext, &info)) {
-            return NULL;
+    if (_hf_gethtbyaddr(uaddr, len, af, &info)) {
+        int error = _dns_gethtbyaddr(uaddr, len, af, netcontext, &info);
+        if (error != 0) {
+            return error;
         }
     }
-    *he = NETDB_SUCCESS;
-    return hp;
+    return 0;
 }
 
-static struct hostent* android_gethostbyaddrfornetcontext_proxy_internal(
+static int android_gethostbyaddrfornetcontext_proxy_internal(
         const void* addr, socklen_t len, int af, struct hostent* hp, char* hbuf, size_t hbuflen,
-        int* he, const struct android_net_context* netcontext) {
-    return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, he,
-                                                   netcontext);
+        const struct android_net_context* netcontext) {
+    return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext);
 }
 
 struct hostent* netbsd_gethostent_r(FILE* hf, struct hostent* hent, char* buf, size_t buflen,
@@ -864,7 +850,6 @@
     }
     querybuf* buf = (querybuf*) malloc(sizeof(querybuf));
     if (buf == NULL) {
-        *info->he = NETDB_INTERNAL;
         return EAI_MEMORY;
     }
     res = res_get_state();
@@ -873,30 +858,24 @@
         return EAI_MEMORY;
     }
 
-    int ai_error = EAI_NODATA;
-    n = res_nsearch(res, name, C_IN, type, buf->buf, (int) sizeof(buf->buf), &ai_error);
+    int herrno = NETDB_INTERNAL;
+    n = res_nsearch(res, name, C_IN, type, buf->buf, (int) sizeof(buf->buf), &herrno);
     if (n < 0) {
         free(buf);
         debugprintf("res_nsearch failed (%d)\n", res, n);
-
-        // If server responds empty answer with rcode NOERROR, adjust the error so netd will
-        // get the nulltpr hp.
-        // TODO: Adjust the error closed to res_nsend instead of here after h_errno is removed.
-        if (ai_error == 0) {
-            return herrnoToAiError(h_errno);
-        }
-        return ai_error;
+        // Pass herrno to catch more detailed errors rather than EAI_NODATA.
+        return herrnoToAiErrno(herrno);
     }
-    hp = getanswer(buf, n, name, type, res, info->hp, info->buf, info->buflen, info->he);
+    hp = getanswer(buf, n, name, type, res, info->hp, info->buf, info->buflen, &herrno);
     free(buf);
     if (hp == NULL) {
-        return herrnoToAiError(h_errno);
+        return herrnoToAiErrno(herrno);
     }
     return 0;
 }
 
-static bool _dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
-                             const android_net_context* netcontext, getnamaddr* info) {
+static int _dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
+                            const android_net_context* netcontext, getnamaddr* info) {
     char qbuf[MAXDNAME + 1], *qp, *ep;
     int n;
     struct hostent* hp;
@@ -921,41 +900,40 @@
                 if (advance > 0 && qp + advance < ep)
                     qp += advance;
                 else {
-                    *info->he = NETDB_INTERNAL;
-                    return false;
+                    // TODO: Consider to remap error code without relying on errno.
+                    return EAI_SYSTEM;
                 }
             }
             if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
-                *info->he = NETDB_INTERNAL;
-                return false;
+                // TODO: Consider to remap error code without relying on errno.
+                return EAI_SYSTEM;
             }
             break;
         default:
-            return false;
+            return EAI_FAMILY;
     }
 
     querybuf* buf = (querybuf*) malloc(sizeof(querybuf));
     if (buf == NULL) {
-        *info->he = NETDB_INTERNAL;
-        return false;
+        return EAI_MEMORY;
     }
     res = res_get_state();
     if (res == NULL) {
         free(buf);
-        return false;
+        return EAI_MEMORY;
     }
     res_setnetcontext(res, netcontext);
-    int ai_error = 0;
-    n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int) sizeof(buf->buf), &ai_error);
+    int herrno = NETDB_INTERNAL;
+    n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int) sizeof(buf->buf), &herrno);
     if (n < 0) {
         free(buf);
         debugprintf("res_nquery failed (%d)\n", res, n);
-        return false;
+        return herrnoToAiErrno(herrno);
     }
-    hp = getanswer(buf, n, qbuf, T_PTR, res, info->hp, info->buf, info->buflen, info->he);
+    hp = getanswer(buf, n, qbuf, T_PTR, res, info->hp, info->buf, info->buflen, &herrno);
     free(buf);
     if (hp == NULL) {
-        return false;
+        return herrnoToAiErrno(herrno);
     }
 
     char* bf = (char*) (hp->h_addr_list + 2);
@@ -978,13 +956,11 @@
         memcpy(bf + NS_INADDRSZ, NAT64_PAD, sizeof(NAT64_PAD));
     }
 
-    *info->he = NETDB_SUCCESS;
-    return true;
+    return 0;
 
 nospc:
     errno = ENOSPC;
-    *info->he = NETDB_INTERNAL;
-    return false;
+    return EAI_MEMORY;
 }
 
 /*
@@ -998,45 +974,45 @@
     if (res == NULL) return EAI_MEMORY;
     res_static* rs = res_get_static();  // For thread-safety.
     error = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf),
-                                   &h_errno, netcontext);
+                                   netcontext);
     if (error == 0) {
         *hp = &rs->host;
     }
     return error;
 }
 
-struct hostent* android_gethostbyaddrfornetcontext(const void* addr, socklen_t len, int af,
-                                                   const struct android_net_context* netcontext) {
-    return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext);
+int android_gethostbyaddrfornetcontext(const void* addr, socklen_t len, int af,
+                                       const struct android_net_context* netcontext, hostent** hp) {
+    return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp);
 }
 
-static struct hostent* android_gethostbyaddrfornetcontext_proxy(
-        const void* addr, socklen_t len, int af, const struct android_net_context* netcontext) {
+static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,
+                                                    const struct android_net_context* netcontext,
+                                                    hostent** hp) {
     struct res_static* rs = res_get_static();  // For thread-safety.
-    return android_gethostbyaddrfornetcontext_proxy_internal(
-            addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), &h_errno, netcontext);
+    int error = android_gethostbyaddrfornetcontext_proxy_internal(
+            addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), netcontext);
+    if (error == 0) *hp = &rs->host;
+    return error;
 }
 
-int herrnoToAiError(int herror) {
-    switch (herror) {
-        case HOST_NOT_FOUND:
+int herrnoToAiErrno(int herrno) {
+    switch (herrno) {
+        // extended h_errno
+        case NETD_RESOLV_H_ERRNO_EXT_TIMEOUT:
+            return NETD_RESOLV_TIMEOUT;
+        // legacy h_errno
+        case NETDB_SUCCESS:
+            return 0;
+        case HOST_NOT_FOUND:  // TODO: Perhaps convert HOST_NOT_FOUND to EAI_NONAME instead
+        case NO_DATA:         // NO_ADDRESS
             return EAI_NODATA;
         case TRY_AGAIN:
             return EAI_AGAIN;
+        case NETDB_INTERNAL:
+            return EAI_SYSTEM;  // see errno for detail
+        case NO_RECOVERY:
         default:
             return EAI_FAIL;
     }
-}
-
-int rcodeToAiError(int rcode) {
-    // Catch the two cases (success, timeout). For other cases, just set it EAI_NODATA
-    // as EAI_NODATA is returned in dns_getaddrinfo() when res_searchN() returns -1.
-    switch (rcode) {
-        case NOERROR:
-            return 0;
-        case RCODE_TIMEOUT:
-            return NETD_RESOLV_TIMEOUT;
-        default:
-            return EAI_NODATA;
-    }
-}
+}
\ No newline at end of file
diff --git a/resolv/hostent.h b/resolv/hostent.h
index 892ed88..4f6a33b 100644
--- a/resolv/hostent.h
+++ b/resolv/hostent.h
@@ -38,12 +38,11 @@
     struct hostent* hp;
     char* buf;
     size_t buflen;
-    int* he;
 };
 
 // /etc/hosts lookup
-bool _hf_gethtbyaddr(const unsigned char* uaddr, int len, int af, getnamaddr* info);
-hostent* _hf_gethtbyname2(const char* name, int af, getnamaddr* info);
+int _hf_gethtbyaddr(const unsigned char* uaddr, int len, int af, getnamaddr* info);
+int _hf_gethtbyname2(const char* name, int af, getnamaddr* info);
 hostent* netbsd_gethostent_r(FILE*, struct hostent*, char*, size_t, int*);
 
 // Reserved padding for remapping IPv4 address to NAT64 synthesis IPv6 address
diff --git a/resolv/include/netd_resolv/resolv.h b/resolv/include/netd_resolv/resolv.h
index f50744c..e30f31a 100644
--- a/resolv/include/netd_resolv/resolv.h
+++ b/resolv/include/netd_resolv/resolv.h
@@ -91,7 +91,7 @@
 
 struct ExternalPrivateDnsStatus {
     PrivateDnsMode mode;
-    unsigned numServers;
+    int numServers;
     struct PrivateDnsInfo {
         sockaddr_storage ss;
         const char* hostname;
@@ -102,8 +102,8 @@
 typedef void (*private_dns_validated_callback)(unsigned netid, const char* server,
                                                const char* hostname, bool success);
 
-LIBNETD_RESOLV_PUBLIC hostent* android_gethostbyaddrfornetcontext(const void*, socklen_t, int,
-                                                                  const android_net_context*);
+LIBNETD_RESOLV_PUBLIC int android_gethostbyaddrfornetcontext(const void*, socklen_t, int,
+                                                             const android_net_context*, hostent**);
 LIBNETD_RESOLV_PUBLIC int android_gethostbynamefornetcontext(const char*, int,
                                                              const android_net_context*, hostent**);
 LIBNETD_RESOLV_PUBLIC int android_getaddrinfofornetcontext(const char*, const char*,
@@ -119,15 +119,14 @@
 
 // Set name servers for a network
 LIBNETD_RESOLV_PUBLIC int resolv_set_nameservers_for_net(unsigned netid, const char** servers,
-                                                         unsigned numservers, const char* domains,
+                                                         int numservers, const char* domains,
                                                          const __res_params* params);
 
 LIBNETD_RESOLV_PUBLIC int resolv_set_private_dns_for_net(unsigned netid, uint32_t mark,
-                                                         const char** servers,
-                                                         const unsigned numServers,
+                                                         const char** servers, int numServers,
                                                          const char* tlsName,
                                                          const uint8_t** fingerprints,
-                                                         const unsigned numFingerprints);
+                                                         int numFingerprints);
 
 LIBNETD_RESOLV_PUBLIC void resolv_delete_private_dns_for_net(unsigned netid);
 
diff --git a/resolv/include/netd_resolv/resolv_stub.h b/resolv/include/netd_resolv/resolv_stub.h
index a2462bd..fddabcf 100644
--- a/resolv/include/netd_resolv/resolv_stub.h
+++ b/resolv/include/netd_resolv/resolv_stub.h
@@ -37,8 +37,8 @@
     int (*android_getaddrinfofornetcontext)(const char*, const char*, const addrinfo*,
                                             const android_net_context*, addrinfo**);
 
-    hostent* (*android_gethostbyaddrfornetcontext)(const void*, socklen_t, int,
-                                                   const android_net_context*);
+    int (*android_gethostbyaddrfornetcontext)(const void*, socklen_t, int,
+                                              const android_net_context*, hostent**);
 
     int (*android_gethostbynamefornetcontext)(const char*, int, const android_net_context*,
                                               hostent**);
diff --git a/resolv/libnetd_resolv_test.cpp b/resolv/libnetd_resolv_test.cpp
index 683dd21..2bd27fd 100644
--- a/resolv/libnetd_resolv_test.cpp
+++ b/resolv/libnetd_resolv_test.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 
 #include <android-base/stringprintf.h>
+#include <arpa/inet.h>
 #include <netdb.h>
 
 #include "dns_responder.h"
@@ -45,6 +46,15 @@
         resolv_set_nameservers_for_net(TEST_NETID, nullptr, 0, "", nullptr);
     }
 
+    static std::string ToString(const hostent* he) {
+        if (he == nullptr) return "<null>";
+        char buffer[INET6_ADDRSTRLEN];
+        if (!inet_ntop(he->h_addrtype, he->h_addr_list[0], buffer, sizeof(buffer))) {
+            return "<invalid>";
+        }
+        return buffer;
+    }
+
     static std::string ToString(const addrinfo* ai) {
         if (!ai) return "<null>";
         for (const auto* aip = ai; aip != nullptr; aip = aip->ai_next) {
@@ -76,27 +86,27 @@
             .max_samples = 8,
             .base_timeout_msec = 100,
     };
-};
-
-class GetAddrInfoForNetContextTest : public TestBase {};
-
-TEST_F(GetAddrInfoForNetContextTest, InvalidParameters) {
-    struct addrinfo* result = nullptr;
-    const android_net_context netcontext = {
-            .app_netid = NETID_UNSET,
+    const android_net_context mNetcontext = {
+            .app_netid = TEST_NETID,
             .app_mark = MARK_UNSET,
-            .dns_netid = NETID_UNSET,
+            .dns_netid = TEST_NETID,
             .dns_mark = MARK_UNSET,
             .uid = NET_CONTEXT_INVALID_UID,
     };
+};
 
+class GetAddrInfoForNetContextTest : public TestBase {};
+class GetHostByNameForNetContextTest : public TestBase {};
+
+TEST_F(GetAddrInfoForNetContextTest, InvalidParameters) {
     // Both null "netcontext" and null "res" of android_getaddrinfofornetcontext() are not tested
     // here because they are checked by assert() without returning any error number.
 
     // Invalid hostname and servname.
     // Both hostname and servname are null pointers. Expect error number EAI_NONAME.
+    struct addrinfo* result = nullptr;
     int rv = android_getaddrinfofornetcontext(nullptr /*hostname*/, nullptr /*servname*/,
-                                              nullptr /*hints*/, &netcontext, &result);
+                                              nullptr /*hints*/, &mNetcontext, &result);
     EXPECT_EQ(EAI_NONAME, rv);
     if (result) {
         freeaddrinfo(result);
@@ -151,7 +161,7 @@
         };
 
         rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
-                                              &netcontext, &result);
+                                              &mNetcontext, &result);
         EXPECT_EQ(config.expected_errorno, rv);
 
         if (result) {
@@ -162,14 +172,6 @@
 }
 
 TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_Family) {
-    const android_net_context netcontext = {
-            .app_netid = NETID_UNSET,
-            .app_mark = MARK_UNSET,
-            .dns_netid = NETID_UNSET,
-            .dns_mark = MARK_UNSET,
-            .uid = NET_CONTEXT_INVALID_UID,
-    };
-
     for (int family = 0; family < AF_MAX; ++family) {
         if (family == AF_UNSPEC || family == AF_INET || family == AF_INET6) {
             continue;  // skip supported family
@@ -182,7 +184,7 @@
         };
 
         int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
-                                                  &netcontext, &result);
+                                                  &mNetcontext, &result);
         EXPECT_EQ(EAI_FAMILY, rv);
 
         if (result) freeaddrinfo(result);
@@ -190,14 +192,6 @@
 }
 
 TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_MeaningfulSocktypeAndProtocolCombination) {
-    const android_net_context netcontext = {
-            .app_netid = NETID_UNSET,
-            .app_mark = MARK_UNSET,
-            .dns_netid = NETID_UNSET,
-            .dns_mark = MARK_UNSET,
-            .uid = NET_CONTEXT_INVALID_UID,
-    };
-
     static const int families[] = {PF_INET, PF_INET6, PF_UNSPEC};
     // Skip to test socket type SOCK_RAW in meaningful combination (explore_options[]) of
     // system\netd\resolv\getaddrinfo.cpp. In explore_options[], the socket type SOCK_RAW always
@@ -237,7 +231,7 @@
                 };
 
                 int rv = android_getaddrinfofornetcontext("localhost", nullptr /*servname*/, &hints,
-                                                          &netcontext, &result);
+                                                          &mNetcontext, &result);
                 EXPECT_EQ(EAI_BADHINTS, rv);
 
                 if (result) freeaddrinfo(result);
@@ -247,13 +241,6 @@
 }
 
 TEST_F(GetAddrInfoForNetContextTest, InvalidParameters_PortNameAndNumber) {
-    const android_net_context netcontext = {
-            .app_netid = NETID_UNSET,
-            .app_mark = MARK_UNSET,
-            .dns_netid = NETID_UNSET,
-            .dns_mark = MARK_UNSET,
-            .uid = NET_CONTEXT_INVALID_UID,
-    };
     constexpr char http_portno[] = "80";
     constexpr char invalid_portno[] = "65536";  // out of valid port range from 0 to 65535
     constexpr char http_portname[] = "http";
@@ -326,22 +313,18 @@
         };
 
         struct addrinfo* result = nullptr;
-        int rv = android_getaddrinfofornetcontext("localhost", config.servname, &hints, &netcontext,
-                                                  &result);
+        int rv = android_getaddrinfofornetcontext("localhost", config.servname, &hints,
+                                                  &mNetcontext, &result);
         EXPECT_EQ(config.expected_errorno, rv);
 
         if (result) freeaddrinfo(result);
     }
 }
 
-// Blocked by aosp/816674 which causes wrong error code EAI_FAIL (4) but EAI_NODATA (7).
-// TODO: fix aosp/816674 and add testcases AlphabeticalHostname_NoData back.
-/*
 TEST_F(GetAddrInfoForNetContextTest, AlphabeticalHostname_NoData) {
     constexpr char listen_addr[] = "127.0.0.3";
     constexpr char listen_srv[] = "53";
     constexpr char v4_host_name[] = "v4only.example.com.";
-    constexpr char nonexistent_host_name[] = "nonexistent.example.com.";
     test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
     dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
     ASSERT_TRUE(dns.startServer());
@@ -351,37 +334,16 @@
                                                 mDefaultSearchDomains, &mDefaultParams_Binder));
     dns.clearQueries();
 
-    struct addrinfo* result = nullptr;
-    const android_net_context netcontext = {
-            .app_netid = TEST_NETID,
-            .app_mark = MARK_UNSET,
-            .dns_netid = TEST_NETID,
-            .dns_mark = MARK_UNSET,
-            .uid = NET_CONTEXT_INVALID_UID,
-    };
-
-    // Query nonexistent hostname.
-    int rv =
-            android_getaddrinfofornetcontext("nonexistent", nullptr, nullptr, &netcontext, &result);
-    EXPECT_LE(1U, GetNumQueries(dns, nonexistent_host_name));
-    EXPECT_TRUE(result == nullptr);
-    EXPECT_EQ(EAI_NODATA, rv);
-
-    if (result) {
-        freeaddrinfo(result);
-        result = nullptr;
-    }
-
     // Want AAAA answer but DNS server has A answer only.
+    struct addrinfo* result = nullptr;
     const addrinfo hints = {.ai_family = AF_INET6};
-    rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &netcontext, &result);
+    int rv = android_getaddrinfofornetcontext("v4only", nullptr, &hints, &mNetcontext, &result);
     EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
     EXPECT_TRUE(result == nullptr);
     EXPECT_EQ(EAI_NODATA, rv);
 
     if (result) freeaddrinfo(result);
 }
-*/
 
 TEST_F(GetAddrInfoForNetContextTest, AlphabeticalHostname) {
     constexpr char listen_addr[] = "127.0.0.3";
@@ -399,14 +361,6 @@
                                                 sizeof(servers) / sizeof(servers[0]),
                                                 mDefaultSearchDomains, &mDefaultParams_Binder));
 
-    const android_net_context netcontext = {
-            .app_netid = TEST_NETID,
-            .app_mark = MARK_UNSET,
-            .dns_netid = TEST_NETID,
-            .dns_mark = MARK_UNSET,
-            .uid = NET_CONTEXT_INVALID_UID,
-    };
-
     static const struct TestConfig {
         int ai_family;
         const std::string expected_addr;
@@ -421,7 +375,8 @@
 
         struct addrinfo* result = nullptr;
         const struct addrinfo hints = {.ai_family = config.ai_family};
-        int rv = android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &netcontext, &result);
+        int rv =
+                android_getaddrinfofornetcontext("sawadee", nullptr, &hints, &mNetcontext, &result);
         EXPECT_EQ(0, rv);
         EXPECT_TRUE(result != nullptr);
         EXPECT_EQ(1U, GetNumQueries(dns, host_name));
@@ -436,28 +391,27 @@
     constexpr char listen_srv[] = "53";
     constexpr char host_name[] = "hello.example.com.";
 
-    const android_net_context netcontext = {
-            .app_netid = TEST_NETID,
-            .app_mark = MARK_UNSET,
-            .dns_netid = TEST_NETID,
-            .dns_mark = MARK_UNSET,
-            .uid = NET_CONTEXT_INVALID_UID,
+    static const struct TestConfig {
+        ns_rcode rcode;
+        int expected_errorno;  // expected result
+
+        // Only test failure RCODE [1..5] in RFC 1035 section 4.1.1 and skip successful RCODE 0
+        // which means no error.
+    } testConfigs[]{
+            // clang-format off
+            {ns_rcode::ns_r_formerr, EAI_FAIL},
+            {ns_rcode::ns_r_servfail, EAI_AGAIN},
+            {ns_rcode::ns_r_nxdomain, EAI_NODATA},
+            {ns_rcode::ns_r_notimpl, EAI_FAIL},
+            {ns_rcode::ns_r_refused, EAI_FAIL},
+            // clang-format on
     };
 
-    // Only test failure RCODE [1..5] in RFC 1035 section 4.1.1 and skip successful RCODE 0 which
-    // means no error.
-    // clang-format off
-    static const ns_rcode rcodes[] = {ns_rcode::ns_r_formerr,
-                                      ns_rcode::ns_r_servfail,
-                                      ns_rcode::ns_r_nxdomain,
-                                      ns_rcode::ns_r_notimpl,
-                                      ns_rcode::ns_r_refused};
-    // clang-format on
+    for (const auto& config : testConfigs) {
+        SCOPED_TRACE(StringPrintf("rcode: %d", config.rcode));
 
-    for (const auto& rcode : rcodes) {
-        SCOPED_TRACE(StringPrintf("rcode: %d", rcode));
-
-        test::DNSResponder dns(listen_addr, listen_srv, 250, rcode /*response specific rcode*/);
+        test::DNSResponder dns(listen_addr, listen_srv, 250,
+                               config.rcode /*response specific rcode*/);
         dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
         dns.setResponseProbability(0.0);  // always ignore requests and response preset rcode
         ASSERT_TRUE(dns.startServer());
@@ -468,8 +422,9 @@
 
         struct addrinfo* result = nullptr;
         const struct addrinfo hints = {.ai_family = AF_UNSPEC};
-        int rv = android_getaddrinfofornetcontext(host_name, nullptr, &hints, &netcontext, &result);
-        EXPECT_EQ(EAI_NODATA, rv);
+        int rv =
+                android_getaddrinfofornetcontext(host_name, nullptr, &hints, &mNetcontext, &result);
+        EXPECT_EQ(config.expected_errorno, rv);
 
         if (result) freeaddrinfo(result);
     }
@@ -490,26 +445,140 @@
                                                 mDefaultSearchDomains, &mDefaultParams_Binder));
 
     struct addrinfo* result = nullptr;
-    const android_net_context netcontext = {
-            .app_netid = TEST_NETID,
-            .app_mark = MARK_UNSET,
-            .dns_netid = TEST_NETID,
-            .dns_mark = MARK_UNSET,
-            .uid = NET_CONTEXT_INVALID_UID,
-    };
-
     const struct addrinfo hints = {.ai_family = AF_UNSPEC};
-    int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &netcontext, &result);
+    int rv = android_getaddrinfofornetcontext("hello", nullptr, &hints, &mNetcontext, &result);
     EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
 
     if (result) freeaddrinfo(result);
 }
 
-// Local host file function (files_getaddrinfo) is not tested because it only returns a boolean
-// (success or failure) without any error number.
+TEST_F(GetHostByNameForNetContextTest, AlphabeticalHostname) {
+    constexpr char listen_addr[] = "127.0.0.3";
+    constexpr char listen_srv[] = "53";
+    constexpr char host_name[] = "jiababuei.example.com.";
+    constexpr char v4addr[] = "1.2.3.4";
+    constexpr char v6addr[] = "::1.2.3.4";
 
-// TODO: Add test NULL hostname, or numeric hostname for android_getaddrinfofornetcontext
-// TODO: Add test for android_gethostbyaddrfornetcontext and android_gethostbynamefornetcontext.
+    test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+    dns.addMapping(host_name, ns_type::ns_t_a, v4addr);
+    dns.addMapping(host_name, ns_type::ns_t_aaaa, v6addr);
+    ASSERT_TRUE(dns.startServer());
+    const char* servers[] = {listen_addr};
+    ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+                                                sizeof(servers) / sizeof(servers[0]),
+                                                mDefaultSearchDomains, &mDefaultParams_Binder));
+
+    static const struct TestConfig {
+        int ai_family;
+        const std::string expected_addr;
+    } testConfigs[]{
+            {AF_INET, v4addr},
+            {AF_INET6, v6addr},
+    };
+
+    for (const auto& config : testConfigs) {
+        SCOPED_TRACE(StringPrintf("family: %d", config.ai_family));
+        dns.clearQueries();
+
+        struct hostent* hp = nullptr;
+        int rv = android_gethostbynamefornetcontext("jiababuei", config.ai_family, &mNetcontext,
+                                                    &hp);
+        EXPECT_EQ(0, rv);
+        EXPECT_TRUE(hp != nullptr);
+        EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+        EXPECT_EQ(config.expected_addr, ToString(hp));
+    }
+}
+
+TEST_F(GetHostByNameForNetContextTest, NoData) {
+    constexpr char listen_addr[] = "127.0.0.3";
+    constexpr char listen_srv[] = "53";
+    constexpr char v4_host_name[] = "v4only.example.com.";
+    test::DNSResponder dns(listen_addr, listen_srv, 250, ns_rcode::ns_r_servfail);
+    dns.addMapping(v4_host_name, ns_type::ns_t_a, "1.2.3.3");
+    ASSERT_TRUE(dns.startServer());
+    const char* servers[] = {listen_addr};
+    ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+                                                sizeof(servers) / sizeof(servers[0]),
+                                                mDefaultSearchDomains, &mDefaultParams_Binder));
+    dns.clearQueries();
+
+    // Want AAAA answer but DNS server has A answer only.
+    struct hostent* hp = nullptr;
+    int rv = android_gethostbynamefornetcontext("v4only", AF_INET6, &mNetcontext, &hp);
+    EXPECT_LE(1U, GetNumQueries(dns, v4_host_name));
+    EXPECT_TRUE(hp == nullptr);
+    EXPECT_EQ(EAI_NODATA, rv);
+}
+
+TEST_F(GetHostByNameForNetContextTest, ServerResponseError) {
+    constexpr char listen_addr[] = "127.0.0.3";
+    constexpr char listen_srv[] = "53";
+    constexpr char host_name[] = "hello.example.com.";
+
+    static const struct TestConfig {
+        ns_rcode rcode;
+        int expected_errorno;  // expected result
+
+        // Only test failure RCODE [1..5] in RFC 1035 section 4.1.1 and skip successful RCODE 0
+        // which means no error. Note that the return error codes aren't mapped by rcode in the
+        // test case SERVFAIL, NOTIMP and REFUSED. See the comment of res_nsend()
+        // in system\netd\resolv\res_query.cpp for more detail.
+    } testConfigs[]{
+            // clang-format off
+            {ns_rcode::ns_r_formerr, EAI_FAIL},
+            {ns_rcode::ns_r_servfail, EAI_AGAIN},  // Not mapped by rcode.
+            {ns_rcode::ns_r_nxdomain, EAI_NODATA},
+            {ns_rcode::ns_r_notimpl, EAI_AGAIN},  // Not mapped by rcode.
+            {ns_rcode::ns_r_refused, EAI_AGAIN},  // Not mapped by rcode.
+            // clang-format on
+    };
+
+    for (const auto& config : testConfigs) {
+        SCOPED_TRACE(StringPrintf("rcode: %d", config.rcode));
+
+        test::DNSResponder dns(listen_addr, listen_srv, 250,
+                               config.rcode /*response specific rcode*/);
+        dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+        dns.setResponseProbability(0.0);  // always ignore requests and response preset rcode
+        ASSERT_TRUE(dns.startServer());
+        const char* servers[] = {listen_addr};
+        ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+                                                    sizeof(servers) / sizeof(servers[0]),
+                                                    mDefaultSearchDomains, &mDefaultParams_Binder));
+
+        struct hostent* hp = nullptr;
+        int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp);
+        EXPECT_TRUE(hp == nullptr);
+        EXPECT_EQ(config.expected_errorno, rv);
+    }
+}
+
+// TODO: Add private DNS server timeout test.
+TEST_F(GetHostByNameForNetContextTest, ServerTimeout) {
+    constexpr char listen_addr[] = "127.0.0.3";
+    constexpr char listen_srv[] = "53";
+    constexpr char host_name[] = "hello.example.com.";
+    test::DNSResponder dns(listen_addr, listen_srv, 250, static_cast<ns_rcode>(-1) /*no response*/);
+    dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+    dns.setResponseProbability(0.0);  // always ignore requests and don't response
+    ASSERT_TRUE(dns.startServer());
+    const char* servers[] = {listen_addr};
+    ASSERT_EQ(0, resolv_set_nameservers_for_net(TEST_NETID, servers,
+                                                sizeof(servers) / sizeof(servers[0]),
+                                                mDefaultSearchDomains, &mDefaultParams_Binder));
+
+    struct hostent* hp = nullptr;
+    int rv = android_gethostbynamefornetcontext(host_name, AF_INET, &mNetcontext, &hp);
+    EXPECT_EQ(NETD_RESOLV_TIMEOUT, rv);
+}
+
+// Note that local host file function, files_getaddrinfo(), of android_getaddrinfofornetcontext()
+// is not tested because it only returns a boolean (success or failure) without any error number.
+
+// TODO: Add test NULL hostname, or numeric hostname for android_getaddrinfofornetcontext.
+// TODO: Add test invalid parameters for android_gethostbynamefornetcontext.
+// TODO: Add test for android_gethostbyaddrfornetcontext.
 
 }  // end of namespace net
 }  // end of namespace android
diff --git a/resolv/res_cache.cpp b/resolv/res_cache.cpp
index fd1a324..60a60df 100644
--- a/resolv/res_cache.cpp
+++ b/resolv/res_cache.cpp
@@ -1729,7 +1729,7 @@
     params->base_timeout_msec = 0;  // 0 = legacy algorithm
 }
 
-int resolv_set_nameservers_for_net(unsigned netid, const char** servers, unsigned numservers,
+int resolv_set_nameservers_for_net(unsigned netid, const char** servers, const int numservers,
                                    const char* domains, const __res_params* params) {
     char* cp;
     int* offset;
@@ -1744,13 +1744,13 @@
     // As a side effect this also reduces the time the lock is kept.
     char sbuf[NI_MAXSERV];
     snprintf(sbuf, sizeof(sbuf), "%u", NAMESERVER_PORT);
-    for (unsigned i = 0; i < numservers; i++) {
+    for (int i = 0; i < numservers; i++) {
         // The addrinfo structures allocated here are freed in free_nameservers_locked().
         const addrinfo hints = {
                 .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_flags = AI_NUMERICHOST};
         int rt = getaddrinfo_numeric(servers[i], sbuf, hints, &nsaddrinfo[i]);
         if (rt != 0) {
-            for (unsigned j = 0; j < i; j++) {
+            for (int j = 0; j < i; j++) {
                 freeaddrinfo(nsaddrinfo[j]);
             }
             VLOG << __func__ << ": getaddrinfo_numeric(" << servers[i]
@@ -1778,8 +1778,7 @@
         if (!resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) {
             // free current before adding new
             free_nameservers_locked(cache_info);
-            unsigned i;
-            for (i = 0; i < numservers; i++) {
+            for (int i = 0; i < numservers; i++) {
                 cache_info->nsaddrinfo[i] = nsaddrinfo[i];
                 cache_info->nameservers[i] = strdup(servers[i]);
                 VLOG << __func__ << ": netid = " << netid << ", addr = " << servers[i];
@@ -1804,7 +1803,7 @@
                 res_cache_clear_stats_locked(cache_info);
                 ++cache_info->revision_id;
             }
-            for (unsigned j = 0; j < numservers; j++) {
+            for (int j = 0; j < numservers; j++) {
                 freeaddrinfo(nsaddrinfo[j]);
             }
         }
diff --git a/resolv/res_query.cpp b/resolv/res_query.cpp
index 6faa504..f81b33e 100644
--- a/resolv/res_query.cpp
+++ b/resolv/res_query.cpp
@@ -103,7 +103,7 @@
  * Perform preliminary check of answer, returning success only
  * if no error is indicated and the answer count is nonzero.
  * Return the size of the response on success, -1 on error.
- * Error number is left in H_ERRNO.
+ * Error number is left in *herrno.
  *
  * Caller must parse answer and determine whether it answers the question.
  */
@@ -111,7 +111,8 @@
                int cl, int type,                   // class and type of query
                u_char* answer,                     // buffer to put answer
                int anslen,                         // size of answer buffer
-               int* ai_error)                      // error will be set based on rcode
+               int* herrno)                        // legacy and extended h_errno
+                                                   // NETD_RESOLV_H_ERRNO_EXT_*
 {
     u_char buf[MAXPACKET];
     HEADER* hp = (HEADER*) (void*) answer;
@@ -136,11 +137,10 @@
 #ifdef DEBUG
         if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n");
 #endif
-        RES_SET_H_ERRNO(statp, NO_RECOVERY);
+        *herrno = NO_RECOVERY;
         return n;
     }
     n = res_nsend(statp, buf, n, answer, anslen, &rcode);
-    *ai_error = rcodeToAiError(rcode);
     if (n < 0) {
         /* if the query choked with EDNS0, retry without EDNS0 */
         if ((statp->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0U &&
@@ -152,7 +152,28 @@
 #ifdef DEBUG
         if (statp->options & RES_DEBUG) printf(";; res_query: send error\n");
 #endif
-        RES_SET_H_ERRNO(statp, TRY_AGAIN);
+        // Note that rcodes SERVFAIL, NOTIMP, REFUSED may cause res_nquery() to return a general
+        // error code EAI_AGAIN, but mapping the error code from rcode as res_queryN() does for
+        // getaddrinfo(). Different rcodes trigger different behaviors:
+        //
+        // - SERVFAIL, NOTIMP, REFUSED
+        //   These result in send_dg() returning 0, causing res_nsend() to try the next
+        //   nameserver. After all nameservers failed, res_nsend() returns -ETIMEDOUT, causing
+        //   res_nquery() to return EAI_AGAIN here regardless of the rcode from the DNS response.
+        //
+        // - NXDOMAIN, FORMERR
+        //   These rcodes may cause res_nsend() to return successfully (i.e. the result is a
+        //   positive integer). In this case, res_nquery() returns error number by referring
+        //   the rcode from the DNS response.
+        switch (rcode) {
+            case RCODE_TIMEOUT:  // Not defined in RFC.
+                // DNS metrics monitors DNS query timeout.
+                *herrno = NETD_RESOLV_H_ERRNO_EXT_TIMEOUT;  // extended h_errno.
+                break;
+            default:
+                *herrno = TRY_AGAIN;
+                break;
+        }
         return n;
     }
 
@@ -164,19 +185,19 @@
 #endif
         switch (hp->rcode) {
             case NXDOMAIN:
-                RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
+                *herrno = HOST_NOT_FOUND;
                 break;
             case SERVFAIL:
-                RES_SET_H_ERRNO(statp, TRY_AGAIN);
+                *herrno = TRY_AGAIN;
                 break;
             case NOERROR:
-                RES_SET_H_ERRNO(statp, NO_DATA);
+                *herrno = NO_DATA;
                 break;
             case FORMERR:
             case NOTIMP:
             case REFUSED:
             default:
-                RES_SET_H_ERRNO(statp, NO_RECOVERY);
+                *herrno = NO_RECOVERY;
                 break;
         }
         return -1;
@@ -188,13 +209,14 @@
  * Formulate a normal query, send, and retrieve answer in supplied buffer.
  * Return the size of the response on success, -1 on error.
  * If enabled, implement search rules until answer or unrecoverable failure
- * is detected.  Error code, if any, is left in H_ERRNO.
+ * is detected.  Error code, if any, is left in *herrno.
  */
 int res_nsearch(res_state statp, const char* name, /* domain name */
                 int cl, int type,                  /* class and type of query */
                 u_char* answer,                    /* buffer to put answer */
                 int anslen,                        /* size of answer */
-                int* ai_error)                     /* error will be set based on rcode*/
+                int* herrno)                       /* legacy and extended
+                                                      h_errno NETD_RESOLV_H_ERRNO_EXT_* */
 {
     const char *cp, *const *domain;
     HEADER* hp = (HEADER*) (void*) answer;
@@ -205,7 +227,7 @@
     int searched = 0;
 
     errno = 0;
-    RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); /* True if we never query. */
+    *herrno = HOST_NOT_FOUND; /* True if we never query. */
 
     dots = 0;
     for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
@@ -219,9 +241,9 @@
      */
     saved_herrno = -1;
     if (dots >= statp->ndots || trailing_dot) {
-        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, ai_error);
+        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, herrno);
         if (ret > 0 || trailing_dot) return ret;
-        saved_herrno = statp->res_h_errno;
+        saved_herrno = *herrno;
         tried_as_is++;
     }
 
@@ -250,7 +272,7 @@
             if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0'))
                 root_on_list++;
 
-            ret = res_nquerydomain(statp, name, *domain, cl, type, answer, anslen, ai_error);
+            ret = res_nquerydomain(statp, name, *domain, cl, type, answer, anslen, herrno);
             if (ret > 0) return ret;
 
             /*
@@ -267,11 +289,11 @@
              * fully-qualified.
              */
             if (errno == ECONNREFUSED) {
-                RES_SET_H_ERRNO(statp, TRY_AGAIN);
+                *herrno = TRY_AGAIN;
                 return -1;
             }
 
-            switch (statp->res_h_errno) {
+            switch (*herrno) {
                 case NO_DATA:
                     got_nodata++;
                     break;
@@ -303,7 +325,7 @@
      */
     if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0U) &&
         !(tried_as_is || root_on_list)) {
-        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, ai_error);
+        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, herrno);
         if (ret > 0) return ret;
     }
 
@@ -315,11 +337,11 @@
      * the last DNSRCH we did.
      */
     if (saved_herrno != -1)
-        RES_SET_H_ERRNO(statp, saved_herrno);
+        *herrno = saved_herrno;
     else if (got_nodata)
-        RES_SET_H_ERRNO(statp, NO_DATA);
+        *herrno = NO_DATA;
     else if (got_servfail)
-        RES_SET_H_ERRNO(statp, TRY_AGAIN);
+        *herrno = TRY_AGAIN;
     return -1;
 }
 
@@ -331,7 +353,7 @@
                      int type,       /* class and type of query */
                      u_char* answer, /* buffer to put answer */
                      int anslen,     /* size of answer */
-                     int* ai_error)  /* error will be set based on rcode*/
+                     int* herrno)    /* legacy and extended h_errno NETD_RESOLV_H_ERRNO_EXT_* */
 {
     char nbuf[MAXDNAME];
     const char* longname = nbuf;
@@ -349,7 +371,7 @@
          */
         n = strlen(name);
         if (n >= MAXDNAME) {
-            RES_SET_H_ERRNO(statp, NO_RECOVERY);
+            *herrno = NO_RECOVERY;
             return -1;
         }
         n--;
@@ -362,10 +384,10 @@
         n = strlen(name);
         d = strlen(domain);
         if (n + d + 1 >= MAXDNAME) {
-            RES_SET_H_ERRNO(statp, NO_RECOVERY);
+            *herrno = NO_RECOVERY;
             return -1;
         }
         snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);
     }
-    return res_nquery(statp, longname, cl, type, answer, anslen, ai_error);
+    return res_nquery(statp, longname, cl, type, answer, anslen, herrno);
 }
diff --git a/resolv/res_state.cpp b/resolv/res_state.cpp
index 325bb6e..26febff 100644
--- a/resolv/res_state.cpp
+++ b/resolv/res_state.cpp
@@ -43,7 +43,6 @@
 #include "resolv_private.h"
 
 typedef struct {
-    int _h_errno;
     // TODO: Have one __res_state per network so we don't have to repopulate frequently.
     struct __res_state _nres[1];
     struct res_static _rstatic[1];
@@ -53,7 +52,6 @@
     _res_thread* rt = (_res_thread*) calloc(1, sizeof(*rt));
 
     if (rt) {
-        rt->_h_errno = 0;
         memset(rt->_rstatic, 0, sizeof rt->_rstatic);
     }
     return rt;
@@ -119,13 +117,6 @@
 
 struct __res_state _nres;
 
-int* __get_h_errno(void) {
-    _res_thread* rt = res_thread_get();
-    static int panic = NETDB_INTERNAL;
-
-    return rt ? &rt->_h_errno : &panic;
-}
-
 res_state res_get_state(void) {
     _res_thread* rt = res_thread_get();
 
diff --git a/resolv/resolv_private.h b/resolv/resolv_private.h
index bd48660..18e5886 100644
--- a/resolv/resolv_private.h
+++ b/resolv/resolv_private.h
@@ -71,27 +71,6 @@
 #define MAXHOSTNAMELEN 256
 
 /*
- * This used to be defined in res_query.c, now it's in herror.c.
- * [XXX no it's not.  It's in irs/irs_data.c]
- * It was
- * never extern'd by any *.h file before it was placed here.  For thread
- * aware programs, the last h_errno value set is stored in res->h_errno.
- *
- * XXX:	There doesn't seem to be a good reason for exposing RES_SET_H_ERRNO
- *	(and __h_errno_set) to the public via <resolv.h>.
- * XXX:	__h_errno_set is really part of IRS, not part of the resolver.
- *	If somebody wants to build and use a resolver that doesn't use IRS,
- *	what do they do?  Perhaps something like
- *		#ifdef WANT_IRS
- *		# define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x)
- *		#else
- *		# define RES_SET_H_ERRNO(r,x) (h_errno = (r)->res_h_errno = (x))
- *		#endif
- */
-
-#define RES_SET_H_ERRNO(r, x) (h_errno = (r)->res_h_errno = (x))
-
-/*
  * Global defines and variables for resolver stub.
  */
 #define MAXDFLSRCH 3       /* # default domain levels to try */
@@ -126,7 +105,6 @@
         struct in_addr addr;
         uint32_t mask;
     } sort_list[MAXRESOLVSORT];
-    int res_h_errno;      /* last one set for this context */
     unsigned _mark;       /* If non-0 SET_MARK to _mark on all request sockets */
     int _vcsock;          /* PRIVATE: for res_send VC i/o */
     u_int _flags;         /* PRIVATE: see below */
@@ -233,6 +211,16 @@
 #define RES_PRF_TRUNC 0x00008000
 /*			0x00010000	*/
 
+/*
+ * Error code extending h_errno codes defined in bionic/libc/include/netdb.h.
+ *
+ * This error code, including legacy h_errno, is returned from res_nquery(), res_nsearch(),
+ * res_nquerydomain(), res_queryN(), res_searchN() and res_querydomainN() for DNS metrics.
+ *
+ * TODO: Consider mapping legacy and extended h_errno into a unified resolver error code mapping.
+ */
+#define NETD_RESOLV_H_ERRNO_EXT_TIMEOUT RCODE_TIMEOUT
+
 extern const char* const _res_opcodes[];
 
 /* Things involving an internal (static) resolver context. */
@@ -280,9 +268,6 @@
                         addrinfo** result);
 
 // Helper function for converting h_errno to the error codes visible to netd
-int herrnoToAiError(int herrno);
-
-// Helper function for converting rcode to the error codes visible to netd
-int rcodeToAiError(int rcode);
+int herrnoToAiErrno(int herrno);
 
 #endif  // NETD_RESOLV_PRIVATE_H
diff --git a/resolv/resolver_test.cpp b/resolv/resolver_test.cpp
index d0c0ba5..5c5d833 100644
--- a/resolv/resolver_test.cpp
+++ b/resolv/resolver_test.cpp
@@ -140,7 +140,7 @@
         std::vector<int32_t> stats32;
         auto rv = mNetdSrv->getResolverInfo(TEST_NETID, servers, domains, tlsServers, &params32,
                                             &stats32);
-        if (!rv.isOk() || params32.size() != INetd::RESOLVER_PARAMS_COUNT) {
+        if (!rv.isOk() || params32.size() != static_cast<size_t>(INetd::RESOLVER_PARAMS_COUNT)) {
             return false;
         }
         *params = __res_params {
@@ -456,10 +456,10 @@
         INetd::RESOLVER_PARAMS_MAX_SAMPLES,
         INetd::RESOLVER_PARAMS_BASE_TIMEOUT_MSEC,
     };
-    int size = static_cast<int>(params_offsets.size());
+    const int size = static_cast<int>(params_offsets.size());
     EXPECT_EQ(size, INetd::RESOLVER_PARAMS_COUNT);
     std::sort(params_offsets.begin(), params_offsets.end());
-    for (int i = 0 ; i < size ; ++i) {
+    for (int i = 0; i < size; ++i) {
         EXPECT_EQ(params_offsets[i], i);
     }
 }
@@ -479,8 +479,8 @@
     ASSERT_TRUE(SetResolversForNetwork(servers, domains, mDefaultParams_Binder));
 
     const hostent* result = gethostbyname(mapping.host.c_str());
-    size_t total_queries = std::accumulate(dns.begin(), dns.end(), 0,
-            [this, &mapping](size_t total, auto& d) {
+    const size_t total_queries =
+            std::accumulate(dns.begin(), dns.end(), 0, [this, &mapping](size_t total, auto& d) {
                 return total + GetNumQueriesForType(*d, ns_type::ns_t_a, mapping.entry.c_str());
             });
 
@@ -696,7 +696,7 @@
     // for the next sample_lifetime seconds.
     // TODO: This approach is implementation-dependent, change once metrics reporting is available.
     addrinfo hints = {.ai_family = AF_INET6};
-    for (int i = 0 ; i < sample_count ; ++i) {
+    for (int i = 0; i < sample_count; ++i) {
         std::string domain = StringPrintf("nonexistent%d", i);
         ScopedAddrinfo result = safe_getaddrinfo(domain.c_str(), nullptr, &hints);
     }
@@ -1636,8 +1636,8 @@
     ASSERT_TRUE(SetResolversForNetwork(servers, mDefaultSearchDomains, mDefaultParams_Binder));
     dns.clearQueries();
 
-    int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1);   // Type A       1
-    int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28);  // Type AAAA    28
+    int fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);   // Type A       1
+    int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28, 0);  // Type AAAA    28
     EXPECT_TRUE(fd1 != -1);
     EXPECT_TRUE(fd2 != -1);
 
@@ -1654,8 +1654,8 @@
     EXPECT_EQ(2U, GetNumQueries(dns, host_name));
 
     // Re-query verify cache works
-    fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1);   // Type A       1
-    fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28);  // Type AAAA    28
+    fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);   // Type A       1
+    fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28, 0);  // Type AAAA    28
 
     EXPECT_TRUE(fd1 != -1);
     EXPECT_TRUE(fd2 != -1);
@@ -1698,7 +1698,7 @@
 
     for (auto& td : kTestData) {
         SCOPED_TRACE(td.dname);
-        td.fd = resNetworkQuery(TEST_NETID, td.dname, 1, td.queryType);
+        td.fd = resNetworkQuery(TEST_NETID, td.dname, 1, td.queryType, 0);
         EXPECT_TRUE(td.fd != -1);
     }
 
@@ -1733,17 +1733,17 @@
     {
         std::unique_lock lk(cvMutex);
         // A 1  AAAA 28
-        fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28);
+        fd1 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 28, 0);
         EXPECT_TRUE(fd1 != -1);
         EXPECT_EQ(std::cv_status::no_timeout, cv.wait_for(lk, std::chrono::seconds(1)));
     }
 
     dns.setResponseProbability(0.0);
 
-    int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1);
+    int fd2 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);
     EXPECT_TRUE(fd2 != -1);
 
-    int fd3 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1);
+    int fd3 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);
     EXPECT_TRUE(fd3 != -1);
 
     uint8_t buf[MAXPACKET] = {};
@@ -1760,7 +1760,7 @@
 
     dns.setResponseProbability(1.0);
 
-    int fd4 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1);
+    int fd4 = resNetworkQuery(TEST_NETID, "howdy.example.com", 1, 1, 0);
     EXPECT_TRUE(fd4 != -1);
 
     memset(buf, 0, MAXPACKET);
@@ -2035,9 +2035,6 @@
     EXPECT_EQ(result_str, "1.2.3.4");
 }
 
-// blocked by aosp/816674 which causes wrong error code EAI_FAIL (4) but EAI_NODATA (7).
-// TODO: fix aosp/816674 and add testcases GetAddrInfo_Dns64QuerySpecified back.
-/*
 TEST_F(ResolverTest, GetAddrInfo_Dns64QuerySpecified) {
     constexpr char listen_addr[] = "::1";
     constexpr char listen_srv[] = "53";
@@ -2073,7 +2070,6 @@
     result_str = ToString(result);
     EXPECT_EQ(result_str, "1.2.3.4");
 }
-*/
 
 TEST_F(ResolverTest, GetAddrInfo_Dns64QueryUnspecifiedV6) {
     constexpr char listen_addr[] = "::1";
@@ -2657,11 +2653,6 @@
     EXPECT_EQ(result_str, host_name);
 }
 
-// blocked by aosp/816674 which causes wrong error code EAI_FAIL (4) but EAI_NODATA (7).
-// TODO:
-// 1. fix aosp/816674 and add testcases GetHostByName2_Dns64Synthesize back.
-// 2. Manual test gethostbyname2 synthesis for IPv4 address which comes from host file.
-/*
 TEST_F(ResolverTest, GetHostByName2_Dns64Synthesize) {
     constexpr char dns64_name[] = "ipv4only.arpa.";
     constexpr char host_name[] = "ipv4only.example.com.";
@@ -2686,7 +2677,6 @@
     std::string result_str = ToString(result);
     EXPECT_EQ(result_str, "64:ff9b::102:304");
 }
-*/
 
 TEST_F(ResolverTest, GetHostByName2_DnsQueryWithHavingNat64Prefix) {
     constexpr char dns64_name[] = "ipv4only.arpa.";
diff --git a/resolv/sethostent.cpp b/resolv/sethostent.cpp
index 7c5b905..49cc236 100644
--- a/resolv/sethostent.cpp
+++ b/resolv/sethostent.cpp
@@ -61,7 +61,7 @@
     }
 }
 
-hostent* _hf_gethtbyname2(const char* name, int af, getnamaddr* info) {
+int _hf_gethtbyname2(const char* name, int af, getnamaddr* info) {
     struct hostent *hp, hent;
     char *buf, *ptr;
     size_t len, num, i;
@@ -72,13 +72,12 @@
     sethostent_r(&hf);
     if (hf == NULL) {
         errno = EINVAL;
-        *info->he = NETDB_INTERNAL;
-        return NULL;
+        // TODO: Consider to remap error code without relying on errno.
+        return EAI_SYSTEM;
     }
 
     if ((ptr = buf = (char*) malloc(len = info->buflen)) == NULL) {
-        *info->he = NETDB_INTERNAL;
-        return NULL;
+        return EAI_MEMORY;
     }
 
     hent.h_name = NULL;
@@ -90,9 +89,10 @@
         info->hp->h_addrtype = af;
         info->hp->h_length = 0;
 
-        hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen, info->he);
+        int herrno;
+        hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen, &herrno);
         if (hp == NULL) {
-            if (*info->he == NETDB_INTERNAL && errno == ENOSPC) {
+            if (herrno == NETDB_INTERNAL && errno == ENOSPC) {
                 goto nospc;  // glibc compatibility.
             }
             break;
@@ -127,9 +127,11 @@
     endhostent_r(&hf);
 
     if (num == 0) {
-        *info->he = HOST_NOT_FOUND;
         free(buf);
-        return NULL;
+        // TODO: Perhaps convert HOST_NOT_FOUND to EAI_NONAME instead.
+        // The original return error number is h_errno HOST_NOT_FOUND which was converted to
+        // EAI_NODATA.
+        return EAI_NODATA;
     }
 
     hp = info->hp;
@@ -160,33 +162,35 @@
     hp->h_aliases[anum] = NULL;
 
     free(buf);
-    return hp;
+    return 0;
 nospc:
-    *info->he = NETDB_INTERNAL;
     free(buf);
     errno = ENOSPC;
-    return NULL;
+    return EAI_MEMORY;
 }
 
-bool _hf_gethtbyaddr(const unsigned char* uaddr, int len, int af, getnamaddr* info) {
+int _hf_gethtbyaddr(const unsigned char* uaddr, int len, int af, getnamaddr* info) {
     info->hp->h_length = len;
     info->hp->h_addrtype = af;
 
     FILE* hf = NULL;
     sethostent_r(&hf);
     if (hf == NULL) {
-        *info->he = NETDB_INTERNAL;
-        return false;
+        // TODO: Consider to remap error code without relying on errno.
+        return EAI_SYSTEM;
     }
     struct hostent* hp;
-    while ((hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen, info->he)) != NULL)
+    int herrno;
+    while ((hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen, &herrno)) != NULL)
         if (!memcmp(hp->h_addr_list[0], uaddr, (size_t) hp->h_length)) break;
     endhostent_r(&hf);
 
     if (hp == NULL) {
-        if (errno == ENOSPC) return false;  // glibc compatibility.
-        *info->he = HOST_NOT_FOUND;
-        return false;
+        if (errno == ENOSPC) return EAI_MEMORY;  // glibc compatibility.
+        // TODO: Perhaps convert HOST_NOT_FOUND to EAI_NONAME instead.
+        // The original return error number is h_errno HOST_NOT_FOUND which was converted to
+        // EAI_NODATA.
+        return EAI_NODATA;
     }
-    return true;
+    return 0;
 }
diff --git a/server/DnsProxyListener.cpp b/server/DnsProxyListener.cpp
index 73a1e2a..ef36438 100644
--- a/server/DnsProxyListener.cpp
+++ b/server/DnsProxyListener.cpp
@@ -126,7 +126,7 @@
     RESOLV_STUB.resolv_get_private_dns_status_for_net(dns_netid, &privateDnsStatus);
     switch (static_cast<PrivateDnsMode>(privateDnsStatus.mode)) {
         case PrivateDnsMode::OPPORTUNISTIC:
-            for (unsigned i = 0; i < privateDnsStatus.numServers; i++) {
+            for (int i = 0; i < privateDnsStatus.numServers; i++) {
                 if (privateDnsStatus.serverStatus[i].validation == Validation::success) {
                     return true;
                 }
@@ -171,11 +171,11 @@
     }
 }
 
-void addIpAddrWithinLimit(std::vector<android::String16>* ip_addrs, const sockaddr* addr,
+void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
                           socklen_t addrlen);
 
-int extractIpAddressAnswers(const uint8_t* answer, size_t anslen, int ipType,
-                            std::vector<String16>* ip_addrs) {
+int extractResNsendAnswers(const uint8_t* answer, size_t anslen, int ipType,
+                           std::vector<std::string>* ip_addrs) {
     int total_ip_addr_count = 0;
     ns_msg handle;
     if (ns_initparse((const uint8_t*) answer, anslen, &handle) < 0) {
@@ -194,9 +194,8 @@
             addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin, sizeof(sin));
             total_ip_addr_count++;
         } else if (ipType == ns_t_aaaa) {
-            struct in6_addr in6;
-            memcpy(&in6, rdata, IN6ADDRSZ);
-            sockaddr_in6 sin6 = {.sin6_family = AF_INET6, .sin6_addr = in6};
+            sockaddr_in6 sin6 = {.sin6_family = AF_INET6};
+            memcpy(&sin6.sin6_addr, rdata, sizeof(sin6.sin6_addr));
             addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin6, sizeof(sin6));
             total_ip_addr_count++;
         }
@@ -205,6 +204,65 @@
     return total_ip_addr_count;
 }
 
+int extractGetaddrinfoAnswers(const addrinfo* result, std::vector<std::string>* ip_addrs) {
+    int total_ip_addr_count = 0;
+    if (result == nullptr) {
+        return 0;
+    }
+    for (const addrinfo* ai = result; ai; ai = ai->ai_next) {
+        sockaddr* ai_addr = ai->ai_addr;
+        if (ai_addr) {
+            addIpAddrWithinLimit(ip_addrs, ai_addr, ai->ai_addrlen);
+            total_ip_addr_count++;
+        }
+    }
+    return total_ip_addr_count;
+}
+
+int extractGethostbynameAnswers(const hostent* hp, std::vector<std::string>* ip_addrs) {
+    int total_ip_addr_count = 0;
+    if (hp == nullptr) {
+        return 0;
+    }
+    if (hp->h_addrtype == AF_INET) {
+        in_addr** list = (in_addr**) hp->h_addr_list;
+        for (int i = 0; list[i] != nullptr; i++) {
+            sockaddr_in sin = {.sin_family = AF_INET, .sin_addr = *list[i]};
+            addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin, sizeof(sin));
+            total_ip_addr_count++;
+        }
+    } else if (hp->h_addrtype == AF_INET6) {
+        in6_addr** list = (in6_addr**) hp->h_addr_list;
+        for (int i = 0; list[i] != nullptr; i++) {
+            sockaddr_in6 sin6 = {.sin6_family = AF_INET6, .sin6_addr = *list[i]};
+            addIpAddrWithinLimit(ip_addrs, (sockaddr*) &sin6, sizeof(sin6));
+            total_ip_addr_count++;
+        }
+    }
+    return total_ip_addr_count;
+}
+
+int rcodeToAiError(int rcode) {
+    switch (rcode) {
+        case NOERROR:
+            return 0;
+        case RCODE_TIMEOUT:
+            return NETD_RESOLV_TIMEOUT;
+        default:
+            return EAI_NODATA;
+    }
+}
+
+int resNSendToAiError(int err, int rcode) {
+    if (err > 0) {
+        return rcodeToAiError(rcode);
+    }
+    if (err == -ETIMEDOUT) {
+        return NETD_RESOLV_TIMEOUT;
+    }
+    return EAI_SYSTEM;
+}
+
 template <typename IntegralType>
 bool simpleStrtoul(const char* input, IntegralType* output, int base = 10) {
     char* endPtr;
@@ -232,6 +290,29 @@
     return true;
 }
 
+void reportDnsInfoAll(int reportingLevel, int eventType, const android_net_context& netContext,
+                      int latencyMs, int returnCode, const std::string& query_name,
+                      const std::vector<std::string>& ip_addrs = {}, int total_ip_addr_count = 0) {
+    const auto listener = gCtls->eventReporter.getNetdEventListener();
+    if (!listener) return;
+
+    switch (reportingLevel) {
+        case INetdEventListener::REPORTING_LEVEL_NONE:
+            // Skip reporting.
+            break;
+        case INetdEventListener::REPORTING_LEVEL_METRICS:
+            // Metrics reporting is on. Send metrics.
+            listener->onDnsEvent(netContext.dns_netid, eventType, returnCode, latencyMs, "", {}, -1,
+                                 -1);
+            break;
+        case INetdEventListener::REPORTING_LEVEL_FULL:
+            // Full event info reporting is on. Send full info.
+            listener->onDnsEvent(netContext.dns_netid, eventType, returnCode, latencyMs, query_name,
+                                 ip_addrs, total_ip_addr_count, netContext.uid);
+            break;
+    }
+}
+
 bool onlyIPv4Answers(const struct addrinfo* res) {
     // Null addrinfo pointer isn't checked because the caller doesn't pass null pointer.
 
@@ -408,18 +489,16 @@
     registerCmd(new ResNSendCommand(this));
 }
 
-DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(
-        SocketClient *c, char* host, char* service, struct addrinfo* hints,
-        const android_net_context& netcontext, const int reportingLevel,
-        const android::sp<android::net::metrics::INetdEventListener>& netdEventListener)
-        : mClient(c),
-          mHost(host),
-          mService(service),
-          mHints(hints),
-          mNetContext(netcontext),
-          mReportingLevel(reportingLevel),
-          mNetdEventListener(netdEventListener) {
-}
+DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient* c, char* host, char* service,
+                                                         addrinfo* hints,
+                                                         const android_net_context& netcontext,
+                                                         const int reportingLevel)
+    : mClient(c),
+      mHost(host),
+      mService(service),
+      mHints(hints),
+      mNetContext(netcontext),
+      mReportingLevel(reportingLevel) {}
 
 DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() {
     free(mHost);
@@ -439,7 +518,7 @@
 }
 
 // Returns true on success
-static bool sendhostent(SocketClient *c, struct hostent *hp) {
+static bool sendhostent(SocketClient* c, hostent* hp) {
     bool success = true;
     int i;
     if (hp->h_name != nullptr) {
@@ -466,7 +545,7 @@
     return success;
 }
 
-static bool sendaddrinfo(SocketClient* c, struct addrinfo* ai) {
+static bool sendaddrinfo(SocketClient* c, addrinfo* ai) {
     // struct addrinfo {
     //      int     ai_flags;       /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
     //      int     ai_family;      /* PF_xxx */
@@ -559,7 +638,7 @@
                 mNetContext.uid, mNetContext.flags);
     }
 
-    struct addrinfo* result = nullptr;
+    addrinfo* result = nullptr;
     Stopwatch s;
     maybeFixupNetContext(&mNetContext);
     const uid_t uid = mClient->getUid();
@@ -583,7 +662,7 @@
         mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
     } else {
         bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
-        struct addrinfo* ai = result;
+        addrinfo* ai = result;
         while (ai && success) {
             success = sendBE32(mClient, 1) && sendaddrinfo(mClient, ai);
             ai = ai->ai_next;
@@ -593,49 +672,17 @@
             ALOGW("Error writing DNS result to client");
         }
     }
-    std::vector<String16> ip_addrs;
-    int total_ip_addr_count = 0;
-    if (result) {
-        if (mNetdEventListener != nullptr
-                && mReportingLevel == INetdEventListener::REPORTING_LEVEL_FULL) {
-            for (addrinfo* ai = result; ai; ai = ai->ai_next) {
-                sockaddr* ai_addr = ai->ai_addr;
-                if (ai_addr) {
-                    addIpAddrWithinLimit(&ip_addrs, ai_addr, ai->ai_addrlen);
-                    total_ip_addr_count++;
-                }
-            }
-        }
-        freeaddrinfo(result);
-    }
+    std::vector<std::string> ip_addrs;
+    const int total_ip_addr_count = extractGetaddrinfoAnswers(result, &ip_addrs);
+    reportDnsInfoAll(mReportingLevel, INetdEventListener::EVENT_GETADDRINFO, mNetContext, latencyMs,
+                     rv, mHost, ip_addrs, total_ip_addr_count);
+    freeaddrinfo(result);
     mClient->decRef();
-    if (mNetdEventListener != nullptr) {
-        switch (mReportingLevel) {
-            case INetdEventListener::REPORTING_LEVEL_NONE:
-                // Skip reporting.
-                break;
-            case INetdEventListener::REPORTING_LEVEL_METRICS:
-                // Metrics reporting is on. Send metrics.
-                mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
-                                               INetdEventListener::EVENT_GETADDRINFO, rv,
-                                               latencyMs, String16(""), {}, -1, -1);
-                break;
-            case INetdEventListener::REPORTING_LEVEL_FULL:
-                // Full event info reporting is on. Send full info.
-                mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
-                                               INetdEventListener::EVENT_GETADDRINFO, rv,
-                                               latencyMs, String16(mHost), ip_addrs,
-                                               total_ip_addr_count, mNetContext.uid);
-                break;
-        }
-    } else {
-        ALOGW("Netd event listener is not available; skipping.");
-    }
 }
 
 namespace {
 
-void addIpAddrWithinLimit(std::vector<android::String16>* ip_addrs, const sockaddr* addr,
+void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* addr,
                           socklen_t addrlen) {
     // ipAddresses array is limited to first INetdEventListener::DNS_REPORTED_IP_ADDRESSES_LIMIT
     // addresses for A and AAAA. Total count of addresses is provided, to be able to tell whether
@@ -643,7 +690,7 @@
     if (ip_addrs->size() < INetdEventListener::DNS_REPORTED_IP_ADDRESSES_LIMIT) {
         char ip_addr[INET6_ADDRSTRLEN];
         if (getnameinfo(addr, addrlen, ip_addr, sizeof(ip_addr), nullptr, 0, NI_NUMERICHOST) == 0) {
-            ip_addrs->push_back(String16(ip_addr));
+            ip_addrs->push_back(std::string(ip_addr));
         }
     }
 }
@@ -682,7 +729,7 @@
         service = strdup(service);
     }
 
-    struct addrinfo* hints = nullptr;
+    addrinfo* hints = nullptr;
     int ai_flags = strtol(argv[3], nullptr, 10);
     int ai_family = strtol(argv[4], nullptr, 10);
     int ai_socktype = strtol(argv[5], nullptr, 10);
@@ -699,7 +746,7 @@
 
     if (ai_flags != -1 || ai_family != -1 ||
         ai_socktype != -1 || ai_protocol != -1) {
-        hints = (struct addrinfo*) calloc(1, sizeof(struct addrinfo));
+        hints = (addrinfo*) calloc(1, sizeof(addrinfo));
         hints->ai_flags = ai_flags;
         hints->ai_family = ai_family;
         hints->ai_socktype = ai_socktype;
@@ -717,9 +764,8 @@
 
     const int metricsLevel = mDnsProxyListener->mEventReporter->getMetricsReportingLevel();
 
-    DnsProxyListener::GetAddrInfoHandler* handler =
-            new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext,
-                    metricsLevel, mDnsProxyListener->mEventReporter->getNetdEventListener());
+    DnsProxyListener::GetAddrInfoHandler* handler = new DnsProxyListener::GetAddrInfoHandler(
+            cli, name, service, hints, netcontext, metricsLevel);
     tryThreadOrError(cli, handler);
     return 0;
 }
@@ -754,22 +800,16 @@
     }
     const int metricsLevel = mDnsProxyListener->mEventReporter->getMetricsReportingLevel();
 
-    DnsProxyListener::ResNSendHandler* handler = new DnsProxyListener::ResNSendHandler(
-            cli, argv[1], netcontext, metricsLevel,
-            mDnsProxyListener->mEventReporter->getNetdEventListener());
+    DnsProxyListener::ResNSendHandler* handler =
+            new DnsProxyListener::ResNSendHandler(cli, argv[1], netcontext, metricsLevel);
     tryThreadOrError(cli, handler);
     return 0;
 }
 
-DnsProxyListener::ResNSendHandler::ResNSendHandler(
-        SocketClient* c, std::string msg, const android_net_context& netcontext,
-        const int reportingLevel,
-        const android::sp<android::net::metrics::INetdEventListener>& netdEventListener)
-    : mClient(c),
-      mMsg(std::move(msg)),
-      mNetContext(netcontext),
-      mReportingLevel(reportingLevel),
-      mNetdEventListener(netdEventListener) {}
+DnsProxyListener::ResNSendHandler::ResNSendHandler(SocketClient* c, std::string msg,
+                                                   const android_net_context& netcontext,
+                                                   const int reportingLevel)
+    : mClient(c), mMsg(std::move(msg)), mNetContext(netcontext), mReportingLevel(reportingLevel) {}
 
 DnsProxyListener::ResNSendHandler::~ResNSendHandler() {
     mClient->decRef();
@@ -826,6 +866,10 @@
     // Fail, send -errno
     if (nsendAns < 0) {
         sendBE32(mClient, nsendAns);
+        if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
+            reportDnsInfoAll(mReportingLevel, INetdEventListener::EVENT_RES_NSEND, mNetContext,
+                             latencyMs, resNSendToAiError(nsendAns, arcode), rr_name);
+        }
         return;
     }
 
@@ -841,32 +885,13 @@
         return;
     }
 
-    // Parse result for statistics
-    if (mNetdEventListener != nullptr) {
-        // TODO: Enable this when we have a INetdEventListener::EVENT_RES_NSEND constant
-        if (0) {
-            switch (mReportingLevel) {
-                case INetdEventListener::REPORTING_LEVEL_NONE:
-                    // Reporting is off.
-                    break;
-                case INetdEventListener::REPORTING_LEVEL_METRICS:
-                    // Metrics reporting is on. Send metrics.
-                    mNetdEventListener->onDnsEvent(
-                            mNetContext.dns_netid, INetdEventListener::EVENT_GETHOSTBYNAME,
-                            nsendAns > 0 ? 0 : nsendAns, latencyMs, String16(""), {}, -1, -1);
-                    break;
-                case INetdEventListener::REPORTING_LEVEL_FULL:
-                    // Full event info reporting is on. Send full info.
-                    std::vector<String16> ip_addrs;
-                    int total_ip_addr_count =
-                            extractIpAddressAnswers(ansBuf.data(), nsendAns, rr_type, &ip_addrs);
-                    mNetdEventListener->onDnsEvent(
-                            mNetContext.dns_netid, INetdEventListener::EVENT_GETHOSTBYNAME,
-                            nsendAns > 0 ? 0 : nsendAns, latencyMs, String16(rr_name.c_str()),
-                            ip_addrs, total_ip_addr_count, uid);
-                    break;
-            }
-        }
+    if (rr_type == ns_t_a || rr_type == ns_t_aaaa) {
+        std::vector<std::string> ip_addrs;
+        const int total_ip_addr_count =
+                extractResNsendAnswers((uint8_t*) ansBuf.data(), nsendAns, rr_type, &ip_addrs);
+        reportDnsInfoAll(mReportingLevel, INetdEventListener::EVENT_RES_NSEND, mNetContext,
+                         latencyMs, resNSendToAiError(nsendAns, arcode), rr_name, ip_addrs,
+                         total_ip_addr_count);
     }
 }
 
@@ -912,22 +937,15 @@
     const int metricsLevel = mDnsProxyListener->mEventReporter->getMetricsReportingLevel();
 
     DnsProxyListener::GetHostByNameHandler* handler =
-            new DnsProxyListener::GetHostByNameHandler(cli, name, af, netcontext, metricsLevel,
-                    mDnsProxyListener->mEventReporter->getNetdEventListener());
+            new DnsProxyListener::GetHostByNameHandler(cli, name, af, netcontext, metricsLevel);
     tryThreadOrError(cli, handler);
     return 0;
 }
 
 DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c, char* name, int af,
-        const android_net_context& netcontext, const int metricsLevel,
-        const android::sp<android::net::metrics::INetdEventListener>& netdEventListener)
-        : mClient(c),
-          mName(name),
-          mAf(af),
-          mNetContext(netcontext),
-          mReportingLevel(metricsLevel),
-          mNetdEventListener(netdEventListener) {
-}
+                                                             const android_net_context& netcontext,
+                                                             const int metricsLevel)
+    : mClient(c), mName(name), mAf(af), mNetContext(netcontext), mReportingLevel(metricsLevel) {}
 
 DnsProxyListener::GetHostByNameHandler::~GetHostByNameHandler() {
     free(mName);
@@ -976,7 +994,7 @@
     Stopwatch s;
     maybeFixupNetContext(&mNetContext);
     const uid_t uid = mClient->getUid();
-    struct hostent* hp = nullptr;
+    hostent* hp = nullptr;
     int32_t rv = 0;
     if (queryLimiter.start(uid)) {
         rv = RESOLV_STUB.android_gethostbynamefornetcontext(mName, mAf, &mNetContext, &hp);
@@ -1009,46 +1027,10 @@
         ALOGW("GetHostByNameHandler: Error writing DNS result to client");
     }
 
-    if (mNetdEventListener != nullptr) {
-        std::vector<String16> ip_addrs;
-        int total_ip_addr_count = 0;
-        if (mReportingLevel == INetdEventListener::REPORTING_LEVEL_FULL) {
-            if (hp != nullptr && hp->h_addrtype == AF_INET) {
-                in_addr** list = (in_addr**) hp->h_addr_list;
-                for (int i = 0; list[i] != nullptr; i++) {
-                    sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = *list[i] };
-                    addIpAddrWithinLimit(&ip_addrs, (sockaddr*) &sin, sizeof(sin));
-                    total_ip_addr_count++;
-                }
-            } else if (hp != nullptr && hp->h_addrtype == AF_INET6) {
-                in6_addr** list = (in6_addr**) hp->h_addr_list;
-                for (int i = 0; list[i] != nullptr; i++) {
-                    sockaddr_in6 sin6 = { .sin6_family = AF_INET6, .sin6_addr = *list[i] };
-                    addIpAddrWithinLimit(&ip_addrs, (sockaddr*) &sin6, sizeof(sin6));
-                    total_ip_addr_count++;
-                }
-            }
-        }
-        switch (mReportingLevel) {
-            case INetdEventListener::REPORTING_LEVEL_NONE:
-                // Reporting is off.
-                break;
-            case INetdEventListener::REPORTING_LEVEL_METRICS:
-                // Metrics reporting is on. Send metrics.
-                mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
-                                               INetdEventListener::EVENT_GETHOSTBYNAME,
-                                               rv, latencyMs, String16(""), {}, -1, -1);
-                break;
-            case INetdEventListener::REPORTING_LEVEL_FULL:
-                // Full event info reporting is on. Send full info.
-                mNetdEventListener->onDnsEvent(mNetContext.dns_netid,
-                                               INetdEventListener::EVENT_GETHOSTBYNAME,
-                                               rv, latencyMs, String16(mName), ip_addrs,
-                                               total_ip_addr_count, uid);
-                break;
-        }
-    }
-
+    std::vector<std::string> ip_addrs;
+    const int total_ip_addr_count = extractGethostbynameAnswers(hp, &ip_addrs);
+    reportDnsInfoAll(mReportingLevel, INetdEventListener::EVENT_GETHOSTBYNAME, mNetContext,
+                     latencyMs, rv, mName, ip_addrs, total_ip_addr_count);
     mClient->decRef();
 }
 
@@ -1081,7 +1063,7 @@
     unsigned netId = strtoul(argv[4], nullptr, 10);
     const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);
 
-    void* addr = malloc(sizeof(struct in6_addr));
+    void* addr = malloc(sizeof(in6_addr));
     errno = 0;
     int result = inet_pton(addrFamily, addrStr, addr);
     if (result <= 0) {
@@ -1151,8 +1133,8 @@
     if (queryLimiter.start(uid)) {
         // Remove NAT64 prefix and do reverse DNS query
         struct in_addr v4addr = {.s_addr = v6addr.s6_addr32[3]};
-        *hpp = RESOLV_STUB.android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET,
-                                                              &mNetContext);
+        RESOLV_STUB.android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET,
+                                                       &mNetContext, hpp);
         queryLimiter.finish(uid);
         if (*hpp) {
             // Replace IPv4 address with original queried IPv6 address in place. The space has
@@ -1175,22 +1157,23 @@
 
     maybeFixupNetContext(&mNetContext);
     const uid_t uid = mClient->getUid();
-    struct hostent* hp = nullptr;
+    hostent* hp = nullptr;
+    int32_t rv = 0;
     if (queryLimiter.start(uid)) {
-        hp = RESOLV_STUB.android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily,
-                                                            &mNetContext);
+        rv = RESOLV_STUB.android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily,
+                                                            &mNetContext, &hp);
         queryLimiter.finish(uid);
     } else {
+        rv = EAI_MEMORY;
         ALOGE("gethostbyaddr: from UID %d, max concurrent queries reached", uid);
     }
 
     doDns64ReverseLookup(&hp);
 
     if (DBG) {
-        ALOGD("GetHostByAddrHandler::run gethostbyaddr errno: %s hp->h_name = %s, name_len = %zu",
-                hp ? "success" : strerror(errno),
-                (hp && hp->h_name) ? hp->h_name : "null",
-                (hp && hp->h_name) ? strlen(hp->h_name) + 1 : 0);
+        ALOGD("GetHostByAddrHandler::run gethostbyaddr result: %s hp->h_name = %s, name_len = %zu",
+              hp ? "success" : gai_strerror(rv), (hp && hp->h_name) ? hp->h_name : "null",
+              (hp && hp->h_name) ? strlen(hp->h_name) + 1 : 0);
     }
 
     bool success = true;
diff --git a/server/DnsProxyListener.h b/server/DnsProxyListener.h
index e3fcd88..985ed62 100644
--- a/server/DnsProxyListener.h
+++ b/server/DnsProxyListener.h
@@ -22,8 +22,7 @@
 
 #include "EventReporter.h"
 #include "NetdCommand.h"
-#include "android/net/metrics/INetdEventListener.h"
-#include "netd_resolv/resolv.h"  // struct android_net_context
+#include "netd_resolv/resolv.h"  // android_net_context
 
 namespace android {
 namespace net {
@@ -51,30 +50,25 @@
         DnsProxyListener* mDnsProxyListener;
     };
 
+    /* ------ getaddrinfo ------*/
     class GetAddrInfoHandler {
       public:
         // Note: All of host, service, and hints may be NULL
-        GetAddrInfoHandler(SocketClient *c,
-                           char* host,
-                           char* service,
-                           struct addrinfo* hints,
-                           const struct android_net_context& netcontext,
-                           const int reportingLevel,
-                           const android::sp<android::net::metrics::INetdEventListener>& listener);
+        GetAddrInfoHandler(SocketClient* c, char* host, char* service, addrinfo* hints,
+                           const android_net_context& netcontext, int reportingLevel);
         ~GetAddrInfoHandler();
 
         void run();
 
       private:
-        void doDns64Synthesis(int32_t* rv, struct addrinfo** res);
+        void doDns64Synthesis(int32_t* rv, addrinfo** res);
 
         SocketClient* mClient;  // ref counted
         char* mHost;            // owned. TODO: convert to std::string.
         char* mService;         // owned. TODO: convert to std::string.
-        struct addrinfo* mHints;  // owned
-        struct android_net_context mNetContext;
+        addrinfo* mHints;       // owned
+        android_net_context mNetContext;
         const int mReportingLevel;
-        android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
     };
 
     /* ------ gethostbyname ------*/
@@ -90,25 +84,20 @@
 
     class GetHostByNameHandler {
       public:
-        GetHostByNameHandler(SocketClient *c,
-                            char *name,
-                            int af,
-                            const android_net_context& netcontext,
-                            int reportingLevel,
-                            const android::sp<android::net::metrics::INetdEventListener>& listener);
+        GetHostByNameHandler(SocketClient* c, char* name, int af,
+                             const android_net_context& netcontext, int reportingLevel);
         ~GetHostByNameHandler();
 
         void run();
 
       private:
-        void doDns64Synthesis(int32_t* rv, struct hostent** hpp);
+        void doDns64Synthesis(int32_t* rv, hostent** hpp);
 
         SocketClient* mClient; //ref counted
         char* mName;           // owned. TODO: convert to std::string.
         int mAf;
         android_net_context mNetContext;
         const int mReportingLevel;
-        android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
     };
 
     /* ------ gethostbyaddr ------*/
@@ -134,7 +123,7 @@
         void run();
 
       private:
-        void doDns64ReverseLookup(struct hostent** hpp);
+        void doDns64ReverseLookup(hostent** hpp);
 
         SocketClient* mClient;  // ref counted
         void* mAddress;    // address to lookup; owned
@@ -157,8 +146,7 @@
     class ResNSendHandler {
       public:
         ResNSendHandler(SocketClient* c, std::string msg, const android_net_context& netcontext,
-                        const int reportingLevel,
-                        const android::sp<android::net::metrics::INetdEventListener>& listener);
+                        int reportingLevel);
         ~ResNSendHandler();
 
         void run();
@@ -166,9 +154,8 @@
       private:
         SocketClient* mClient;  // ref counted
         std::string mMsg;
-        struct android_net_context mNetContext;
+        android_net_context mNetContext;
         const int mReportingLevel;
-        android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
     };
 };
 
diff --git a/server/ResolverController.cpp b/server/ResolverController.cpp
index a0dc3de..39a0b83 100644
--- a/server/ResolverController.cpp
+++ b/server/ResolverController.cpp
@@ -321,7 +321,7 @@
 
     ExternalPrivateDnsStatus privateDnsStatus = {PrivateDnsMode::OFF, 0, {}};
     RESOLV_STUB.resolv_get_private_dns_status_for_net(netId, &privateDnsStatus);
-    for (unsigned i = 0; i < privateDnsStatus.numServers; i++) {
+    for (int i = 0; i < privateDnsStatus.numServers; i++) {
         std::string tlsServer_str = addrToString(&(privateDnsStatus.serverStatus[i].ss));
         tlsServers->push_back(std::move(tlsServer_str));
     }
@@ -418,7 +418,7 @@
         } else {
             dw.println("Private DNS configuration (%u entries)", privateDnsStatus.numServers);
             dw.incIndent();
-            for (unsigned i = 0; i < privateDnsStatus.numServers; i++) {
+            for (int i = 0; i < privateDnsStatus.numServers; i++) {
                 dw.println("%s name{%s} status{%s}",
                            addrToString(&(privateDnsStatus.serverStatus[i].ss)).c_str(),
                            privateDnsStatus.serverStatus[i].hostname,
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index 62dcbbf..7d7be58 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -325,11 +325,10 @@
 // dnsmasq can't parse commands larger than this due to the fixed-size buffer
 // in check_android_listeners(). The receiving buffer is 1024 bytes long, but
 // dnsmasq reads up to 1023 bytes.
-#define MAX_CMD_SIZE 1023
+const size_t MAX_CMD_SIZE = 1023;
 
+// TODO: convert callers to the overload taking a vector<string>
 int TetherController::setDnsForwarders(unsigned netId, char **servers, int numServers) {
-    int i;
-
     Fwmark fwmark;
     fwmark.netId = netId;
     fwmark.explicitlySelected = true;
@@ -339,7 +338,7 @@
     std::string daemonCmd = StringPrintf("update_dns%s0x%x", SEPARATOR, fwmark.intValue);
 
     mDnsForwarders.clear();
-    for (i = 0; i < numServers; i++) {
+    for (int i = 0; i < numServers; i++) {
         ALOGD("setDnsForwarders(0x%x %d = '%s')", fwmark.intValue, i, servers[i]);
 
         addrinfo *res, hints = { .ai_flags = AI_NUMERICHOST };
diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp
index b6d1320..b921c9a 100644
--- a/server/TrafficController.cpp
+++ b/server/TrafficController.cpp
@@ -200,6 +200,34 @@
     return netdutils::status::ok;
 }
 
+static Status attachProgramToCgroup(const char* programPath, const int cgroupFd,
+                                    bpf_attach_type type) {
+    unique_fd cgroupProg(bpfFdGet(programPath, 0));
+    if (cgroupProg == -1) {
+        int ret = errno;
+        ALOGE("Failed to get program from %s: %s", programPath, strerror(ret));
+        return statusFromErrno(ret, "cgroup program get failed");
+    }
+    if (android::bpf::attachProgram(type, cgroupProg, cgroupFd)) {
+        int ret = errno;
+        ALOGE("Program from %s attach failed: %s", programPath, strerror(ret));
+        return statusFromErrno(ret, "program attach failed");
+    }
+    return netdutils::status::ok;
+}
+
+static Status initPrograms() {
+    unique_fd cg_fd(open(CGROUP_ROOT_PATH, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+    if (cg_fd == -1) {
+        int ret = errno;
+        ALOGE("Failed to open the cgroup directory: %s", strerror(ret));
+        return statusFromErrno(ret, "Open the cgroup directory failed");
+    }
+    RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_EGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_EGRESS));
+    RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_INGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_INGRESS));
+    return netdutils::status::ok;
+}
+
 Status TrafficController::start() {
 
     if (!ebpfSupported) {
@@ -216,6 +244,8 @@
 
     RETURN_IF_NOT_OK(initMaps());
 
+    RETURN_IF_NOT_OK(initPrograms());
+
     // Fetch the list of currently-existing interfaces. At this point NetlinkHandler is
     // already running, so it will call addInterface() when any new interface appears.
     std::map<std::string, uint32_t> ifacePairs;
@@ -257,19 +287,6 @@
     };
     expectOk(mSkDestroyListener->subscribe(kSockDiagDoneMsgType, rxDoneHandler));
 
-    int* status = nullptr;
-
-    std::vector<const char*> prog_args{
-        "/system/bin/bpfloader",
-    };
-
-    prog_args.push_back(nullptr);
-    int ret = android_fork_execvp(prog_args.size(), (char**) prog_args.data(), status, false, true);
-    if (ret) {
-        ret = errno;
-        ALOGE("failed to execute %s: %s", prog_args[0], strerror(errno));
-        return statusFromErrno(ret, "run bpf loader failed");
-    }
     return netdutils::status::ok;
 }
 
diff --git a/server/binder/android/net/metrics/INetdEventListener.aidl b/server/binder/android/net/metrics/INetdEventListener.aidl
index 95b07ea..ef1b2cb 100644
--- a/server/binder/android/net/metrics/INetdEventListener.aidl
+++ b/server/binder/android/net/metrics/INetdEventListener.aidl
@@ -24,6 +24,8 @@
 oneway interface INetdEventListener {
     const int EVENT_GETADDRINFO = 1;
     const int EVENT_GETHOSTBYNAME = 2;
+    const int EVENT_GETHOSTBYADDR = 3;
+    const int EVENT_RES_NSEND = 4;
 
     const int REPORTING_LEVEL_NONE = 0;
     const int REPORTING_LEVEL_METRICS = 1;
@@ -46,8 +48,9 @@
      *        of ipAddresses if there were too many addresses to log.
      * @param uid the UID of the application that performed the query.
      */
-    void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs, String hostname,
-            in String[] ipAddresses, int ipAddressesCount, int uid);
+    void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
+            @utf8InCpp String hostname, in @utf8InCpp String[] ipAddresses,
+            int ipAddressesCount, int uid);
 
     /**
      * Represents a private DNS validation success or failure.
diff --git a/server/main.cpp b/server/main.cpp
index b9ff33f..4b06ada 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <chrono>
 #include <stdio.h>
 #include <stdlib.h>
 #include <signal.h>
@@ -31,6 +32,7 @@
 
 #include "log/log.h"
 
+#include <android-base/properties.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
@@ -90,6 +92,12 @@
     // Before we start any threads, populate the resolver stub pointers.
     resolv_stub_init();
 
+    // Make sure BPF programs are loaded before doing anything
+    while (!android::base::WaitForProperty("bpf.progs_loaded", "1",
+           std::chrono::seconds(5))) {
+        ALOGD("netd waited 5s for bpf.progs_loaded, still waiting...");
+    }
+
     NetlinkManager *nm = NetlinkManager::Instance();
     if (nm == nullptr) {
         ALOGE("Unable to create NetlinkManager");
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index c4029f0..d7c449e 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -940,7 +940,6 @@
 
     for (size_t i = 0; i < std::size(kTestData); i++) {
         const auto& td = kTestData[i];
-
         const binder::Status status =
                 mNetd->setProcSysNet(td.ipversion, td.which, td.ifname, td.parameter, td.value);