Merge "onDnsEvent check in resolv_integration_test"
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.cpp b/tests/dns_metrics_listener/dns_metrics_listener.cpp
index c3a15ed..1715118 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.cpp
+++ b/tests/dns_metrics_listener/dns_metrics_listener.cpp
@@ -16,9 +16,11 @@
 
 #include "dns_metrics_listener.h"
 
-#include <android-base/chrono_utils.h>
 #include <thread>
 
+#include <android-base/chrono_utils.h>
+#include <android-base/format.h>
+
 namespace android {
 namespace net {
 namespace metrics {
@@ -29,6 +31,18 @@
 constexpr milliseconds kRetryIntervalMs{20};
 constexpr milliseconds kEventTimeoutMs{5000};
 
+bool DnsMetricsListener::DnsEvent::operator==(const DnsMetricsListener::DnsEvent& o) const {
+    return std::tie(netId, eventType, returnCode, hostname, ipAddresses, ipAddressesCount) ==
+           std::tie(o.netId, o.eventType, o.returnCode, o.hostname, o.ipAddresses,
+                    o.ipAddressesCount);
+}
+
+std::ostream& operator<<(std::ostream& os, const DnsMetricsListener::DnsEvent& data) {
+    return os << fmt::format("[{}, {}, {}, {}, [{}], {}]", data.netId, data.eventType,
+                             data.returnCode, data.hostname, fmt::join(data.ipAddresses, ", "),
+                             data.ipAddressesCount);
+}
+
 ::ndk::ScopedAStatus DnsMetricsListener::onNat64PrefixEvent(int32_t netId, bool added,
                                                             const std::string& prefixString,
                                                             int32_t /*prefixLength*/) {
@@ -50,6 +64,19 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
+::ndk::ScopedAStatus DnsMetricsListener::onDnsEvent(int32_t netId, int32_t eventType,
+                                                    int32_t returnCode, int32_t /*latencyMs*/,
+                                                    const std::string& hostname,
+                                                    const std::vector<std::string>& ipAddresses,
+                                                    int32_t ipAddressesCount, int32_t /*uid*/) {
+    std::lock_guard lock(mMutex);
+    if (netId == mNetId) {
+        mDnsEventRecords.push(
+                {netId, eventType, returnCode, hostname, ipAddresses, ipAddressesCount});
+    }
+    return ::ndk::ScopedAStatus::ok();
+}
+
 bool DnsMetricsListener::waitForNat64Prefix(ExpectNat64PrefixStatus status, milliseconds timeout) {
     android::base::Timer t;
     while (t.duration() < timeout) {
@@ -91,6 +118,25 @@
     return false;
 }
 
+std::optional<DnsMetricsListener::DnsEvent> DnsMetricsListener::popDnsEvent() {
+    // Wait until the queue is not empty or timeout.
+    android::base::Timer t;
+    while (t.duration() < milliseconds{1000}) {
+        {
+            std::lock_guard lock(mMutex);
+            if (!mDnsEventRecords.empty()) break;
+        }
+        std::this_thread::sleep_for(kRetryIntervalMs);
+    }
+
+    std::lock_guard lock(mMutex);
+    if (mDnsEventRecords.empty()) return std::nullopt;
+
+    auto ret = mDnsEventRecords.front();
+    mDnsEventRecords.pop();
+    return ret;
+}
+
 }  // namespace metrics
 }  // namespace net
 }  // namespace android
diff --git a/tests/dns_metrics_listener/dns_metrics_listener.h b/tests/dns_metrics_listener/dns_metrics_listener.h
index 0b36462..ff71466 100644
--- a/tests/dns_metrics_listener/dns_metrics_listener.h
+++ b/tests/dns_metrics_listener/dns_metrics_listener.h
@@ -18,6 +18,7 @@
 
 #include <condition_variable>
 #include <map>
+#include <queue>
 #include <utility>
 
 #include <android-base/thread_annotations.h>
@@ -38,6 +39,20 @@
 // verify the event count, the event change order, and so on.
 class DnsMetricsListener : public BaseMetricsListener {
   public:
+    struct DnsEvent {
+        int32_t netId;
+        int32_t eventType;
+        int32_t returnCode;
+        std::string hostname;
+        std::vector<std::string> ipAddresses;
+        int32_t ipAddressesCount;
+
+        bool operator==(const DnsEvent& o) const;
+
+        // Informative for debugging.
+        friend std::ostream& operator<<(std::ostream& os, const DnsEvent& data);
+    };
+
     DnsMetricsListener() = delete;
     DnsMetricsListener(int32_t netId) : mNetId(netId){};
 
@@ -50,6 +65,11 @@
                                                      const std::string& /*hostname*/,
                                                      bool validated) override;
 
+    ::ndk::ScopedAStatus onDnsEvent(int32_t netId, int32_t eventType, int32_t returnCode,
+                                    int32_t /*latencyMs*/, const std::string& hostname,
+                                    const std::vector<std::string>& ipAddresses,
+                                    int32_t ipAddressesCount, int32_t /*uid*/) override;
+
     // Wait for expected NAT64 prefix status until timeout.
     bool waitForNat64Prefix(ExpectNat64PrefixStatus status, std::chrono::milliseconds timeout)
             EXCLUDES(mMutex);
@@ -70,10 +90,15 @@
         return mValidationRecords.find({mNetId, serverAddr}) != mValidationRecords.end();
     }
 
+    std::optional<DnsEvent> popDnsEvent() EXCLUDES(mMutex);
+
     void reset() EXCLUDES(mMutex) {
         std::lock_guard lock(mMutex);
         mUnexpectedNat64PrefixUpdates = 0;
         mValidationRecords.clear();
+
+        std::queue<DnsEvent> emptyQueue;
+        std::swap(mDnsEventRecords, emptyQueue);
     }
 
   private:
@@ -99,6 +124,9 @@
     // Used to store the data from onPrivateDnsValidationEvent.
     std::map<ServerKey, bool> mValidationRecords GUARDED_BY(mMutex);
 
+    // Used to store the data from onDnsEvent.
+    std::queue<DnsEvent> mDnsEventRecords GUARDED_BY(mMutex);
+
     mutable std::mutex mMutex;
     std::condition_variable mCv;
 };
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index 236b031..fddb96e 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -61,6 +61,7 @@
 #include "ResolverStats.h"
 #include "netid_client.h"  // NETID_UNSET
 #include "params.h"        // MAXNS
+#include "stats.h"         // RCODE_TIMEOUT
 #include "test_utils.h"
 #include "tests/dns_metrics_listener/dns_metrics_listener.h"
 #include "tests/dns_responder/dns_responder.h"
@@ -84,6 +85,7 @@
 using aidl::android::net::IDnsResolver;
 using aidl::android::net::INetd;
 using aidl::android::net::ResolverParamsParcel;
+using aidl::android::net::metrics::INetdEventListener;
 using android::base::ParseInt;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -230,6 +232,16 @@
         return sDnsMetricsListener->findValidationRecord(serverAddr);
     }
 
+    void ExpectDnsEvent(int32_t eventType, int32_t returnCode, const std::string& hostname,
+                        const std::vector<std::string>& ipAddresses) {
+        const DnsMetricsListener::DnsEvent expect = {
+                TEST_NETID, eventType,   returnCode,
+                hostname,   ipAddresses, static_cast<int32_t>(ipAddresses.size())};
+        const auto dnsEvent = sDnsMetricsListener->popDnsEvent();
+        ASSERT_TRUE(dnsEvent.has_value());
+        EXPECT_EQ(dnsEvent.value(), expect);
+    }
+
     bool expectStatsFromGetResolverInfo(const std::vector<NameserverStats>& nameserversStats) {
         std::vector<std::string> res_servers;
         std::vector<std::string> res_domains;
@@ -953,6 +965,7 @@
     EXPECT_TRUE(result != nullptr);
     EXPECT_EQ(1U, GetNumQueries(dns0, host_name1));
     EXPECT_EQ(1U, GetNumQueries(dns1, host_name1));
+    ExpectDnsEvent(INetdEventListener::EVENT_GETADDRINFO, 0, host_name1, {"2001:db8::6"});
 
     // Now make dns1 also ignore 100% requests... The resolve should alternate
     // queries between the nameservers and fail
@@ -962,6 +975,7 @@
     EXPECT_EQ(nullptr, result2);
     EXPECT_EQ(1U, GetNumQueries(dns0, host_name2));
     EXPECT_EQ(1U, GetNumQueries(dns1, host_name2));
+    ExpectDnsEvent(INetdEventListener::EVENT_GETADDRINFO, RCODE_TIMEOUT, host_name2, {});
 }
 
 TEST_F(ResolverTest, GetAddrInfoV6_concurrent) {
@@ -2170,6 +2184,12 @@
     res = getAsyncResponse(fd1, &rcode, buf, MAXPACKET);
     EXPECT_GT(res, 0);
     EXPECT_EQ("::1.2.3.4", toString(buf, res, AF_INET6));
+
+    // Trailing dot is removed. Is it intended?
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, 0, "howdy.example.com", {"::1.2.3.4"});
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, RCODE_TIMEOUT, "howdy.example.com", {});
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, RCODE_TIMEOUT, "howdy.example.com", {});
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, 0, "howdy.example.com", {"1.2.3.4"});
 }
 
 TEST_F(ResolverTest, Async_MalformedQuery) {
@@ -2452,6 +2472,8 @@
     // expect no response
     expectAnswersNotValid(fd1, -ETIMEDOUT);
     expectAnswersNotValid(fd2, -ETIMEDOUT);
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, RCODE_TIMEOUT, "howdy.example.com", {});
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, RCODE_TIMEOUT, "howdy.example.com", {});
 
     // No retry case, expect total 2 queries. The server is selected randomly.
     EXPECT_EQ(2U, GetNumQueries(dns0, host_name) + GetNumQueries(dns1, host_name));
@@ -2468,6 +2490,8 @@
     // expect no response
     expectAnswersNotValid(fd1, -ETIMEDOUT);
     expectAnswersNotValid(fd2, -ETIMEDOUT);
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, RCODE_TIMEOUT, "howdy.example.com", {});
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, RCODE_TIMEOUT, "howdy.example.com", {});
 
     // Retry case, expect 4 queries
     EXPECT_EQ(4U, GetNumQueries(dns0, host_name));
@@ -2707,10 +2731,13 @@
                 ASSERT_FALSE(h_result->h_addr_list[0] == nullptr);
                 EXPECT_EQ(ADDR4, ToString(h_result));
                 EXPECT_TRUE(h_result->h_addr_list[1] == nullptr);
+                ExpectDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, 0, host_name, {ADDR4});
             } else {
                 EXPECT_EQ(0U, GetNumQueriesForType(dns, ns_type::ns_t_a, host_name));
                 ASSERT_TRUE(h_result == nullptr);
                 ASSERT_EQ(HOST_NOT_FOUND, h_errno);
+                int returnCode = (config.edns == Edns::DROP) ? RCODE_TIMEOUT : EAI_FAIL;
+                ExpectDnsEvent(INetdEventListener::EVENT_GETHOSTBYNAME, returnCode, host_name, {});
             }
         } else if (config.method == GETADDRINFO) {
             ScopedAddrinfo ai_result;
@@ -2721,9 +2748,12 @@
                 EXPECT_EQ(1U, GetNumQueries(dns, host_name));
                 const std::string result_str = ToString(ai_result);
                 EXPECT_EQ(ADDR4, result_str);
+                ExpectDnsEvent(INetdEventListener::EVENT_GETADDRINFO, 0, host_name, {ADDR4});
             } else {
                 EXPECT_TRUE(ai_result == nullptr);
                 EXPECT_EQ(0U, GetNumQueries(dns, host_name));
+                int returnCode = (config.edns == Edns::DROP) ? RCODE_TIMEOUT : EAI_FAIL;
+                ExpectDnsEvent(INetdEventListener::EVENT_GETADDRINFO, returnCode, host_name, {});
             }
         } else {
             FAIL() << "Unsupported query method: " << config.method;
@@ -4016,6 +4046,9 @@
     memset(buf, 0, MAXPACKET);
     res = getAsyncResponse(fd1, &rcode, buf, MAXPACKET);
     EXPECT_EQ(-ECONNREFUSED, res);
+
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, EAI_SYSTEM, "howdy.example.com", {});
+    ExpectDnsEvent(INetdEventListener::EVENT_RES_NSEND, EAI_SYSTEM, "howdy.example.com", {});
 }
 
 namespace {
@@ -4932,6 +4965,7 @@
     EXPECT_NEAR(DNS_TIMEOUT_MS, timeTakenMs, TIMING_TOLERANCE_MS)
             << "took time should approximate equal timeout";
     EXPECT_EQ(2U, GetNumQueries(neverRespondDns, host_name));
+    ExpectDnsEvent(INetdEventListener::EVENT_GETADDRINFO, RCODE_TIMEOUT, host_name, {});
 }
 
 TEST_F(ResolverTest, GetAddrInfoParallelLookupSleepTime) {