Merge "Enable DNS resolver tests to be included in android-mts-dnsresolver as well as android-mts."
diff --git a/Android.bp b/Android.bp
index 5963362..9800f36 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,8 @@
"binder/android/net/ResolverHostsParcel.aidl",
"binder/android/net/ResolverOptionsParcel.aidl",
"binder/android/net/ResolverParamsParcel.aidl",
+ // New AIDL classes should go into android.net.resolv.aidl so they can be clearly identified
+ "binder/android/net/resolv/aidl/**/*.aidl",
],
imports: [
"netd_event_listener_interface",
@@ -54,6 +56,7 @@
"4",
"5",
"6",
+ "7",
],
}
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index d7fbcfc..2f3ebb0 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -62,6 +62,8 @@
#include "stats.pb.h"
using aidl::android::net::metrics::INetdEventListener;
+using aidl::android::net::resolv::aidl::DnsHealthEventParcel;
+using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
using android::net::NetworkDnsEventReported;
namespace android {
@@ -347,16 +349,43 @@
maybeLogQuery(eventType, netContext, event, query_name, ip_addrs);
const auto& listeners = ResolverEventReporter::getInstance().getListeners();
- if (listeners.size() == 0) {
+ if (listeners.empty()) {
LOG(ERROR) << __func__
<< ": DNS event not sent since no INetdEventListener receiver is available.";
- return;
}
const int latencyMs = latencyUs / 1000;
for (const auto& it : listeners) {
it->onDnsEvent(netContext.dns_netid, eventType, returnCode, latencyMs, query_name, ip_addrs,
total_ip_addr_count, netContext.uid);
}
+
+ const auto& unsolEventListeners = ResolverEventReporter::getInstance().getUnsolEventListeners();
+
+ if (returnCode == NETD_RESOLV_TIMEOUT) {
+ const DnsHealthEventParcel dnsHealthEvent = {
+ .netId = static_cast<int32_t>(netContext.dns_netid),
+ .healthResult = IDnsResolverUnsolicitedEventListener::DNS_HEALTH_RESULT_TIMEOUT,
+ };
+ for (const auto& it : unsolEventListeners) {
+ it->onDnsHealthEvent(dnsHealthEvent);
+ }
+ } else if (returnCode == NOERROR) {
+ DnsHealthEventParcel dnsHealthEvent = {
+ .netId = static_cast<int32_t>(netContext.dns_netid),
+ .healthResult = IDnsResolverUnsolicitedEventListener::DNS_HEALTH_RESULT_OK,
+ };
+ for (const auto& query : event.dns_query_events().dns_query_event()) {
+ if (query.cache_hit() != CS_FOUND && query.rcode() == NS_R_NO_ERROR) {
+ dnsHealthEvent.successRttMicros.push_back(query.latency_micros());
+ }
+ }
+
+ if (!dnsHealthEvent.successRttMicros.empty()) {
+ for (const auto& it : unsolEventListeners) {
+ it->onDnsHealthEvent(dnsHealthEvent);
+ }
+ }
+ }
}
bool onlyIPv4Answers(const addrinfo* res) {
diff --git a/DnsQueryLog.cpp b/DnsQueryLog.cpp
index 6f0e179..9cc3ca4 100644
--- a/DnsQueryLog.cpp
+++ b/DnsQueryLog.cpp
@@ -17,7 +17,7 @@
#include "DnsQueryLog.h"
-#include <android-base/stringprintf.h>
+#include "util.h"
namespace android::net {
@@ -45,25 +45,10 @@
return ret.empty() ? "" : ret.substr(0, ret.length() - 2);
}
-// Return the readable string format "hr:min:sec.ms".
-std::string timestampToString(const std::chrono::system_clock::time_point& ts) {
- using std::chrono::duration_cast;
- using std::chrono::milliseconds;
- const auto time_sec = std::chrono::system_clock::to_time_t(ts);
- char buf[32];
- std::strftime(buf, sizeof(buf), "%H:%M:%S", std::localtime(&time_sec));
- int ms = duration_cast<milliseconds>(ts.time_since_epoch()).count() % 1000;
- return android::base::StringPrintf("%s.%03d", buf, ms);
-}
-
} // namespace
void DnsQueryLog::push(Record&& record) {
- std::lock_guard guard(mLock);
- mQueue.push_back(std::move(record));
- if (mQueue.size() > mCapacity) {
- mQueue.pop_front();
- }
+ mQueue.push(std::move(record));
}
void DnsQueryLog::dump(netdutils::DumpWriter& dw) const {
@@ -71,8 +56,7 @@
netdutils::ScopedIndent indentStats(dw);
const auto now = std::chrono::system_clock::now();
- std::lock_guard guard(mLock);
- for (const auto& record : mQueue) {
+ for (const auto& record : mQueue.copy()) {
if (now - record.timestamp > mValidityTimeMs) continue;
const std::string maskedHostname = maskHostname(record.hostname);
diff --git a/DnsQueryLog.h b/DnsQueryLog.h
index c19f8db..3e6478e 100644
--- a/DnsQueryLog.h
+++ b/DnsQueryLog.h
@@ -17,16 +17,16 @@
#pragma once
-#include <deque>
#include <string>
#include <vector>
-#include <android-base/thread_annotations.h>
#include <netdutils/DumpWriter.h>
+#include "LockedQueue.h"
+
namespace android::net {
-// A circular buffer based class used for query logging. It's thread-safe for concurrent access.
+// This class stores query records in a locked ring buffer. It's thread-safe for concurrent access.
class DnsQueryLog {
public:
static constexpr std::string_view DUMP_KEYWORD = "querylog";
@@ -52,15 +52,13 @@
// Allow the tests to set the capacity and the validaty time in milliseconds.
DnsQueryLog(size_t size = kDefaultLogSize,
std::chrono::milliseconds time = kDefaultValidityMinutes)
- : mCapacity(size), mValidityTimeMs(time) {}
+ : mQueue(size), mValidityTimeMs(time) {}
- void push(Record&& record) EXCLUDES(mLock);
- void dump(netdutils::DumpWriter& dw) const EXCLUDES(mLock);
+ void push(Record&& record);
+ void dump(netdutils::DumpWriter& dw) const;
private:
- mutable std::mutex mLock;
- std::deque<Record> mQueue GUARDED_BY(mLock);
- const size_t mCapacity;
+ LockedRingBuffer<Record> mQueue;
const std::chrono::milliseconds mValidityTimeMs;
// The capacity of the circular buffer.
diff --git a/DnsResolver.cpp b/DnsResolver.cpp
index 5068aef..fcd4293 100644
--- a/DnsResolver.cpp
+++ b/DnsResolver.cpp
@@ -20,6 +20,8 @@
#include "DnsProxyListener.h"
#include "DnsResolverService.h"
+#include "DnsTlsDispatcher.h"
+#include "PrivateDnsConfiguration.h"
#include "netd_resolv/resolv.h"
#include "res_debug.h"
#include "util.h"
@@ -28,7 +30,8 @@
android::base::InitLogging(/*argv=*/nullptr);
android::base::SetDefaultTag("libnetd_resolv");
LOG(INFO) << __func__ << ": Initializing resolver";
- resolv_set_log_severity(android::base::WARNING);
+ // TODO(b/170539625): restore log level to WARNING after clarifying flaky tests.
+ resolv_set_log_severity(isUserDebugBuild() ? android::base::DEBUG : android::base::WARNING);
using android::net::gApiLevel;
gApiLevel = getApiLevel();
using android::net::gResNetdCallbacks;
@@ -72,6 +75,14 @@
return &instance;
}
+DnsResolver::DnsResolver() {
+ // TODO: make them member variables after fixing the circular dependency:
+ // DnsTlsDispatcher.h -> resolv_private.h -> DnsResolver.h -> DnsTlsDispatcher.h
+ auto& dnsTlsDispatcher = DnsTlsDispatcher::getInstance();
+ auto& privateDnsConfiguration = PrivateDnsConfiguration::getInstance();
+ privateDnsConfiguration.setObserver(&dnsTlsDispatcher);
+}
+
bool DnsResolver::start() {
if (!verifyCallbacks()) {
LOG(ERROR) << __func__ << ": Callback verification failed";
diff --git a/DnsResolver.h b/DnsResolver.h
index 863b096..9c2f3d8 100644
--- a/DnsResolver.h
+++ b/DnsResolver.h
@@ -40,7 +40,8 @@
ResolverController resolverCtrl;
private:
- DnsResolver() {}
+ DnsResolver();
+
DnsProxyListener mDnsProxyListener;
DnsQueryLog mQueryLog;
};
diff --git a/DnsResolverService.cpp b/DnsResolverService.cpp
index af449ab..899f726 100644
--- a/DnsResolverService.cpp
+++ b/DnsResolverService.cpp
@@ -32,6 +32,7 @@
#include "DnsResolver.h"
#include "Experiments.h"
#include "NetdPermissions.h" // PERM_*
+#include "PrivateDnsConfiguration.h"
#include "ResolverEventReporter.h"
#include "resolv_cache.h"
@@ -117,6 +118,8 @@
gDnsResolv->resolverCtrl.dump(dw, netId);
dw.blankline();
}
+
+ PrivateDnsConfiguration::getInstance().dump(dw);
Experiments::getInstance()->dump(dw);
return STATUS_OK;
}
@@ -138,6 +141,16 @@
return statusFromErrcode(res);
}
+::ndk::ScopedAStatus DnsResolverService::registerUnsolicitedEventListener(
+ const std::shared_ptr<
+ aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener>& listener) {
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+ int res = ResolverEventReporter::getInstance().addUnsolEventListener(listener);
+
+ return statusFromErrcode(res);
+}
+
::ndk::ScopedAStatus DnsResolverService::checkAnyPermission(
const std::vector<const char*>& permissions) {
// TODO: Remove callback and move this to unnamed namespace after libbiner_ndk supports
diff --git a/DnsResolverService.h b/DnsResolverService.h
index 2228e21..fe39301 100644
--- a/DnsResolverService.h
+++ b/DnsResolverService.h
@@ -39,6 +39,10 @@
::ndk::ScopedAStatus registerEventListener(
const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener)
override;
+ ::ndk::ScopedAStatus registerUnsolicitedEventListener(
+ const std::shared_ptr<
+ aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener>&
+ listener) override;
// Resolver commands.
::ndk::ScopedAStatus setResolverConfiguration(
diff --git a/DnsTlsDispatcher.h b/DnsTlsDispatcher.h
index c3dad06..2cb7519 100644
--- a/DnsTlsDispatcher.h
+++ b/DnsTlsDispatcher.h
@@ -28,6 +28,7 @@
#include "DnsTlsServer.h"
#include "DnsTlsTransport.h"
#include "IDnsTlsSocketFactory.h"
+#include "PrivateDnsValidationObserver.h"
#include "resolv_private.h"
namespace android {
@@ -35,7 +36,7 @@
// This is a singleton class that manages the collection of active DnsTlsTransports.
// Queries made here are dispatched to an existing or newly constructed DnsTlsTransport.
-class DnsTlsDispatcher {
+class DnsTlsDispatcher : public PrivateDnsValidationObserver {
public:
// Constructor with dependency injection for testing.
explicit DnsTlsDispatcher(std::unique_ptr<IDnsTlsSocketFactory> factory)
@@ -60,6 +61,9 @@
const netdutils::Slice query, const netdutils::Slice ans,
int* _Nonnull resplen, bool* _Nonnull connectTriggered);
+ // Implement PrivateDnsValidationObserver.
+ void onValidationStateUpdate(const std::string&, Validation, uint32_t) override{};
+
private:
DnsTlsDispatcher();
diff --git a/DnsTlsServer.h b/DnsTlsServer.h
index 6a72d88..67b0012 100644
--- a/DnsTlsServer.h
+++ b/DnsTlsServer.h
@@ -24,19 +24,11 @@
#include <params.h>
+#include "PrivateDnsCommon.h"
+
namespace android {
namespace net {
-// Validation status of a DNS over TLS server (on a specific netId).
-enum class Validation : uint8_t {
- in_process,
- success,
- success_but_expired,
- fail,
- unknown_server,
- unknown_netid,
-};
-
// DnsTlsServer represents a recursive resolver that supports, or may support, a
// secure protocol.
struct DnsTlsServer {
@@ -74,6 +66,12 @@
Validation validationState() const { return mValidation; }
void setValidationState(Validation val) { mValidation = val; }
+ // The socket mark used for validation.
+ // Note that the mark of a connection to which the DnsResolver sends app's DNS requests can
+ // be different.
+ // TODO: make it const.
+ uint32_t mark = 0;
+
// Return whether or not the server can be used for a network. It depends on
// the resolver configuration.
bool active() const { return mActive; }
diff --git a/DnsTlsTransport.cpp b/DnsTlsTransport.cpp
index bcbb0a1..a2964cc 100644
--- a/DnsTlsTransport.cpp
+++ b/DnsTlsTransport.cpp
@@ -158,8 +158,8 @@
// static
// TODO: Use this function to preheat the session cache.
// That may require moving it to DnsTlsDispatcher.
-bool DnsTlsTransport::validate(const DnsTlsServer& server, unsigned netid, uint32_t mark) {
- LOG(DEBUG) << "Beginning validation on " << netid;
+bool DnsTlsTransport::validate(const DnsTlsServer& server, uint32_t mark) {
+ LOG(DEBUG) << "Beginning validation with mark " << std::hex << mark;
// Generate "<random>-dnsotls-ds.metric.gstatic.com", which we will lookup through |ss| in
// order to prove that it is actually a working DNS over TLS server.
static const char kDnsSafeChars[] =
@@ -195,7 +195,7 @@
DnsTlsTransport transport(server, mark, &factory);
auto r = transport.query(netdutils::Slice(query, qlen)).get();
if (r.code != Response::success) {
- LOG(DEBUG) << "query failed";
+ LOG(WARNING) << "query failed";
return false;
}
@@ -212,7 +212,7 @@
}
const int ancount = (recvbuf[6] << 8) | recvbuf[7];
- LOG(DEBUG) << netid << " answer count: " << ancount;
+ LOG(DEBUG) << "answer count: " << ancount;
// TODO: Further validate the response contents (check for valid AAAA record, ...).
// Note that currently, integration tests rely on this function accepting a
diff --git a/DnsTlsTransport.h b/DnsTlsTransport.h
index c0fbaef..7b7f81f 100644
--- a/DnsTlsTransport.h
+++ b/DnsTlsTransport.h
@@ -52,10 +52,10 @@
// Given a |query|, this method sends it to the server and returns the result asynchronously.
std::future<Result> query(const netdutils::Slice query) EXCLUDES(mLock);
- // Check that a given TLS server is fully working on the specified netid.
+ // Check that a given TLS server is fully working with a specified mark.
// This function is used in ResolverController to ensure that we don't enable DNS over TLS
// on networks where it doesn't actually work.
- static bool validate(const DnsTlsServer& server, unsigned netid, uint32_t mark);
+ static bool validate(const DnsTlsServer& server, uint32_t mark);
int getConnectCounter() const EXCLUDES(mLock);
diff --git a/LockedQueue.h b/LockedQueue.h
index 0481eda..8694f2d 100644
--- a/LockedQueue.h
+++ b/LockedQueue.h
@@ -46,6 +46,30 @@
std::deque<T> mQueue GUARDED_BY(mLock);
};
+template <typename T>
+class LockedRingBuffer {
+ public:
+ explicit LockedRingBuffer(size_t size) : mCapacity(size) {}
+
+ void push(T&& record) {
+ std::lock_guard guard(mLock);
+ mQueue.push_back(std::move(record));
+ if (mQueue.size() > mCapacity) {
+ mQueue.pop_front();
+ }
+ }
+
+ std::deque<T> copy() const {
+ std::lock_guard guard(mLock);
+ return mQueue;
+ }
+
+ private:
+ mutable std::mutex mLock;
+ const size_t mCapacity;
+ std::deque<T> mQueue GUARDED_BY(mLock);
+};
+
} // end of namespace net
} // end of namespace android
diff --git a/PrivateDnsCommon.h b/PrivateDnsCommon.h
new file mode 100644
index 0000000..229e97c
--- /dev/null
+++ b/PrivateDnsCommon.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::net {
+
+// Validation status of a private DNS server on a specific netId.
+enum class Validation : uint8_t {
+ in_process,
+ success,
+ success_but_expired,
+ fail,
+ unknown_server,
+ unknown_netid,
+};
+
+// The private DNS mode on a specific netId.
+enum class PrivateDnsMode : uint8_t {
+ OFF,
+ OPPORTUNISTIC,
+ STRICT,
+};
+
+constexpr const char* validationStatusToString(Validation value) {
+ switch (value) {
+ case Validation::in_process:
+ return "in_process";
+ case Validation::success:
+ return "success";
+ case Validation::success_but_expired:
+ return "success_but_expired";
+ case Validation::fail:
+ return "fail";
+ case Validation::unknown_server:
+ return "unknown_server";
+ case Validation::unknown_netid:
+ return "unknown_netid";
+ default:
+ return "unknown_status";
+ }
+}
+
+constexpr const char* getPrivateDnsModeString(PrivateDnsMode mode) {
+ switch (mode) {
+ case PrivateDnsMode::OFF:
+ return "OFF";
+ case PrivateDnsMode::OPPORTUNISTIC:
+ return "OPPORTUNISTIC";
+ case PrivateDnsMode::STRICT:
+ return "STRICT";
+ }
+}
+
+} // namespace android::net
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp
index 47969b3..c8b070a 100644
--- a/PrivateDnsConfiguration.cpp
+++ b/PrivateDnsConfiguration.cpp
@@ -18,6 +18,7 @@
#include "PrivateDnsConfiguration.h"
+#include <android-base/format.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <netdutils/ThreadUtil.h>
@@ -29,6 +30,8 @@
#include "netdutils/BackoffSequence.h"
#include "util.h"
+using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
+using aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel;
using android::base::StringPrintf;
using android::netdutils::setThreadName;
using std::chrono::milliseconds;
@@ -70,6 +73,7 @@
DnsTlsServer server(parsed);
server.name = name;
server.certificate = caCert;
+ server.mark = mark;
tmp[ServerIdentity(server)] = server;
}
@@ -106,7 +110,7 @@
if (needsValidation(server)) {
updateServerState(identity, Validation::in_process, netId);
- startValidation(server, netId, mark);
+ startValidation(server, netId);
}
}
@@ -140,10 +144,41 @@
mPrivateDnsTransports.erase(netId);
}
-void PrivateDnsConfiguration::startValidation(const DnsTlsServer& server, unsigned netId,
- uint32_t mark) REQUIRES(mPrivateDnsLock) {
+bool PrivateDnsConfiguration::requestValidation(unsigned netId, const DnsTlsServer& server,
+ uint32_t mark) {
+ std::lock_guard guard(mPrivateDnsLock);
+ auto netPair = mPrivateDnsTransports.find(netId);
+ if (netPair == mPrivateDnsTransports.end()) {
+ return false;
+ }
+
+ auto& tracker = netPair->second;
+ const ServerIdentity identity = ServerIdentity(server);
+ auto it = tracker.find(identity);
+ if (it == tracker.end()) {
+ return false;
+ }
+
+ const DnsTlsServer& target = it->second;
+
+ if (!target.active()) return false;
+
+ if (target.validationState() != Validation::success) return false;
+
+ // Don't run the validation if |mark| (from android_net_context.dns_mark) is different.
+ // This is to protect validation from running on unexpected marks.
+ // Validation should be associated with a mark gotten by system permission.
+ if (target.mark != mark) return false;
+
+ updateServerState(identity, Validation::in_process, netId);
+ startValidation(target, netId);
+ return true;
+}
+
+void PrivateDnsConfiguration::startValidation(const DnsTlsServer& server, unsigned netId)
+ REQUIRES(mPrivateDnsLock) {
// Note that capturing |server| and |netId| in this lambda create copies.
- std::thread validate_thread([this, server, netId, mark] {
+ std::thread validate_thread([this, server, netId] {
setThreadName(StringPrintf("TlsVerify_%u", netId).c_str());
// cat /proc/sys/net/ipv4/tcp_syn_retries yields "6".
@@ -167,10 +202,11 @@
while (true) {
// ::validate() is a blocking call that performs network operations.
// It can take milliseconds to minutes, up to the SYN retry limit.
- LOG(WARNING) << "Validating DnsTlsServer on netId " << netId;
- const bool success = DnsTlsTransport::validate(server, netId, mark);
- LOG(DEBUG) << "validateDnsTlsServer returned " << success << " for "
- << server.toIpString();
+ LOG(WARNING) << "Validating DnsTlsServer " << server.toIpString() << " with mark 0x"
+ << std::hex << server.mark;
+ const bool success = DnsTlsTransport::validate(server, server.mark);
+ LOG(WARNING) << "validateDnsTlsServer returned " << success << " for "
+ << server.toIpString();
const bool needs_reeval = this->recordPrivateDnsValidation(server, netId, success);
if (!needs_reeval) {
@@ -188,6 +224,35 @@
validate_thread.detach();
}
+void PrivateDnsConfiguration::sendPrivateDnsValidationEvent(const DnsTlsServer& server,
+ unsigned netId, bool success) {
+ LOG(DEBUG) << "Sending validation " << (success ? "success" : "failure") << " event on netId "
+ << netId << " for " << server.toIpString() << " with hostname {" << server.name
+ << "}";
+ // Send a validation event to NetdEventListenerService.
+ const auto& listeners = ResolverEventReporter::getInstance().getListeners();
+ if (listeners.empty()) {
+ LOG(ERROR)
+ << "Validation event not sent since no INetdEventListener receiver is available.";
+ }
+ for (const auto& it : listeners) {
+ it->onPrivateDnsValidationEvent(netId, server.toIpString(), server.name, success);
+ }
+
+ // Send a validation event to unsolicited event listeners.
+ const auto& unsolEventListeners = ResolverEventReporter::getInstance().getUnsolEventListeners();
+ const PrivateDnsValidationEventParcel validationEvent = {
+ .netId = static_cast<int32_t>(netId),
+ .ipAddress = server.toIpString(),
+ .hostname = server.name,
+ .validation = success ? IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_SUCCESS
+ : IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_FAILURE,
+ };
+ for (const auto& it : unsolEventListeners) {
+ it->onPrivateDnsValidationEvent(validationEvent);
+ }
+}
+
bool PrivateDnsConfiguration::recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId,
bool success) {
constexpr bool NEEDS_REEVALUATION = true;
@@ -199,14 +264,14 @@
auto netPair = mPrivateDnsTransports.find(netId);
if (netPair == mPrivateDnsTransports.end()) {
LOG(WARNING) << "netId " << netId << " was erased during private DNS validation";
- maybeNotifyObserver(identity.ip.toString(), Validation::fail, netId);
+ notifyValidationStateUpdate(identity.ip.toString(), Validation::fail, netId);
return DONT_REEVALUATE;
}
const auto mode = mPrivateDnsModes.find(netId);
if (mode == mPrivateDnsModes.end()) {
LOG(WARNING) << "netId " << netId << " has no private DNS validation mode";
- maybeNotifyObserver(identity.ip.toString(), Validation::fail, netId);
+ notifyValidationStateUpdate(identity.ip.toString(), Validation::fail, netId);
return DONT_REEVALUATE;
}
const bool modeDoesReevaluation = (mode->second == PrivateDnsMode::STRICT);
@@ -234,19 +299,8 @@
reevaluationStatus = DONT_REEVALUATE;
}
- // Send a validation event to NetdEventListenerService.
- const auto& listeners = ResolverEventReporter::getInstance().getListeners();
- if (listeners.size() != 0) {
- for (const auto& it : listeners) {
- it->onPrivateDnsValidationEvent(netId, server.toIpString(), server.name, success);
- }
- LOG(DEBUG) << "Sent validation " << (success ? "success" : "failure") << " event on netId "
- << netId << " for " << server.toIpString() << " with hostname {" << server.name
- << "}";
- } else {
- LOG(ERROR)
- << "Validation event not sent since no INetdEventListener receiver is available.";
- }
+ // Send private dns validation result to listeners.
+ sendPrivateDnsValidationEvent(server, netId, success);
if (success) {
updateServerState(identity, Validation::success, netId);
@@ -267,18 +321,21 @@
uint32_t netId) {
auto netPair = mPrivateDnsTransports.find(netId);
if (netPair == mPrivateDnsTransports.end()) {
- maybeNotifyObserver(identity.ip.toString(), Validation::fail, netId);
+ notifyValidationStateUpdate(identity.ip.toString(), Validation::fail, netId);
return;
}
auto& tracker = netPair->second;
if (tracker.find(identity) == tracker.end()) {
- maybeNotifyObserver(identity.ip.toString(), Validation::fail, netId);
+ notifyValidationStateUpdate(identity.ip.toString(), Validation::fail, netId);
return;
}
tracker[identity].setValidationState(state);
- maybeNotifyObserver(identity.ip.toString(), state, netId);
+ notifyValidationStateUpdate(identity.ip.toString(), state, netId);
+
+ RecordEntry record(netId, identity, state);
+ mPrivateDnsLog.push(std::move(record));
}
bool PrivateDnsConfiguration::needsValidation(const DnsTlsServer& server) {
@@ -297,17 +354,31 @@
return false;
}
-void PrivateDnsConfiguration::setObserver(Observer* observer) {
+void PrivateDnsConfiguration::setObserver(PrivateDnsValidationObserver* observer) {
std::lock_guard guard(mPrivateDnsLock);
mObserver = observer;
}
-void PrivateDnsConfiguration::maybeNotifyObserver(const std::string& serverIp,
- Validation validation, uint32_t netId) const {
+void PrivateDnsConfiguration::notifyValidationStateUpdate(const std::string& serverIp,
+ Validation validation,
+ uint32_t netId) const {
if (mObserver) {
mObserver->onValidationStateUpdate(serverIp, validation, netId);
}
}
+void PrivateDnsConfiguration::dump(netdutils::DumpWriter& dw) const {
+ dw.println("PrivateDnsLog:");
+ netdutils::ScopedIndent indentStats(dw);
+
+ for (const auto& record : mPrivateDnsLog.copy()) {
+ dw.println(fmt::format("{} - netId={} PrivateDns={{{}/{}}} state={}",
+ timestampToString(record.timestamp), record.netId,
+ record.serverIdentity.ip.toString(), record.serverIdentity.name,
+ validationStatusToString(record.state)));
+ }
+ dw.blankline();
+}
+
} // namespace net
} // namespace android
diff --git a/PrivateDnsConfiguration.h b/PrivateDnsConfiguration.h
index 722ed71..04f6a47 100644
--- a/PrivateDnsConfiguration.h
+++ b/PrivateDnsConfiguration.h
@@ -22,16 +22,16 @@
#include <vector>
#include <android-base/thread_annotations.h>
+#include <netdutils/DumpWriter.h>
#include <netdutils/InternetAddresses.h>
#include "DnsTlsServer.h"
+#include "LockedQueue.h"
+#include "PrivateDnsValidationObserver.h"
namespace android {
namespace net {
-// The DNS over TLS mode on a specific netId.
-enum class PrivateDnsMode : uint8_t { OFF, OPPORTUNISTIC, STRICT };
-
struct PrivateDnsStatus {
PrivateDnsMode mode;
@@ -65,6 +65,11 @@
void clear(unsigned netId) EXCLUDES(mPrivateDnsLock);
+ // Request |server| to be revalidated on a connection tagged with |mark|.
+ // Return true if the request is accepted; otherwise, return false.
+ bool requestValidation(unsigned netId, const DnsTlsServer& server, uint32_t mark)
+ EXCLUDES(mPrivateDnsLock);
+
struct ServerIdentity {
const netdutils::IPAddress ip;
const std::string name;
@@ -83,18 +88,24 @@
}
};
+ void setObserver(PrivateDnsValidationObserver* observer);
+
+ void dump(netdutils::DumpWriter& dw) const;
+
private:
typedef std::map<ServerIdentity, DnsTlsServer> PrivateDnsTracker;
typedef std::set<DnsTlsServer, AddressComparator> ThreadTracker;
PrivateDnsConfiguration() = default;
- void startValidation(const DnsTlsServer& server, unsigned netId, uint32_t mark)
- REQUIRES(mPrivateDnsLock);
+ void startValidation(const DnsTlsServer& server, unsigned netId) REQUIRES(mPrivateDnsLock);
bool recordPrivateDnsValidation(const DnsTlsServer& server, unsigned netId, bool success)
EXCLUDES(mPrivateDnsLock);
+ void sendPrivateDnsValidationEvent(const DnsTlsServer& server, unsigned netId, bool success)
+ REQUIRES(mPrivateDnsLock);
+
// Decide if a validation for |server| is needed. Note that servers that have failed
// multiple validation attempts but for which there is still a validating
// thread running are marked as being Validation::in_process.
@@ -112,26 +123,25 @@
// Any pending validation threads will continue running because we have no way to cancel them.
std::map<unsigned, PrivateDnsTracker> mPrivateDnsTransports GUARDED_BY(mPrivateDnsLock);
- // For testing. The observer is notified of onValidationStateUpdate 1) when a validation is
- // about to begin or 2) when a validation finishes. If a validation finishes when in OFF mode
- // or when the network has been destroyed, |validation| will be Validation::fail.
- // WARNING: The Observer is notified while the lock is being held. Be careful not to call
- // any method of PrivateDnsConfiguration from the observer.
+ void notifyValidationStateUpdate(const std::string& serverIp, Validation validation,
+ uint32_t netId) const REQUIRES(mPrivateDnsLock);
+
// TODO: fix the reentrancy problem.
- class Observer {
- public:
- virtual ~Observer(){};
- virtual void onValidationStateUpdate(const std::string& serverIp, Validation validation,
- uint32_t netId) = 0;
- };
-
- void setObserver(Observer* observer);
- void maybeNotifyObserver(const std::string& serverIp, Validation validation,
- uint32_t netId) const REQUIRES(mPrivateDnsLock);
-
- Observer* mObserver GUARDED_BY(mPrivateDnsLock);
+ PrivateDnsValidationObserver* mObserver GUARDED_BY(mPrivateDnsLock);
friend class PrivateDnsConfigurationTest;
+
+ struct RecordEntry {
+ RecordEntry(uint32_t netId, const ServerIdentity& identity, Validation state)
+ : netId(netId), serverIdentity(identity), state(state) {}
+
+ const uint32_t netId;
+ const ServerIdentity serverIdentity;
+ const Validation state;
+ const std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
+ };
+
+ LockedRingBuffer<RecordEntry> mPrivateDnsLog{100};
};
} // namespace net
diff --git a/PrivateDnsConfigurationTest.cpp b/PrivateDnsConfigurationTest.cpp
index f290277..8da3efb 100644
--- a/PrivateDnsConfigurationTest.cpp
+++ b/PrivateDnsConfigurationTest.cpp
@@ -60,7 +60,7 @@
}
protected:
- class MockObserver : public PrivateDnsConfiguration::Observer {
+ class MockObserver : public PrivateDnsValidationObserver {
public:
MOCK_METHOD(void, onValidationStateUpdate,
(const std::string& serverIp, Validation validation, uint32_t netId),
@@ -254,6 +254,56 @@
EXPECT_NE(ServerIdentity(server), ServerIdentity(other));
}
+TEST_F(PrivateDnsConfigurationTest, RequestValidation) {
+ const DnsTlsServer server(netdutils::IPSockAddr::toIPSockAddr(kServer1, 853));
+
+ testing::InSequence seq;
+
+ for (const std::string_view config : {"SUCCESS", "IN_PROGRESS", "FAIL"}) {
+ SCOPED_TRACE(config);
+
+ EXPECT_CALL(mObserver, onValidationStateUpdate(kServer1, Validation::in_process, kNetId));
+ if (config == "SUCCESS") {
+ EXPECT_CALL(mObserver, onValidationStateUpdate(kServer1, Validation::success, kNetId));
+ } else if (config == "IN_PROGRESS") {
+ backend.setDeferredResp(true);
+ } else {
+ // config = "FAIL"
+ ASSERT_TRUE(backend.stopServer());
+ EXPECT_CALL(mObserver, onValidationStateUpdate(kServer1, Validation::fail, kNetId));
+ }
+ EXPECT_EQ(mPdc.set(kNetId, kMark, {kServer1}, {}, {}), 0);
+ expectPrivateDnsStatus(PrivateDnsMode::OPPORTUNISTIC);
+
+ // Wait until the validation state is transitioned.
+ const int runningThreads = (config == "IN_PROGRESS") ? 1 : 0;
+ ASSERT_TRUE(PollForCondition([&]() { return mObserver.runningThreads == runningThreads; }));
+
+ bool requestAccepted = false;
+ if (config == "SUCCESS") {
+ EXPECT_CALL(mObserver,
+ onValidationStateUpdate(kServer1, Validation::in_process, kNetId));
+ EXPECT_CALL(mObserver, onValidationStateUpdate(kServer1, Validation::success, kNetId));
+ requestAccepted = true;
+ } else if (config == "IN_PROGRESS") {
+ EXPECT_CALL(mObserver, onValidationStateUpdate(kServer1, Validation::success, kNetId));
+ }
+
+ EXPECT_EQ(mPdc.requestValidation(kNetId, server, kMark), requestAccepted);
+
+ // Resending the same request or requesting nonexistent servers are denied.
+ EXPECT_FALSE(mPdc.requestValidation(kNetId, server, kMark));
+ EXPECT_FALSE(mPdc.requestValidation(kNetId, server, kMark + 1));
+ EXPECT_FALSE(mPdc.requestValidation(kNetId + 1, server, kMark));
+
+ // Reset the test state.
+ backend.setDeferredResp(false);
+ backend.startServer();
+ ASSERT_TRUE(PollForCondition([&]() { return mObserver.runningThreads == 0; }));
+ mPdc.clear(kNetId);
+ }
+}
+
// TODO: add ValidationFail_Strict test.
} // namespace android::net
diff --git a/PrivateDnsValidationObserver.h b/PrivateDnsValidationObserver.h
new file mode 100644
index 0000000..b0cd5e2
--- /dev/null
+++ b/PrivateDnsValidationObserver.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "PrivateDnsCommon.h"
+
+namespace android::net {
+
+// The observer is notified of onValidationStateUpdate 1) when a validation is
+// about to begin or 2) when a validation finishes. If a validation finishes when in OFF mode
+// or when the network has been destroyed, |validation| will be Validation::fail.
+// WARNING: The Observer is notified while the lock is being held. Be careful not to call
+// any method of PrivateDnsConfiguration from the observer to cause reentrancy problem.
+class PrivateDnsValidationObserver {
+ public:
+ virtual ~PrivateDnsValidationObserver(){};
+ virtual void onValidationStateUpdate(const std::string& serverIp, Validation validation,
+ uint32_t netId) = 0;
+};
+
+} // namespace android::net
diff --git a/ResolverController.cpp b/ResolverController.cpp
index 3a7f914..b193af1 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -35,6 +35,8 @@
#include "stats.h"
using aidl::android::net::ResolverParamsParcel;
+using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
+using aidl::android::net::resolv::aidl::Nat64PrefixEventParcel;
namespace android {
@@ -44,45 +46,31 @@
namespace {
-const char* getPrivateDnsModeString(PrivateDnsMode mode) {
- switch (mode) {
- case PrivateDnsMode::OFF:
- return "OFF";
- case PrivateDnsMode::OPPORTUNISTIC:
- return "OPPORTUNISTIC";
- case PrivateDnsMode::STRICT:
- return "STRICT";
- }
-}
-
-constexpr const char* validationStatusToString(Validation value) {
- switch (value) {
- case Validation::in_process:
- return "in_process";
- case Validation::success:
- return "success";
- case Validation::success_but_expired:
- return "success_but_expired";
- case Validation::fail:
- return "fail";
- case Validation::unknown_server:
- return "unknown_server";
- case Validation::unknown_netid:
- return "unknown_netid";
- default:
- return "unknown_status";
- }
-}
-
void sendNat64PrefixEvent(const Dns64Configuration::Nat64PrefixInfo& args) {
+ LOG(DEBUG) << "Sending Nat64Prefix " << (args.added ? "added" : "removed") << " event on netId "
+ << args.netId << " with address {" << args.prefixString << "(" << args.prefixLength
+ << ")}";
+ // Send a nat64 prefix event to NetdEventListenerService.
const auto& listeners = ResolverEventReporter::getInstance().getListeners();
- if (listeners.size() == 0) {
- LOG(ERROR) << __func__ << ": No available listener. dropping NAT64 prefix event";
- return;
+ if (listeners.empty()) {
+ LOG(ERROR) << __func__ << ": No available listener. Skipping NAT64 prefix event";
}
for (const auto& it : listeners) {
it->onNat64PrefixEvent(args.netId, args.added, args.prefixString, args.prefixLength);
}
+
+ const auto& unsolEventListeners = ResolverEventReporter::getInstance().getUnsolEventListeners();
+ const Nat64PrefixEventParcel nat64PrefixEvent = {
+ .netId = static_cast<int32_t>(args.netId),
+ .prefixOperation =
+ args.added ? IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_ADDED
+ : IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_REMOVED,
+ .prefixAddress = args.prefixString,
+ .prefixLength = args.prefixLength,
+ };
+ for (const auto& it : unsolEventListeners) {
+ it->onNat64PrefixEvent(nat64PrefixEvent);
+ }
}
int getDnsInfo(unsigned netId, std::vector<std::string>* servers, std::vector<std::string>* domains,
diff --git a/ResolverEventReporter.cpp b/ResolverEventReporter.cpp
index b4e9b5c..a809c88 100644
--- a/ResolverEventReporter.cpp
+++ b/ResolverEventReporter.cpp
@@ -21,6 +21,7 @@
#include <android/binder_manager.h>
using aidl::android::net::metrics::INetdEventListener;
+using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
ResolverEventReporter& ResolverEventReporter::getInstance() {
// It should be initialized only once.
@@ -38,10 +39,19 @@
return getListenersImpl();
}
+ResolverEventReporter::UnsolEventListenerSet ResolverEventReporter::getUnsolEventListeners() const {
+ return getUnsolEventListenersImpl();
+}
+
int ResolverEventReporter::addListener(const std::shared_ptr<INetdEventListener>& listener) {
return addListenerImpl(listener);
}
+int ResolverEventReporter::addUnsolEventListener(
+ const std::shared_ptr<IDnsResolverUnsolicitedEventListener>& listener) {
+ return addUnsolEventListenerImpl(listener);
+}
+
// TODO: Consider registering metrics listener from framework and remove this function.
// Currently, the framework listener "netd_listener" is shared by netd and libnetd_resolv.
// Consider breaking it into two listeners. Once it has done, may let framework register
@@ -73,11 +83,28 @@
if (found != mListeners.end()) mListeners.erase(found);
}
+void ResolverEventReporter::handleUnsolEventBinderDied(const void* who) {
+ std::lock_guard lock(mMutex);
+
+ // Use the raw binder pointer address to be the identification of dead binder. Treat "who"
+ // which passes the raw address of dead binder as an identification only.
+ auto found = std::find_if(mUnsolEventListeners.begin(), mUnsolEventListeners.end(),
+ [=](const auto& it) { return static_cast<void*>(it.get()) == who; });
+
+ if (found != mUnsolEventListeners.end()) mUnsolEventListeners.erase(found);
+}
+
ResolverEventReporter::ListenerSet ResolverEventReporter::getListenersImpl() const {
std::lock_guard lock(mMutex);
return mListeners;
}
+ResolverEventReporter::UnsolEventListenerSet ResolverEventReporter::getUnsolEventListenersImpl()
+ const {
+ std::lock_guard lock(mMutex);
+ return mUnsolEventListeners;
+}
+
int ResolverEventReporter::addListenerImpl(const std::shared_ptr<INetdEventListener>& listener) {
std::lock_guard lock(mMutex);
return addListenerImplLocked(listener);
@@ -126,4 +153,56 @@
mListeners.insert(listener);
return 0;
-}
\ No newline at end of file
+}
+
+int ResolverEventReporter::addUnsolEventListenerImpl(
+ const std::shared_ptr<IDnsResolverUnsolicitedEventListener>& listener) {
+ std::lock_guard lock(mMutex);
+ return addUnsolEventListenerImplLocked(listener);
+}
+
+int ResolverEventReporter::addUnsolEventListenerImplLocked(
+ const std::shared_ptr<IDnsResolverUnsolicitedEventListener>& listener) {
+ if (listener == nullptr) {
+ LOG(ERROR) << "The unsolicited event listener should not be null";
+ return -EINVAL;
+ }
+
+ for (const auto& it : mUnsolEventListeners) {
+ if (it->asBinder().get() == listener->asBinder().get()) {
+ LOG(WARNING) << "The unsolicited event listener was already subscribed";
+ return -EEXIST;
+ }
+ }
+
+ static AIBinder_DeathRecipient* deathRecipient = nullptr;
+ if (deathRecipient == nullptr) {
+ // The AIBinder_DeathRecipient object is used to manage all death recipients for multiple
+ // binder objects. It doesn't released because there should have at least one binder object
+ // from framework.
+ // TODO: Considering to remove death recipient for the binder object from framework because
+ // it doesn't need death recipient actually.
+ deathRecipient = AIBinder_DeathRecipient_new([](void* cookie) {
+ ResolverEventReporter::getInstance().handleUnsolEventBinderDied(cookie);
+ });
+ }
+
+ // Pass the raw binder pointer address to be the cookie of the death recipient. While the death
+ // notification is fired, the cookie is used for identifying which binder was died. Because
+ // the NDK binder doesn't pass dead binder pointer to binder death handler, the binder death
+ // handler can't know who was died via wp<IBinder>. The reason for wp<IBinder> is not passed
+ // is that NDK binder can't transform a wp<IBinder> to a wp<AIBinder> in some cases.
+ // See more information in b/128712772.
+ auto binder = listener->asBinder().get();
+ auto cookie = static_cast<void*>(listener.get()); // Used for dead binder identification.
+ binder_status_t status = AIBinder_linkToDeath(binder, deathRecipient, cookie);
+
+ if (STATUS_OK != status) {
+ LOG(ERROR)
+ << "Failed to register death notification for IDnsResolverUnsolicitedEventListener";
+ return -EAGAIN;
+ }
+
+ mUnsolEventListeners.insert(listener);
+ return 0;
+}
diff --git a/ResolverEventReporter.h b/ResolverEventReporter.h
index eb0d9cf..39663b7 100644
--- a/ResolverEventReporter.h
+++ b/ResolverEventReporter.h
@@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include "aidl/android/net/metrics/INetdEventListener.h"
+#include "aidl/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.h"
/*
* This class can be used to get the binder reference to the netd events listener service
@@ -36,6 +37,8 @@
ResolverEventReporter& operator=(ResolverEventReporter&&) = delete;
using ListenerSet = std::set<std::shared_ptr<aidl::android::net::metrics::INetdEventListener>>;
+ using UnsolEventListenerSet = std::set<std::shared_ptr<
+ aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener>>;
// Get the instance of the singleton ResolverEventReporter.
static ResolverEventReporter& getInstance();
@@ -43,10 +46,20 @@
// Return the binder from the singleton ResolverEventReporter. This method is threadsafe.
ListenerSet getListeners() const;
+ // Return registered binder services from the singleton ResolverEventReporter. This method is
+ // threadsafe.
+ UnsolEventListenerSet getUnsolEventListeners() const;
+
// Add the binder to the singleton ResolverEventReporter. This method is threadsafe.
int addListener(
const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener);
+ // Add the binder to the singleton ResolverEventReporter. This method is threadsafe.
+ int addUnsolEventListener(
+ const std::shared_ptr<
+ aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener>&
+ listener);
+
private:
ResolverEventReporter() = default;
~ResolverEventReporter() = default;
@@ -58,11 +71,22 @@
int addListenerImplLocked(
const std::shared_ptr<aidl::android::net::metrics::INetdEventListener>& listener)
REQUIRES(mMutex);
+ int addUnsolEventListenerImpl(
+ const std::shared_ptr<
+ aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener>&
+ listener) EXCLUDES(mMutex);
+ int addUnsolEventListenerImplLocked(
+ const std::shared_ptr<
+ aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener>&
+ listener) REQUIRES(mMutex);
ListenerSet getListenersImpl() const EXCLUDES(mMutex);
+ UnsolEventListenerSet getUnsolEventListenersImpl() const EXCLUDES(mMutex);
void handleBinderDied(const void* who) EXCLUDES(mMutex);
+ void handleUnsolEventBinderDied(const void* who) EXCLUDES(mMutex);
mutable std::mutex mMutex;
ListenerSet mListeners GUARDED_BY(mMutex);
+ UnsolEventListenerSet mUnsolEventListeners GUARDED_BY(mMutex);
};
#endif // NETD_RESOLV_EVENT_REPORTER_H
diff --git a/aidl_api/dnsresolver_aidl_interface/7/.hash b/aidl_api/dnsresolver_aidl_interface/7/.hash
new file mode 100644
index 0000000..75f2e38
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/.hash
@@ -0,0 +1 @@
+a1dc9394598357ccaa74e96f564e7f248b72bad2
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..1f80545
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/IDnsResolver.aidl
@@ -0,0 +1,66 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface IDnsResolver {
+ boolean isAlive();
+ void registerEventListener(android.net.metrics.INetdEventListener listener);
+ void setResolverConfiguration(in android.net.ResolverParamsParcel resolverParams);
+ void getResolverInfo(int netId, out @utf8InCpp String[] servers, out @utf8InCpp String[] domains, out @utf8InCpp String[] tlsServers, out int[] params, out int[] stats, out int[] wait_for_pending_req_timeout_count);
+ void startPrefix64Discovery(int netId);
+ void stopPrefix64Discovery(int netId);
+ @utf8InCpp String getPrefix64(int netId);
+ void createNetworkCache(int netId);
+ void destroyNetworkCache(int netId);
+ void setLogSeverity(int logSeverity);
+ void flushNetworkCache(int netId);
+ void setPrefix64(int netId, @utf8InCpp String prefix);
+ void registerUnsolicitedEventListener(android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener listener);
+ const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
+ const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
+ const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
+ const int RESOLVER_PARAMS_MAX_SAMPLES = 3;
+ const int RESOLVER_PARAMS_BASE_TIMEOUT_MSEC = 4;
+ const int RESOLVER_PARAMS_RETRY_COUNT = 5;
+ const int RESOLVER_PARAMS_COUNT = 6;
+ const int RESOLVER_STATS_SUCCESSES = 0;
+ const int RESOLVER_STATS_ERRORS = 1;
+ const int RESOLVER_STATS_TIMEOUTS = 2;
+ const int RESOLVER_STATS_INTERNAL_ERRORS = 3;
+ const int RESOLVER_STATS_RTT_AVG = 4;
+ const int RESOLVER_STATS_LAST_SAMPLE_TIME = 5;
+ const int RESOLVER_STATS_USABLE = 6;
+ const int RESOLVER_STATS_COUNT = 7;
+ const int DNS_RESOLVER_LOG_VERBOSE = 0;
+ const int DNS_RESOLVER_LOG_DEBUG = 1;
+ const int DNS_RESOLVER_LOG_INFO = 2;
+ const int DNS_RESOLVER_LOG_WARNING = 3;
+ const int DNS_RESOLVER_LOG_ERROR = 4;
+ const int TC_MODE_DEFAULT = 0;
+ const int TC_MODE_UDP_TCP = 1;
+ const int TRANSPORT_UNKNOWN = -1;
+ const int TRANSPORT_CELLULAR = 0;
+ const int TRANSPORT_WIFI = 1;
+ const int TRANSPORT_BLUETOOTH = 2;
+ const int TRANSPORT_ETHERNET = 3;
+ const int TRANSPORT_VPN = 4;
+ const int TRANSPORT_WIFI_AWARE = 5;
+ const int TRANSPORT_LOWPAN = 6;
+ const int TRANSPORT_TEST = 7;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverHostsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverHostsParcel.aidl
new file mode 100644
index 0000000..c24eb61
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverHostsParcel.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable ResolverHostsParcel {
+ @utf8InCpp String ipAddr;
+ @utf8InCpp String hostName = "";
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverOptionsParcel.aidl
new file mode 100644
index 0000000..e806d04
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverOptionsParcel.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable ResolverOptionsParcel {
+ android.net.ResolverHostsParcel[] hosts = {};
+ int tcMode = 0;
+ boolean enforceDnsUid = false;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverParamsParcel.aidl
new file mode 100644
index 0000000..8fec710
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/ResolverParamsParcel.aidl
@@ -0,0 +1,38 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable ResolverParamsParcel {
+ int netId;
+ int sampleValiditySeconds;
+ int successThreshold;
+ int minSamples;
+ int maxSamples;
+ int baseTimeoutMsec;
+ int retryCount;
+ @utf8InCpp String[] servers;
+ @utf8InCpp String[] domains;
+ @utf8InCpp String tlsName;
+ @utf8InCpp String[] tlsServers;
+ @utf8InCpp String[] tlsFingerprints = {};
+ @utf8InCpp String caCertificate = "";
+ int tlsConnectTimeoutMs = 0;
+ android.net.ResolverOptionsParcel resolverOptions;
+ int[] transportTypes = {};
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/DnsHealthEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/DnsHealthEventParcel.aidl
new file mode 100644
index 0000000..d32be91
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/DnsHealthEventParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+@JavaDerive(toString=true)
+parcelable DnsHealthEventParcel {
+ int netId;
+ int healthResult;
+ int[] successRttMicros;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..d8accd1
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl
@@ -0,0 +1,31 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+interface IDnsResolverUnsolicitedEventListener {
+ oneway void onDnsHealthEvent(in android.net.resolv.aidl.DnsHealthEventParcel dnsHealthEvent);
+ oneway void onNat64PrefixEvent(in android.net.resolv.aidl.Nat64PrefixEventParcel nat64PrefixEvent);
+ oneway void onPrivateDnsValidationEvent(in android.net.resolv.aidl.PrivateDnsValidationEventParcel privateDnsValidationEvent);
+ const int DNS_HEALTH_RESULT_OK = 0;
+ const int DNS_HEALTH_RESULT_TIMEOUT = 255;
+ const int PREFIX_OPERATION_ADDED = 1;
+ const int PREFIX_OPERATION_REMOVED = 2;
+ const int VALIDATION_RESULT_SUCCESS = 1;
+ const int VALIDATION_RESULT_FAILURE = 2;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl
new file mode 100644
index 0000000..2daccb0
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+@JavaDerive(toString=true)
+parcelable Nat64PrefixEventParcel {
+ int netId;
+ int prefixOperation;
+ @utf8InCpp String prefixAddress;
+ int prefixLength;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl
new file mode 100644
index 0000000..e66e21c
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/7/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+@JavaDerive(toString=true)
+parcelable PrivateDnsValidationEventParcel {
+ int netId;
+ @utf8InCpp String ipAddress;
+ @utf8InCpp String hostname;
+ int validation;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
index 863927b..1f80545 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
@@ -2,13 +2,14 @@
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
@@ -30,6 +31,7 @@
void setLogSeverity(int logSeverity);
void flushNetworkCache(int netId);
void setPrefix64(int netId, @utf8InCpp String prefix);
+ void registerUnsolicitedEventListener(android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener listener);
const int RESOLVER_PARAMS_SAMPLE_VALIDITY = 0;
const int RESOLVER_PARAMS_SUCCESS_THRESHOLD = 1;
const int RESOLVER_PARAMS_MIN_SAMPLES = 2;
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl
index 3ab0533..c24eb61 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverHostsParcel.aidl
@@ -2,13 +2,14 @@
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl
index d55ae46..e806d04 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl
@@ -2,13 +2,14 @@
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
index 5dae1ca..8fec710 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
@@ -2,13 +2,14 @@
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
//
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/DnsHealthEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/DnsHealthEventParcel.aidl
new file mode 100644
index 0000000..d32be91
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/DnsHealthEventParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+@JavaDerive(toString=true)
+parcelable DnsHealthEventParcel {
+ int netId;
+ int healthResult;
+ int[] successRttMicros;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..d8accd1
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl
@@ -0,0 +1,31 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+interface IDnsResolverUnsolicitedEventListener {
+ oneway void onDnsHealthEvent(in android.net.resolv.aidl.DnsHealthEventParcel dnsHealthEvent);
+ oneway void onNat64PrefixEvent(in android.net.resolv.aidl.Nat64PrefixEventParcel nat64PrefixEvent);
+ oneway void onPrivateDnsValidationEvent(in android.net.resolv.aidl.PrivateDnsValidationEventParcel privateDnsValidationEvent);
+ const int DNS_HEALTH_RESULT_OK = 0;
+ const int DNS_HEALTH_RESULT_TIMEOUT = 255;
+ const int PREFIX_OPERATION_ADDED = 1;
+ const int PREFIX_OPERATION_REMOVED = 2;
+ const int VALIDATION_RESULT_SUCCESS = 1;
+ const int VALIDATION_RESULT_FAILURE = 2;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl
new file mode 100644
index 0000000..2daccb0
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+@JavaDerive(toString=true)
+parcelable Nat64PrefixEventParcel {
+ int netId;
+ int prefixOperation;
+ @utf8InCpp String prefixAddress;
+ int prefixLength;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl
new file mode 100644
index 0000000..e66e21c
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.resolv.aidl;
+/* @hide */
+@JavaDerive(toString=true)
+parcelable PrivateDnsValidationEventParcel {
+ int netId;
+ @utf8InCpp String ipAddress;
+ @utf8InCpp String hostname;
+ int validation;
+}
diff --git a/binder/android/net/IDnsResolver.aidl b/binder/android/net/IDnsResolver.aidl
index bcf68a8..33316e3 100644
--- a/binder/android/net/IDnsResolver.aidl
+++ b/binder/android/net/IDnsResolver.aidl
@@ -18,6 +18,7 @@
import android.net.ResolverParamsParcel;
import android.net.metrics.INetdEventListener;
+import android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener;
/** {@hide} */
interface IDnsResolver {
@@ -214,4 +215,21 @@
* unix errno.
*/
void setPrefix64(int netId, @utf8InCpp String prefix);
+
+ /**
+ * Register unsolicited event listener
+ * DnsResolver supports multiple unsolicited event listeners.
+ *
+ * This is a non-public interface between DnsResolver and Connectivity/NetworkStack.
+ * It is subject to change on Mainline updates without notice. DO NOT DEPEND ON IT.
+ *
+ * Only system services(Connectivity/NetworkStack) will register the unsolicited listener.
+ * Besides, there is no unregister method since the system services will be always there to
+ * listen unsolicited events.
+ *
+ * @param listener unsolicited event listener to register
+ * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+ * unix errno.
+ */
+ void registerUnsolicitedEventListener(IDnsResolverUnsolicitedEventListener listener);
}
diff --git a/binder/android/net/resolv/aidl/DnsHealthEventParcel.aidl b/binder/android/net/resolv/aidl/DnsHealthEventParcel.aidl
new file mode 100644
index 0000000..33f60da
--- /dev/null
+++ b/binder/android/net/resolv/aidl/DnsHealthEventParcel.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.resolv.aidl;
+
+/**
+ * The DNS health result of queries.
+ *
+ * Only sent for API calls that actually generated network traffic:
+ * - No cache hits
+ * - No power management errors (rcode == INTERNAL_ERROR && errno == EPERM)
+ * - Only lookup result is OK or TIMEOUT.
+ *
+ * This must not be used for use-cases other than evaluating the health of DNS queries. The behavior
+ * of this interface will change across regular module updates, and in particular it will be updated
+ * to stop sending one event by DNS API call, and instead report network failures in realtime.
+ *
+ * Non-public API, only callable from Connectivity or NetworkStack Mainline modules.
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable DnsHealthEventParcel {
+
+ /*** The ID of the network that lookup was performed on. */
+ int netId;
+
+ /**
+ * The health result of queries.
+ * Currently, this value is for data stall detection and it only checks whether result is
+ * timeout or not. So if the result is neither OK nor TIMEOUT, will not send this event.
+ */
+ int healthResult;
+
+ /*** The round trip time for each dns query that received a reply */
+ int[] successRttMicros;
+}
diff --git a/binder/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl b/binder/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..b0d6553
--- /dev/null
+++ b/binder/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.resolv.aidl;
+
+import android.net.resolv.aidl.DnsHealthEventParcel;
+import android.net.resolv.aidl.Nat64PrefixEventParcel;
+import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
+
+/**
+ * Unsolicited dns resolver events listener.
+ * This one-way interface groups asynchronous notifications sent by dns resolver to any process that
+ * registered itself via IDnsResolver.registerUnsolicitedEventListener.
+ *
+ * {@hide}
+ */
+oneway interface IDnsResolverUnsolicitedEventListener {
+
+ /*** Types for {@code healthResult} of {@code DnsHealthEventParcel}. */
+ const int DNS_HEALTH_RESULT_OK = 0;
+ const int DNS_HEALTH_RESULT_TIMEOUT = 255;
+
+ /**
+ * Represents a DNS health result of queries.
+ *
+ * Sent by the resolver when it has events to report about its observed network health.
+ * Refer to DnsHealthEventParcel for more details.
+ *
+ * @param dnsHealthEvent the DNS health result.
+ */
+ void onDnsHealthEvent(in DnsHealthEventParcel dnsHealthEvent);
+
+ /*** Types for {@code prefixOperation} of {@code Nat64PrefixEventParcel}. */
+ const int PREFIX_OPERATION_ADDED = 1;
+ const int PREFIX_OPERATION_REMOVED = 2;
+
+ /**
+ * Represents a NAT64 prefix operation.
+ *
+ * @param nat64PrefixEvent the NAT64 prefix status.
+ */
+ void onNat64PrefixEvent(in Nat64PrefixEventParcel nat64PrefixEvent);
+
+ /*** Types for {@code validation} of {@code PrivateDnsValidationEventParcel}. */
+ const int VALIDATION_RESULT_SUCCESS = 1;
+ const int VALIDATION_RESULT_FAILURE = 2;
+
+ /**
+ * Represents a private DNS validation result.
+ *
+ * @param privateDnsValidationEvent the private DNS validation result.
+ */
+ void onPrivateDnsValidationEvent(in PrivateDnsValidationEventParcel privateDnsValidationEvent);
+}
diff --git a/binder/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl b/binder/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl
new file mode 100644
index 0000000..45e25a1
--- /dev/null
+++ b/binder/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.resolv.aidl;
+
+/**
+ * Nat64 prefix operation event.
+ *
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable Nat64PrefixEventParcel {
+
+ /** The ID of the network the prefix operation was performed on. */
+ int netId;
+
+ /**
+ * The NAT64 prefix operation.
+ * There is only one prefix at a time for each netId. If a prefix is added, it replaces the
+ * previous-added prefix.
+ */
+ int prefixOperation;
+
+ /** The detected NAT64 prefix address. */
+ @utf8InCpp String prefixAddress;
+
+ /** The prefix length associated with this NAT64 prefix. */
+ int prefixLength;
+}
diff --git a/binder/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl b/binder/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl
new file mode 100644
index 0000000..e5d7f78
--- /dev/null
+++ b/binder/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.resolv.aidl;
+
+/**
+ * A private DNS validation result.
+ *
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable PrivateDnsValidationEventParcel {
+
+ /** The ID of the network the validation was performed on. */
+ int netId;
+
+ /** The IP address for which validation was performed. */
+ @utf8InCpp String ipAddress;
+
+ /** The hostname for which validation was performed. */
+ @utf8InCpp String hostname;
+
+ /** The validation result. */
+ int validation;
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 632aeb8..b832348 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -165,7 +165,6 @@
],
static_libs: [
"dnsresolver_aidl_interface-unstable-ndk_platform",
- "libbpf_android",
"libcrypto_static",
"libgmock",
"libnetd_test_dnsresponder_ndk",
@@ -177,6 +176,7 @@
"netd_aidl_interface-ndk_platform",
"netd_event_listener_interface-unstable-ndk_platform",
"libipchecksum",
+ "resolv_unsolicited_listener",
],
// This test talks to the DnsResolver module over a binary protocol on a socket, so keep it as
// multilib setting is worth because we might be able to get some coverage for the case where
diff --git a/tests/dnsresolver_binder_test.cpp b/tests/dnsresolver_binder_test.cpp
index 50f446e..18bd6dd 100644
--- a/tests/dnsresolver_binder_test.cpp
+++ b/tests/dnsresolver_binder_test.cpp
@@ -40,6 +40,7 @@
#include "dns_metrics_listener/base_metrics_listener.h"
#include "dns_metrics_listener/test_metrics.h"
+#include "unsolicited_listener/unsolicited_event_listener.h"
#include "ResolverStats.h"
#include "dns_responder.h"
@@ -55,6 +56,7 @@
using android::base::unique_fd;
using android::net::ResolverStats;
using android::net::metrics::TestOnDnsEvent;
+using android::net::resolv::aidl::UnsolicitedEventListener;
using android::netdutils::Stopwatch;
// TODO: make this dynamic and stop depending on implementation details.
@@ -121,19 +123,33 @@
// Basic regexp to match dump output lines. Matches the beginning and end of the line, and
// puts the output of the command itself into the first match group.
// Example: " 11-05 00:23:39.481 myCommand(args) <2.02ms>".
+ // Note: There are 4 leading blank characters in Q, but 6 in R.
const std::basic_regex lineRegex(
- "^ [0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[.][0-9]{3} "
+ "^ {4,6}[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[.][0-9]{3} "
"(.*)"
" <[0-9]+[.][0-9]{2}ms>$");
// For each element of testdata, check that the expected output appears in the dump output.
// If not, fail the test and use hintRegex to print similar lines to assist in debugging.
for (const auto& td : mExpectedLogData) {
+ const std::string toErase = "(null)";
const bool found =
std::any_of(lines.begin(), lines.end(), [&](const std::string& line) {
std::smatch match;
if (!std::regex_match(line, match, lineRegex)) return false;
- return (match.size() == 2) && (match[1].str() == td.output);
+ if (match.size() != 2) return false;
+
+ // The binder_to_string format is changed from S that will add "(null)" to
+ // the log on method's argument if binder object is null. But Q and R don't
+ // have this format in log. So to make register null listener tests are
+ // compatible from all version, just remove the "(null)" argument from
+ // output logs if existed.
+ std::string output = match[1].str();
+ const auto pos = output.find(toErase);
+ if (pos != std::string::npos && pos < output.length()) {
+ output.erase(pos, toErase.length());
+ }
+ return output == td.output;
});
EXPECT_TRUE(found) << "Didn't find line '" << td.output << "' in dumpsys output.";
if (found) continue;
@@ -272,16 +288,16 @@
}
TEST_F(DnsResolverBinderTest, RegisterEventListener_DuplicateSubscription) {
- class DummyListener : public android::net::metrics::BaseMetricsListener {};
+ class FakeListener : public android::net::metrics::BaseMetricsListener {};
// Expect to subscribe successfully.
- std::shared_ptr<DummyListener> dummyListener = ndk::SharedRefBase::make<DummyListener>();
- ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(dummyListener);
+ std::shared_ptr<FakeListener> fakeListener = ndk::SharedRefBase::make<FakeListener>();
+ ::ndk::ScopedAStatus status = mDnsResolver->registerEventListener(fakeListener);
ASSERT_TRUE(status.isOk()) << status.getMessage();
mExpectedLogData.push_back({"registerEventListener()", "registerEventListener.*"});
// Expect to subscribe failed with registered listener instance.
- status = mDnsResolver->registerEventListener(dummyListener);
+ status = mDnsResolver->registerEventListener(fakeListener);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(EEXIST, status.getServiceSpecificError());
mExpectedLogData.push_back(
@@ -289,6 +305,34 @@
"registerEventListener.*17"});
}
+TEST_F(DnsResolverBinderTest, RegisterUnsolicitedEventListener_NullListener) {
+ ::ndk::ScopedAStatus status = mDnsResolver->registerUnsolicitedEventListener(nullptr);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(EINVAL, status.getServiceSpecificError());
+ mExpectedLogData.push_back(
+ {"registerUnsolicitedEventListener() -> ServiceSpecificException(22, \"Invalid "
+ "argument\")",
+ "registerUnsolicitedEventListener.*22"});
+}
+
+TEST_F(DnsResolverBinderTest, RegisterUnsolicitedEventListener_DuplicateSubscription) {
+ // Expect to subscribe successfully.
+ std::shared_ptr<UnsolicitedEventListener> listener =
+ ndk::SharedRefBase::make<UnsolicitedEventListener>(TEST_NETID);
+ ::ndk::ScopedAStatus status = mDnsResolver->registerUnsolicitedEventListener(listener);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+ mExpectedLogData.push_back(
+ {"registerUnsolicitedEventListener()", "registerUnsolicitedEventListener.*"});
+
+ // Expect to subscribe failed with registered listener instance.
+ status = mDnsResolver->registerUnsolicitedEventListener(listener);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(EEXIST, status.getServiceSpecificError());
+ mExpectedLogData.push_back(
+ {"registerUnsolicitedEventListener() -> ServiceSpecificException(17, \"File exists\")",
+ "registerUnsolicitedEventListener.*17"});
+}
+
// TODO: Move this test to resolv_integration_test.cpp
TEST_F(DnsResolverBinderTest, RegisterEventListener_onDnsEvent) {
// The test configs are used to trigger expected events. The expected results are defined in
diff --git a/tests/resolv_gold_test.cpp b/tests/resolv_gold_test.cpp
index f7f417b..c5b24aa 100644
--- a/tests/resolv_gold_test.cpp
+++ b/tests/resolv_gold_test.cpp
@@ -369,6 +369,7 @@
ASSERT_NO_FATAL_FAILURE(SetResolversWithTls());
EXPECT_TRUE(WaitForPrivateDnsValidation(tls.listen_address()));
tls.setDelayQueries(2);
+ tls.setDelayQueriesTimeout(200);
dns.clearQueries();
addrinfo* res = nullptr;
@@ -431,6 +432,7 @@
ASSERT_TRUE(dns.startServer());
test::DnsTlsFrontend tls;
tls.setDelayQueries(2);
+ tls.setDelayQueriesTimeout(200);
if (protocol == DnsProtocol::CLEARTEXT) {
ASSERT_NO_FATAL_FAILURE(SetResolvers());
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index 09c71bf..cef8b6b 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -27,7 +27,6 @@
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <binder/ProcessState.h>
-#include <bpf/BpfUtils.h>
#include <cutils/sockets.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
@@ -72,6 +71,7 @@
#include "tests/dns_responder/dns_tls_frontend.h"
#include "tests/resolv_test_utils.h"
#include "tests/tun_forwarder.h"
+#include "tests/unsolicited_listener/unsolicited_event_listener.h"
// Valid VPN netId range is 100 ~ 65535
constexpr int TEST_VPN_NETID = 65502;
@@ -95,6 +95,10 @@
using aidl::android::net::INetd;
using aidl::android::net::ResolverParamsParcel;
using aidl::android::net::metrics::INetdEventListener;
+using aidl::android::net::resolv::aidl::DnsHealthEventParcel;
+using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
+using aidl::android::net::resolv::aidl::Nat64PrefixEventParcel;
+using aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel;
using android::base::Error;
using android::base::ParseInt;
using android::base::Result;
@@ -103,6 +107,7 @@
using android::net::ResolverStats;
using android::net::TunForwarder;
using android::net::metrics::DnsMetricsListener;
+using android::net::resolv::aidl::UnsolicitedEventListener;
using android::netdutils::enableSockopt;
using android::netdutils::makeSlice;
using android::netdutils::ResponseCode;
@@ -205,6 +210,12 @@
TEST_NETID /*monitor specific network*/);
ASSERT_TRUE(resolvService->registerEventListener(sDnsMetricsListener).isOk());
+ // Subscribe the unsolicited event listener for verifying unsolicited event contents.
+ sUnsolicitedEventListener = ndk::SharedRefBase::make<UnsolicitedEventListener>(
+ TEST_NETID /*monitor specific network*/);
+ ASSERT_TRUE(
+ resolvService->registerUnsolicitedEventListener(sUnsolicitedEventListener).isOk());
+
// Start the binder thread pool for listening DNS metrics events and receiving death
// recipient.
ABinderProcess_startThreadPool();
@@ -215,6 +226,7 @@
void SetUp() {
mDnsClient.SetUp();
sDnsMetricsListener->reset();
+ sUnsolicitedEventListener->reset();
}
void TearDown() {
@@ -248,15 +260,25 @@
bool WaitForNat64Prefix(ExpectNat64PrefixStatus status,
std::chrono::milliseconds timeout = std::chrono::milliseconds(1000)) {
- return sDnsMetricsListener->waitForNat64Prefix(status, timeout);
+ return sDnsMetricsListener->waitForNat64Prefix(status, timeout) &&
+ sUnsolicitedEventListener->waitForNat64Prefix(
+ status == EXPECT_FOUND
+ ? IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_ADDED
+ : IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_REMOVED,
+ timeout);
}
bool WaitForPrivateDnsValidation(std::string serverAddr, bool validated) {
- return sDnsMetricsListener->waitForPrivateDnsValidation(serverAddr, validated);
+ return sDnsMetricsListener->waitForPrivateDnsValidation(serverAddr, validated) &&
+ sUnsolicitedEventListener->waitForPrivateDnsValidation(
+ serverAddr,
+ validated ? IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_SUCCESS
+ : IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_FAILURE);
}
bool hasUncaughtPrivateDnsValidation(const std::string& serverAddr) {
- return sDnsMetricsListener->findValidationRecord(serverAddr);
+ return sDnsMetricsListener->findValidationRecord(serverAddr) &&
+ sUnsolicitedEventListener->findValidationRecord(serverAddr);
}
void ExpectDnsEvent(int32_t eventType, int32_t returnCode, const std::string& hostname,
@@ -271,6 +293,20 @@
if (dnsEvent.value() == expect) break;
LOG(INFO) << "Skip unexpected DnsEvent: " << dnsEvent.value();
} while (true);
+
+ while (returnCode == 0 || returnCode == RCODE_TIMEOUT) {
+ // Blocking call until timeout.
+ Result<int> result = sUnsolicitedEventListener->popDnsHealthResult();
+ ASSERT_TRUE(result.ok()) << "Expected dns health result is " << returnCode;
+ if ((returnCode == 0 &&
+ result.value() == IDnsResolverUnsolicitedEventListener::DNS_HEALTH_RESULT_OK) ||
+ (returnCode == RCODE_TIMEOUT &&
+ result.value() ==
+ IDnsResolverUnsolicitedEventListener::DNS_HEALTH_RESULT_TIMEOUT)) {
+ break;
+ }
+ LOG(INFO) << "Skip unexpected dns health result:" << result.value();
+ }
}
enum class StatsCmp { LE, EQ };
@@ -368,6 +404,9 @@
static std::shared_ptr<DnsMetricsListener>
sDnsMetricsListener; // Initialized in SetUpTestSuite.
+ inline static std::shared_ptr<UnsolicitedEventListener>
+ sUnsolicitedEventListener; // Initialized in SetUpTestSuite.
+
// Use a shared static death recipient to monitor the service death. The static death
// recipient could monitor the death not only during the test but also between tests.
static AIBinder_DeathRecipient* sResolvDeathRecipient; // Initialized in SetUpTestSuite.
@@ -4013,6 +4052,7 @@
EXPECT_TRUE(WaitForNat64Prefix(EXPECT_NOT_FOUND));
EXPECT_EQ(0, sDnsMetricsListener->getUnexpectedNat64PrefixUpdates());
+ EXPECT_EQ(0, sUnsolicitedEventListener->getUnexpectedNat64PrefixUpdates());
}
namespace {
@@ -4161,10 +4201,6 @@
}
TEST_F(ResolverTest, BlockDnsQueryWithUidRule) {
- // This test relies on blocking traffic on loopback, which xt_qtaguid does not do.
- // See aosp/358413 and b/34444781 for why.
- SKIP_IF_BPF_NOT_SUPPORTED;
-
constexpr char listen_addr1[] = "127.0.0.4";
constexpr char listen_addr2[] = "::1";
constexpr char host_name[] = "howdy.example.com.";
@@ -4212,8 +4248,6 @@
}
TEST_F(ResolverTest, EnforceDnsUid) {
- SKIP_IF_BPF_NOT_SUPPORTED;
-
constexpr char listen_addr1[] = "127.0.0.4";
constexpr char listen_addr2[] = "::1";
constexpr char host_name[] = "howdy.example.com.";
@@ -4292,9 +4326,6 @@
{hostname2, ns_type::ns_t_a, "1.2.3.5"},
};
- // The resolver will adjust the timeout value to 1000ms since the value is too small.
- ScopedSystemProperties scopedSystemProperties(kDotConnectTimeoutMsFlag, "100");
-
static const struct TestConfig {
bool asyncHandshake;
int maxRetries;
@@ -4323,6 +4354,8 @@
test::DnsTlsFrontend tls(addr, "853", addr, "53");
ASSERT_TRUE(tls.startServer());
+ // The resolver will adjust the timeout value to 1000ms since the value is too small.
+ ScopedSystemProperties scopedSystemProperties(kDotConnectTimeoutMsFlag, "100");
ScopedSystemProperties scopedSystemProperties1(kDotAsyncHandshakeFlag,
config.asyncHandshake ? "1" : "0");
ScopedSystemProperties scopedSystemProperties2(kDotMaxretriesFlag,
@@ -5370,10 +5403,6 @@
}
TEST_F(ResolverTest, BlockDnsQueryUidDoesNotLeadToBadServer) {
- // This test relies on blocking traffic on loopback, which xt_qtaguid does not do.
- // See aosp/358413 and b/34444781 for why.
- SKIP_IF_BPF_NOT_SUPPORTED;
-
constexpr char listen_addr1[] = "127.0.0.4";
constexpr char listen_addr2[] = "::1";
test::DNSResponder dns1(listen_addr1);
@@ -5423,8 +5452,6 @@
StartDns(dns2, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
StartDns(dns3, {{kHelloExampleCom, ns_type::ns_t_a, kHelloExampleComAddrV4}});
- ScopedSystemProperties scopedSystemProperties(kSortNameserversFlag, "1");
-
// NOTE: the servers must be sorted alphabetically.
std::vector<std::string> serverList = {
dns1.listen_address(),
@@ -5437,6 +5464,9 @@
const int queryNum = 50;
int64_t accumulatedTime = 0;
+ // The flag can be reset any time. It's better to re-setup the flag in each iteration.
+ ScopedSystemProperties scopedSystemProperties(kSortNameserversFlag, "1");
+
// Restart the testing network to 1) make the flag take effect and 2) reset the statistics.
resetNetwork();
@@ -6003,7 +6033,6 @@
TEST_F(ResolverMultinetworkTest, DnsWithVpn) {
SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsClient.resolvService(), 4);
- SKIP_IF_BPF_NOT_SUPPORTED;
constexpr char host_name[] = "ohayou.example.com.";
constexpr char ipv4_addr[] = "192.0.2.0";
constexpr char ipv6_addr[] = "2001:db8:cafe:d00d::31";
diff --git a/tests/unsolicited_listener/Android.bp b/tests/unsolicited_listener/Android.bp
new file mode 100644
index 0000000..155bc1d
--- /dev/null
+++ b/tests/unsolicited_listener/Android.bp
@@ -0,0 +1,14 @@
+cc_test_library {
+ name: "resolv_unsolicited_listener",
+ defaults: ["resolv_test_defaults"],
+ srcs: [
+ "unsolicited_event_listener.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "dnsresolver_aidl_interface-unstable-ndk_platform",
+ "libutils",
+ ],
+}
diff --git a/tests/unsolicited_listener/unsolicited_event_listener.cpp b/tests/unsolicited_listener/unsolicited_event_listener.cpp
new file mode 100644
index 0000000..773c385
--- /dev/null
+++ b/tests/unsolicited_listener/unsolicited_event_listener.cpp
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unsolicited_event_listener.h"
+
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/format.h>
+
+namespace android::net::resolv::aidl {
+
+using ::aidl::android::net::resolv::aidl::DnsHealthEventParcel;
+using ::aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener;
+using ::aidl::android::net::resolv::aidl::Nat64PrefixEventParcel;
+using ::aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel;
+using android::base::Error;
+using android::base::Result;
+using android::base::ScopedLockAssertion;
+using std::chrono::milliseconds;
+
+constexpr milliseconds kEventTimeoutMs{5000};
+constexpr milliseconds kRetryIntervalMs{20};
+
+::ndk::ScopedAStatus UnsolicitedEventListener::onDnsHealthEvent(const DnsHealthEventParcel& event) {
+ std::lock_guard lock(mMutex);
+ if (event.netId == mNetId) mDnsHealthResultRecords.push(event.healthResult);
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus UnsolicitedEventListener::onNat64PrefixEvent(
+ const Nat64PrefixEventParcel& event) {
+ std::lock_guard lock(mMutex);
+ mUnexpectedNat64PrefixUpdates++;
+ if (event.netId == mNetId) {
+ mNat64PrefixAddress = (event.prefixOperation ==
+ IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_ADDED)
+ ? event.prefixAddress
+ : "";
+ }
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus UnsolicitedEventListener::onPrivateDnsValidationEvent(
+ const PrivateDnsValidationEventParcel& event) {
+ {
+ std::lock_guard lock(mMutex);
+ // keep updating the server to have latest validation status.
+ mValidationRecords.insert_or_assign({event.netId, event.ipAddress}, event.validation);
+ }
+ mCv.notify_one();
+ return ::ndk::ScopedAStatus::ok();
+}
+
+bool UnsolicitedEventListener::waitForPrivateDnsValidation(const std::string& serverAddr,
+ int validation) {
+ const auto now = std::chrono::steady_clock::now();
+
+ std::unique_lock lock(mMutex);
+ ScopedLockAssertion assume_lock(mMutex);
+
+ // onPrivateDnsValidationEvent() might already be invoked. Search for the record first.
+ do {
+ if (findAndRemoveValidationRecord({mNetId, serverAddr}, validation)) return true;
+ } while (mCv.wait_until(lock, now + kEventTimeoutMs) != std::cv_status::timeout);
+
+ // Timeout.
+ return false;
+}
+
+bool UnsolicitedEventListener::findAndRemoveValidationRecord(const ServerKey& key, int value) {
+ auto it = mValidationRecords.find(key);
+ if (it != mValidationRecords.end() && it->second == value) {
+ mValidationRecords.erase(it);
+ return true;
+ }
+ return false;
+}
+
+bool UnsolicitedEventListener::waitForNat64Prefix(int operation, const milliseconds& timeout) {
+ android::base::Timer t;
+ while (t.duration() < timeout) {
+ {
+ std::lock_guard lock(mMutex);
+ if ((operation == IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_ADDED &&
+ !mNat64PrefixAddress.empty()) ||
+ (operation == IDnsResolverUnsolicitedEventListener::PREFIX_OPERATION_REMOVED &&
+ mNat64PrefixAddress.empty())) {
+ mUnexpectedNat64PrefixUpdates--;
+ return true;
+ }
+ }
+ std::this_thread::sleep_for(kRetryIntervalMs);
+ }
+ return false;
+}
+
+Result<int> UnsolicitedEventListener::popDnsHealthResult() {
+ // Wait until the queue is not empty or timeout.
+ android::base::Timer t;
+ while (t.duration() < milliseconds{1000}) {
+ {
+ std::lock_guard lock(mMutex);
+ if (!mDnsHealthResultRecords.empty()) break;
+ }
+ std::this_thread::sleep_for(kRetryIntervalMs);
+ }
+
+ std::lock_guard lock(mMutex);
+ if (mDnsHealthResultRecords.empty()) return Error() << "Dns health result record is empty";
+
+ auto ret = mDnsHealthResultRecords.front();
+ mDnsHealthResultRecords.pop();
+ return ret;
+}
+
+} // namespace android::net::resolv::aidl
diff --git a/tests/unsolicited_listener/unsolicited_event_listener.h b/tests/unsolicited_listener/unsolicited_event_listener.h
new file mode 100644
index 0000000..5edabd4
--- /dev/null
+++ b/tests/unsolicited_listener/unsolicited_event_listener.h
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <map>
+#include <queue>
+#include <string>
+#include <utility>
+
+#include <aidl/android/net/resolv/aidl/BnDnsResolverUnsolicitedEventListener.h>
+#include <android-base/result.h>
+#include <android-base/thread_annotations.h>
+
+namespace android::net::resolv::aidl {
+
+class UnsolicitedEventListener
+ : public ::aidl::android::net::resolv::aidl::BnDnsResolverUnsolicitedEventListener {
+ public:
+ UnsolicitedEventListener() = delete;
+ UnsolicitedEventListener(int32_t netId) : mNetId(netId){};
+ ~UnsolicitedEventListener() = default;
+
+ ::ndk::ScopedAStatus onDnsHealthEvent(
+ const ::aidl::android::net::resolv::aidl::DnsHealthEventParcel&) override;
+ ::ndk::ScopedAStatus onNat64PrefixEvent(
+ const ::aidl::android::net::resolv::aidl::Nat64PrefixEventParcel&) override;
+ ::ndk::ScopedAStatus onPrivateDnsValidationEvent(
+ const ::aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel&) override;
+
+ // Wait for the expected private DNS validation result until timeout.
+ bool waitForPrivateDnsValidation(const std::string& serverAddr, int validation);
+
+ // Wait for expected NAT64 prefix operation until timeout.
+ bool waitForNat64Prefix(int operation, const std::chrono::milliseconds& timeout)
+ EXCLUDES(mMutex);
+
+ // Pop up last receiving dns health result.
+ android::base::Result<int> popDnsHealthResult() EXCLUDES(mMutex);
+
+ // Return true if a validation result for |serverAddr| is found; otherwise, return false.
+ bool findValidationRecord(const std::string& serverAddr) const EXCLUDES(mMutex) {
+ std::lock_guard lock(mMutex);
+ return mValidationRecords.find({mNetId, serverAddr}) != mValidationRecords.end();
+ }
+
+ // Returns the number of updates to the NAT64 prefix that have not yet been waited for.
+ int getUnexpectedNat64PrefixUpdates() const EXCLUDES(mMutex) {
+ std::lock_guard lock(mMutex);
+ return mUnexpectedNat64PrefixUpdates;
+ }
+
+ void reset() EXCLUDES(mMutex) {
+ std::lock_guard lock(mMutex);
+ mValidationRecords.clear();
+ mUnexpectedNat64PrefixUpdates = 0;
+
+ std::queue<int> emptyQueue;
+ std::swap(mDnsHealthResultRecords, emptyQueue);
+ }
+
+ private:
+ typedef std::pair<int32_t, std::string> ServerKey;
+
+ // Search mValidationRecords. Return true if |key| exists and its value is equal to
+ // |value|, and then remove it; otherwise, return false.
+ bool findAndRemoveValidationRecord(const ServerKey& key, int value) REQUIRES(mMutex);
+
+ // Monitor the event which was fired on specific network id.
+ const int32_t mNetId;
+
+ // Used to store the data from onPrivateDnsValidationEvent.
+ std::map<ServerKey, int> mValidationRecords GUARDED_BY(mMutex);
+
+ // The NAT64 prefix address of the network |mNetId|. It is updated by onNat64PrefixEvent().
+ std::string mNat64PrefixAddress GUARDED_BY(mMutex);
+
+ // The number of updates to the NAT64 prefix of network |mNetId| that have not yet been waited
+ // for. Increases by 1 every time onNat64PrefixEvent is called, and decreases by 1 every time
+ // waitForNat64Prefix returns true.
+ // This allows tests to check that no unexpected events have been received without having to
+ // resort to timeouts that make the tests slower and flakier.
+ int mUnexpectedNat64PrefixUpdates GUARDED_BY(mMutex);
+
+ // Used to store the dns health result from onDnsHealthEvent().
+ std::queue<int> mDnsHealthResultRecords GUARDED_BY(mMutex);
+
+ mutable std::mutex mMutex;
+ std::condition_variable mCv;
+};
+
+} // namespace android::net::resolv::aidl
diff --git a/util.cpp b/util.cpp
index b8df74c..458f3c6 100644
--- a/util.cpp
+++ b/util.cpp
@@ -17,6 +17,7 @@
#include "util.h"
+#include <android-base/format.h>
#include <android-base/parseint.h>
#include <server_configurable_flags/get_flags.h>
@@ -45,3 +46,13 @@
ParseInt(GetServerConfigurableFlag("netd_native", flagName, ""), &val);
return val;
}
+
+std::string timestampToString(const std::chrono::system_clock::time_point& ts) {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ const auto time_sec = std::chrono::system_clock::to_time_t(ts);
+ char buf[32];
+ std::strftime(buf, sizeof(buf), "%H:%M:%S", std::localtime(&time_sec));
+ int ms = duration_cast<milliseconds>(ts.time_since_epoch()).count() % 1000;
+ return fmt::format("{}.{:03d}", buf, ms);
+}
diff --git a/util.h b/util.h
index 38f09b8..bb4a598 100644
--- a/util.h
+++ b/util.h
@@ -17,6 +17,7 @@
#pragma once
+#include <chrono>
#include <string>
#include <netinet/in.h>
@@ -30,6 +31,9 @@
// TODO: Migrate it to DnsResolverExperiments.cpp
int getExperimentFlagInt(const std::string& flagName, int defaultValue);
+// Convert time_point to readable string format "hr:min:sec.ms".
+std::string timestampToString(const std::chrono::system_clock::time_point& ts);
+
// When sdk X release branch is created, aosp's sdk version would still be X-1,
// internal would be X. Also there might be some different setting between real devices and
// CF. Below is the example for the sdk related properties in later R development stage. (internal
@@ -47,3 +51,8 @@
android::base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
return std::max(buildVersionSdk + !!buildVersionPreviewSdk, firstApiLevel);
}
+
+// It's the identical strategy as frameworks/base/core/java/android/os/Build.java did.
+inline bool isUserDebugBuild() {
+ return (android::base::GetProperty("ro.build.type", "user") == "userdebug");
+}