Snap for 6405781 from 44e38183a74427cebf22a6391e3b004e371b401a to sdk-release

Change-Id: I9570566569a30fbce5d6a1a5c2bf87736581caec
diff --git a/Android.bp b/Android.bp
index ccd7d58..ad11334 100644
--- a/Android.bp
+++ b/Android.bp
@@ -16,7 +16,7 @@
     srcs: [
         "binder/android/net/IDnsResolver.aidl",
         "binder/android/net/ResolverHostsParcel.aidl",
-        "binder/android/net/ResolverExperimentalOptionsParcel.aidl",
+        "binder/android/net/ResolverOptionsParcel.aidl",
         "binder/android/net/ResolverParamsParcel.aidl",
     ],
     imports: [
@@ -25,17 +25,22 @@
     backend: {
         java: {
             apex_available: [
+                "//apex_available:platform",
                 "com.android.bluetooth.updatable",
             ],
         },
         ndk: {
             gen_log: true,
+            apex_available: [
+                "com.android.resolv",
+            ],
         },
     },
     versions: [
         "1",
         "2",
         "3",
+        "4",
     ],
 }
 
@@ -112,7 +117,7 @@
     // Link most things statically to minimize our dependence on system ABIs.
     stl: "libc++_static",
     static_libs: [
-        "dnsresolver_aidl_interface-unstable-ndk_platform",
+        "dnsresolver_aidl_interface-ndk_platform",
         "libbase",
         "libcutils",
         "libjsoncpp",
@@ -166,6 +171,7 @@
     sanitize: {
         cfi: true,
     },
+    apex_available: ["com.android.resolv"],
 }
 
 cc_library_static {
@@ -178,6 +184,7 @@
     srcs: [
         "stats.proto",
     ],
+    apex_available: ["com.android.resolv"],
 }
 
 genrule {
@@ -209,6 +216,7 @@
         "libgtest_prod", // Used by libstatspush_compat
         "libstatspush_compat",
     ],
+    apex_available: ["com.android.resolv"],
 }
 
 filegroup {
@@ -221,11 +229,18 @@
 // TODO: Move this test to tests/
 cc_test {
     name: "resolv_unit_test",
-    test_suites: ["device-tests", "mts"],
+    test_suites: [
+        "device-tests",
+        "mts",
+    ],
     require_root: true,
     // TODO: Drop root privileges and make it be an real unit test.
     // TODO: Remove resolv_test_mts_coverage_defaults after mts coverage switched to 64-bit device.
-    defaults: ["netd_defaults", "resolv_test_defaults", "resolv_test_mts_coverage_defaults"],
+    defaults: [
+        "netd_defaults",
+        "resolv_test_defaults",
+        "resolv_test_mts_coverage_defaults",
+    ],
     srcs: [
         "resolv_cache_unit_test.cpp",
         "resolv_callback_unit_test.cpp",
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index 2046c34..8d09751 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -355,6 +355,7 @@
         const std::string& dnsQueryStats = event.dns_query_events().SerializeAsString();
         stats::BytesField dnsQueryBytesField{dnsQueryStats.c_str(), dnsQueryStats.size()};
         event.set_return_code(static_cast<ReturnCode>(returnCode));
+        event.set_network_type(resolv_get_network_types_for_net(netContext.dns_netid));
         android::net::stats::stats_write(android::net::stats::NETWORK_DNS_EVENT_REPORTED,
                                          event.event_type(), event.return_code(),
                                          event.latency_micros(), event.hints_ai_flags(),
diff --git a/ResolverController.cpp b/ResolverController.cpp
index 22878f3..13600b8 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -233,7 +233,7 @@
 
     return resolv_set_nameservers(resolverParams.netId, resolverParams.servers,
                                   resolverParams.domains, res_params,
-                                  resolverParams.experimentalOptions);
+                                  resolverParams.resolverOptions, resolverParams.transportTypes);
 }
 
 int ResolverController::getResolverInfo(int32_t netId, std::vector<std::string>* servers,
@@ -362,8 +362,7 @@
             dw.decIndent();
         }
         dw.println("Concurrent DNS query timeout: %d", wait_for_pending_req_timeout_count[0]);
-        resolv_stats_dump(dw, netId);
-        resolv_oem_options_dump(dw, netId);
+        resolv_netconfig_dump(dw, netId);
     }
     dw.decIndent();
 }
diff --git a/aidl_api/dnsresolver_aidl_interface/4/.hash b/aidl_api/dnsresolver_aidl_interface/4/.hash
new file mode 100644
index 0000000..c0a6b10
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/4/.hash
@@ -0,0 +1 @@
+012d220dd2a6736ed40b8653351386cece8458f7
diff --git a/aidl_api/dnsresolver_aidl_interface/4/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/4/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..9eb65de
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/4/android/net/IDnsResolver.aidl
@@ -0,0 +1,63 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+// You must not make a backward incompatible changes to the AIDL files 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);
+  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/current/android/net/ResolverExperimentalOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverHostsParcel.aidl
similarity index 89%
copy from aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
copy to aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverHostsParcel.aidl
index 07a1143..3ab0533 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverHostsParcel.aidl
@@ -17,7 +17,7 @@
 
 package android.net;
 /* @hide */
-parcelable ResolverExperimentalOptionsParcel {
-  android.net.ResolverHostsParcel[] hosts = {};
-  int tcMode = 0;
+parcelable ResolverHostsParcel {
+  @utf8InCpp String ipAddr;
+  @utf8InCpp String hostName = "";
 }
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverOptionsParcel.aidl
similarity index 95%
rename from aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
rename to aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverOptionsParcel.aidl
index 07a1143..a13216a 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverOptionsParcel.aidl
@@ -17,7 +17,7 @@
 
 package android.net;
 /* @hide */
-parcelable ResolverExperimentalOptionsParcel {
+parcelable ResolverOptionsParcel {
   android.net.ResolverHostsParcel[] hosts = {};
   int tcMode = 0;
 }
diff --git a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverParamsParcel.aidl
similarity index 68%
copy from aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
copy to aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverParamsParcel.aidl
index 07a1143..5dae1ca 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/4/android/net/ResolverParamsParcel.aidl
@@ -17,7 +17,21 @@
 
 package android.net;
 /* @hide */
-parcelable ResolverExperimentalOptionsParcel {
-  android.net.ResolverHostsParcel[] hosts = {};
-  int tcMode = 0;
+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/current/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
index 0ebc44f..9eb65de 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
@@ -51,5 +51,13 @@
   const int DNS_RESOLVER_LOG_ERROR = 4;
   const int TC_MODE_DEFAULT = 0;
   const int TC_MODE_UDP_TCP = 1;
-  const int TC_MODE_MAX = 2;
+  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/current/android/net/ResolverExperimentalOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl
similarity index 95%
copy from aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
copy to aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl
index 07a1143..a13216a 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverExperimentalOptionsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverOptionsParcel.aidl
@@ -17,7 +17,7 @@
 
 package android.net;
 /* @hide */
-parcelable ResolverExperimentalOptionsParcel {
+parcelable ResolverOptionsParcel {
   android.net.ResolverHostsParcel[] hosts = {};
   int tcMode = 0;
 }
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 8d807be..5dae1ca 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
@@ -32,5 +32,6 @@
   @utf8InCpp String[] tlsFingerprints = {};
   @utf8InCpp String caCertificate = "";
   int tlsConnectTimeoutMs = 0;
-  android.net.ResolverExperimentalOptionsParcel experimentalOptions;
+  android.net.ResolverOptionsParcel resolverOptions;
+  int[] transportTypes = {};
 }
diff --git a/binder/android/net/IDnsResolver.aidl b/binder/android/net/IDnsResolver.aidl
index c85e5cf..da13d21 100644
--- a/binder/android/net/IDnsResolver.aidl
+++ b/binder/android/net/IDnsResolver.aidl
@@ -172,14 +172,27 @@
     void flushNetworkCache(int netId);
 
     /**
-     * Values for {@code tcMode} of {@code ResolverExperimentalOptionsParcel}. It controls the way DNS
+     * Values for {@code tcMode} of {@code ResolverOptionsParcel}. It controls the way DNS
      * resolver handles truncated DNS response from UDP connection.
      *
      * TC_MODE_DEFAULT: resolver retries on TCP-only on each name server.
      * TC_MODE_UDP_TCP: resolver retries on TCP on the same server, falls back to UDP from next.
-     * TC_MODE_MAX: any value smaller than TC_MODE_DEFAULT or greater than TC_MODE_MAX is invalid.
      */
     const int TC_MODE_DEFAULT = 0;
     const int TC_MODE_UDP_TCP = 1;
-    const int TC_MODE_MAX = 2;
+
+    /**
+     * Values for {@code transportTypes} of {@code ResolverParamsParcel}. These values are
+     * always the same as the TRANSPORT_* API definations of NetworkCapabilities except for
+     * TRANSPORT_UNKNOWN. It controls the transport types for a given network.
+     */
+    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/binder/android/net/ResolverExperimentalOptionsParcel.aidl b/binder/android/net/ResolverOptionsParcel.aidl
similarity index 96%
rename from binder/android/net/ResolverExperimentalOptionsParcel.aidl
rename to binder/android/net/ResolverOptionsParcel.aidl
index f8fd589..8e435de 100644
--- a/binder/android/net/ResolverExperimentalOptionsParcel.aidl
+++ b/binder/android/net/ResolverOptionsParcel.aidl
@@ -23,7 +23,7 @@
  *
  * {@hide}
  */
-parcelable ResolverExperimentalOptionsParcel {
+parcelable ResolverOptionsParcel {
     /**
      * An IP/hostname mapping table for DNS local lookup customization.
      * WARNING: this is intended for local testing and other special situations.
diff --git a/binder/android/net/ResolverParamsParcel.aidl b/binder/android/net/ResolverParamsParcel.aidl
index c42367f..9a8e843 100644
--- a/binder/android/net/ResolverParamsParcel.aidl
+++ b/binder/android/net/ResolverParamsParcel.aidl
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.net.ResolverExperimentalOptionsParcel;
+import android.net.ResolverOptionsParcel;
 
 /**
  * Configuration for a resolver parameters.
@@ -101,7 +101,15 @@
     int tlsConnectTimeoutMs = 0;
 
     /**
-    * Knobs for OEM to control alternative behavior.
-    */
-    ResolverExperimentalOptionsParcel experimentalOptions;
+     * Knobs for OEM to control alternative behavior.
+     */
+    ResolverOptionsParcel resolverOptions;
+
+    /**
+     * The transport types associated to a given network.
+     * The valid value is defined in one of the IDnsResolver.TRANSPORT_* constants.
+     * If there are multiple transport types but can't be identified to a
+     * reasonable network type by DnsResolver, it would be considered as unknown.
+     */
+    int[] transportTypes = {};
 }
diff --git a/res_cache.cpp b/res_cache.cpp
index 7c0a65a..43c268e 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -49,6 +49,7 @@
 #include <net/if.h>
 #include <netdb.h>
 
+#include <aidl/android/net/IDnsResolver.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
@@ -64,6 +65,7 @@
 #include "resolv_private.h"
 #include "util.h"
 
+using aidl::android::net::IDnsResolver;
 using android::base::StringAppendF;
 using android::net::DnsQueryEvent;
 using android::net::DnsStats;
@@ -1002,6 +1004,7 @@
     // If resolverParams.hosts is empty, the existing customized table will be erased.
     HostMapping customizedTable = {};
     int tc_mode = aidl::android::net::IDnsResolver::TC_MODE_DEFAULT;
+    std::vector<int32_t> transportTypes;
 };
 
 /* gets cache associated with a network, or NULL if none exists */
@@ -1510,6 +1513,13 @@
     }
 }
 
+android::net::NetworkType resolv_get_network_types_for_net(unsigned netid) {
+    std::lock_guard guard(cache_mutex);
+    NetConfig* netconfig = find_netconfig_locked(netid);
+    if (netconfig == nullptr) return android::net::NT_UNKNOWN;
+    return convert_network_type(netconfig->transportTypes);
+}
+
 namespace {
 
 // Returns valid domains without duplicates which are limited to max size |MAXDNSRCH|.
@@ -1569,10 +1579,10 @@
     return result;
 }
 
-int resolv_set_nameservers(
-        unsigned netid, const std::vector<std::string>& servers,
-        const std::vector<std::string>& domains, const res_params& params,
-        const aidl::android::net::ResolverExperimentalOptionsParcel& experimentalOptions) {
+int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers,
+                           const std::vector<std::string>& domains, const res_params& params,
+                           const aidl::android::net::ResolverOptionsParcel& resolverOptions,
+                           const std::vector<int32_t>& transportTypes) {
     std::vector<std::string> nameservers = filter_nameservers(servers);
     const int numservers = static_cast<int>(nameservers.size());
 
@@ -1626,17 +1636,20 @@
         return -EINVAL;
     }
     netconfig->customizedTable.clear();
-    for (const auto& host : experimentalOptions.hosts) {
+    for (const auto& host : resolverOptions.hosts) {
         if (!host.hostName.empty() && !host.ipAddr.empty())
             netconfig->customizedTable.emplace(host.hostName, host.ipAddr);
     }
 
-    if (experimentalOptions.tcMode < aidl::android::net::IDnsResolver::TC_MODE_DEFAULT ||
-        experimentalOptions.tcMode >= aidl::android::net::IDnsResolver::TC_MODE_MAX) {
-        LOG(WARNING) << __func__ << ": netid = " << netid << ", invalid TC mode";
+    if (resolverOptions.tcMode < aidl::android::net::IDnsResolver::TC_MODE_DEFAULT ||
+        resolverOptions.tcMode > aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP) {
+        LOG(WARNING) << __func__ << ": netid = " << netid
+                     << ", invalid TC mode: " << resolverOptions.tcMode;
         return -EINVAL;
     }
-    netconfig->tc_mode = experimentalOptions.tcMode;
+    netconfig->tc_mode = resolverOptions.tcMode;
+
+    netconfig->transportTypes = transportTypes;
 
     return 0;
 }
@@ -1892,22 +1905,7 @@
     return false;
 }
 
-void resolv_stats_dump(DumpWriter& dw, unsigned netid) {
-    std::lock_guard guard(cache_mutex);
-    if (const auto info = find_netconfig_locked(netid); info != nullptr) {
-        info->dnsStats.dump(dw);
-    }
-}
-
-void resolv_oem_options_dump(DumpWriter& dw, unsigned netid) {
-    std::lock_guard guard(cache_mutex);
-    if (const auto info = find_netconfig_locked(netid); info != nullptr) {
-        // TODO: dump info->hosts
-        dw.println("TC mode: %s", tc_mode_to_str(info->tc_mode));
-    }
-}
-
-const char* tc_mode_to_str(const int mode) {
+static const char* tc_mode_to_str(const int mode) {
     switch (mode) {
         case aidl::android::net::IDnsResolver::TC_MODE_DEFAULT:
             return "default";
@@ -1917,3 +1915,95 @@
             return "unknown";
     }
 }
+
+static android::net::NetworkType to_stats_network_type(int32_t mainType, bool withVpn) {
+    switch (mainType) {
+        case IDnsResolver::TRANSPORT_CELLULAR:
+            return withVpn ? android::net::NT_CELLULAR_VPN : android::net::NT_CELLULAR;
+        case IDnsResolver::TRANSPORT_WIFI:
+            return withVpn ? android::net::NT_WIFI_VPN : android::net::NT_WIFI;
+        case IDnsResolver::TRANSPORT_BLUETOOTH:
+            return withVpn ? android::net::NT_BLUETOOTH_VPN : android::net::NT_BLUETOOTH;
+        case IDnsResolver::TRANSPORT_ETHERNET:
+            return withVpn ? android::net::NT_ETHERNET_VPN : android::net::NT_ETHERNET;
+        case IDnsResolver::TRANSPORT_VPN:
+            return withVpn ? android::net::NT_UNKNOWN : android::net::NT_VPN;
+        case IDnsResolver::TRANSPORT_WIFI_AWARE:
+            return withVpn ? android::net::NT_UNKNOWN : android::net::NT_WIFI_AWARE;
+        case IDnsResolver::TRANSPORT_LOWPAN:
+            return withVpn ? android::net::NT_UNKNOWN : android::net::NT_LOWPAN;
+        default:
+            return android::net::NT_UNKNOWN;
+    }
+}
+
+android::net::NetworkType convert_network_type(const std::vector<int32_t>& transportTypes) {
+    // The valid transportTypes size is 1 to 3.
+    if (transportTypes.size() > 3 || transportTypes.size() == 0) return android::net::NT_UNKNOWN;
+    // TransportTypes size == 1, map the type to stats network type directly.
+    if (transportTypes.size() == 1) return to_stats_network_type(transportTypes[0], false);
+    // TransportTypes size == 3, only cellular + wifi + vpn is valid.
+    if (transportTypes.size() == 3) {
+        std::vector<int32_t> sortedTransTypes = transportTypes;
+        std::sort(sortedTransTypes.begin(), sortedTransTypes.end());
+        if (sortedTransTypes != std::vector<int32_t>{IDnsResolver::TRANSPORT_CELLULAR,
+                                                     IDnsResolver::TRANSPORT_WIFI,
+                                                     IDnsResolver::TRANSPORT_VPN}) {
+            return android::net::NT_UNKNOWN;
+        }
+        return android::net::NT_WIFI_CELLULAR_VPN;
+    }
+    // TransportTypes size == 2, it shoud be 1 main type + vpn type.
+    // Otherwise, consider it as UNKNOWN.
+    bool hasVpn = false;
+    int32_t mainType = IDnsResolver::TRANSPORT_UNKNOWN;
+    for (const auto& transportType : transportTypes) {
+        if (transportType == IDnsResolver::TRANSPORT_VPN) {
+            hasVpn = true;
+            continue;
+        }
+        mainType = transportType;
+    }
+    return hasVpn ? to_stats_network_type(mainType, true) : android::net::NT_UNKNOWN;
+}
+
+static const char* transport_type_to_str(const std::vector<int32_t>& transportTypes) {
+    switch (convert_network_type(transportTypes)) {
+        case android::net::NT_CELLULAR:
+            return "CELLULAR";
+        case android::net::NT_WIFI:
+            return "WIFI";
+        case android::net::NT_BLUETOOTH:
+            return "BLUETOOTH";
+        case android::net::NT_ETHERNET:
+            return "ETHERNET";
+        case android::net::NT_VPN:
+            return "VPN";
+        case android::net::NT_WIFI_AWARE:
+            return "WIFI_AWARE";
+        case android::net::NT_LOWPAN:
+            return "LOWPAN";
+        case android::net::NT_CELLULAR_VPN:
+            return "CELLULAR_VPN";
+        case android::net::NT_WIFI_VPN:
+            return "WIFI_VPN";
+        case android::net::NT_BLUETOOTH_VPN:
+            return "BLUETOOTH_VPN";
+        case android::net::NT_ETHERNET_VPN:
+            return "ETHERNET_VPN";
+        case android::net::NT_WIFI_CELLULAR_VPN:
+            return "WIFI_CELLULAR_VPN";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+void resolv_netconfig_dump(DumpWriter& dw, unsigned netid) {
+    std::lock_guard guard(cache_mutex);
+    if (const auto info = find_netconfig_locked(netid); info != nullptr) {
+        info->dnsStats.dump(dw);
+        // TODO: dump info->hosts
+        dw.println("TC mode: %s", tc_mode_to_str(info->tc_mode));
+        dw.println("TransportType: %s", transport_type_to_str(info->transportTypes));
+    }
+}
diff --git a/resolv_cache.h b/resolv_cache.h
index dd7ede1..49c7f56 100644
--- a/resolv_cache.h
+++ b/resolv_cache.h
@@ -32,7 +32,7 @@
 #include <vector>
 
 #include <aidl/android/net/IDnsResolver.h>
-#include <aidl/android/net/ResolverExperimentalOptionsParcel.h>
+#include <aidl/android/net/ResolverOptionsParcel.h>
 
 #include <netdutils/DumpWriter.h>
 #include <netdutils/InternetAddresses.h>
@@ -80,11 +80,12 @@
 
 // Sets name servers for a given network.
 // TODO: Pass all of ResolverParamsParcel and remove the res_params argument.
-int resolv_set_nameservers(
-        unsigned netid, const std::vector<std::string>& servers,
-        const std::vector<std::string>& domains, const res_params& params,
-        const aidl::android::net::ResolverExperimentalOptionsParcel& experimentalOptions = {
-                {} /* hosts */, aidl::android::net::IDnsResolver::TC_MODE_DEFAULT});
+int resolv_set_nameservers(unsigned netid, const std::vector<std::string>& servers,
+                           const std::vector<std::string>& domains, const res_params& params,
+                           const aidl::android::net::ResolverOptionsParcel& resolverOptions =
+                                   {{} /* hosts */,
+                                    aidl::android::net::IDnsResolver::TC_MODE_DEFAULT},
+                           const std::vector<int32_t>& transportTypes = {});
 
 // Creates the cache associated with the given network.
 int resolv_create_cache_for_net(unsigned netid);
@@ -95,6 +96,9 @@
 // Flushes the cache associated with the given network.
 int resolv_flush_cache_for_net(unsigned netid);
 
+// Get transport types to a given network.
+android::net::NetworkType resolv_get_network_types_for_net(unsigned netid);
+
 // For test only.
 // Return true if the cache is existent in the given network, false otherwise.
 bool has_named_cache(unsigned netid);
@@ -111,12 +115,6 @@
 bool resolv_stats_add(unsigned netid, const android::netdutils::IPSockAddr& server,
                       const android::net::DnsQueryEvent* record);
 
-void resolv_stats_dump(android::netdutils::DumpWriter& dw, unsigned netid);
-
-void resolv_oem_options_dump(android::netdutils::DumpWriter& dw, unsigned netid);
-
-const char* tc_mode_to_str(const int mode);
-
 /* Retrieve a local copy of the stats for the given netid. The buffer must have space for
  * MAXNS __resolver_stats. Returns the revision id of the resolvers used.
  */
@@ -130,3 +128,9 @@
 void resolv_cache_add_resolver_stats_sample(unsigned netid, int revision_id,
                                             const android::netdutils::IPSockAddr& serverSockAddr,
                                             const res_sample& sample, int max_samples);
+
+// Convert TRANSPORT_* to NT_*. It's public only for unit testing.
+android::net::NetworkType convert_network_type(const std::vector<int32_t>& transportTypes);
+
+// Dump net configuration log for a given network.
+void resolv_netconfig_dump(android::netdutils::DumpWriter& dw, unsigned netid);
diff --git a/resolv_cache_unit_test.cpp b/resolv_cache_unit_test.cpp
index 4a29258..6e7ffee 100644
--- a/resolv_cache_unit_test.cpp
+++ b/resolv_cache_unit_test.cpp
@@ -22,6 +22,7 @@
 #include <ctime>
 #include <thread>
 
+#include <aidl/android/net/IDnsResolver.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android/multinetwork.h>
@@ -38,6 +39,7 @@
 
 using namespace std::chrono_literals;
 
+using aidl::android::net::IDnsResolver;
 using android::netdutils::IPSockAddr;
 
 constexpr int TEST_NETID = 30;
@@ -59,6 +61,8 @@
     std::vector<std::string> servers;
     std::vector<std::string> domains;
     res_params params;
+    aidl::android::net::ResolverOptionsParcel resolverOptions;
+    std::vector<int32_t> transportTypes;
 };
 
 struct CacheStats {
@@ -189,7 +193,8 @@
     }
 
     int cacheSetupResolver(uint32_t netId, const SetupParams& setup) {
-        return resolv_set_nameservers(netId, setup.servers, setup.domains, setup.params);
+        return resolv_set_nameservers(netId, setup.servers, setup.domains, setup.params,
+                                      setup.resolverOptions, setup.transportTypes);
     }
 
     void cacheAddStats(uint32_t netId, int revision_id, const IPSockAddr& ipsa,
@@ -834,6 +839,64 @@
     EXPECT_STREQ(answer, domain_name);
 }
 
+TEST_F(ResolvCacheTest, GetNetworkTypesForNet) {
+    const SetupParams setup = {
+            .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"},
+            .domains = {"domain1.com", "domain2.com"},
+            .params = kParams,
+            .transportTypes = {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_VPN}};
+    EXPECT_EQ(0, cacheCreate(TEST_NETID));
+    EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, setup));
+    EXPECT_EQ(android::net::NT_WIFI_VPN, resolv_get_network_types_for_net(TEST_NETID));
+}
+
+TEST_F(ResolvCacheTest, ConvertTransportsToNetworkType) {
+    static const struct TestConfig {
+        int32_t networkType;
+        std::vector<int32_t> transportTypes;
+    } testConfigs[] = {
+            {android::net::NT_CELLULAR, {IDnsResolver::TRANSPORT_CELLULAR}},
+            {android::net::NT_WIFI, {IDnsResolver::TRANSPORT_WIFI}},
+            {android::net::NT_BLUETOOTH, {IDnsResolver::TRANSPORT_BLUETOOTH}},
+            {android::net::NT_ETHERNET, {IDnsResolver::TRANSPORT_ETHERNET}},
+            {android::net::NT_VPN, {IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_AWARE, {IDnsResolver::TRANSPORT_WIFI_AWARE}},
+            {android::net::NT_LOWPAN, {IDnsResolver::TRANSPORT_LOWPAN}},
+            {android::net::NT_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_CELLULAR}},
+            {android::net::NT_WIFI_VPN,
+             {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI}},
+            {android::net::NT_BLUETOOTH_VPN,
+             {IDnsResolver::TRANSPORT_BLUETOOTH, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_BLUETOOTH_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_BLUETOOTH}},
+            {android::net::NT_ETHERNET_VPN,
+             {IDnsResolver::TRANSPORT_ETHERNET, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_ETHERNET_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_ETHERNET}},
+            {android::net::NT_UNKNOWN, {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_UNKNOWN,
+             {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_LOWPAN}},
+            {android::net::NT_UNKNOWN, {}},
+            {android::net::NT_UNKNOWN,
+             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_BLUETOOTH,
+              IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_WIFI,
+              IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI,
+              IDnsResolver::TRANSPORT_CELLULAR}},
+    };
+    for (const auto& config : testConfigs) {
+        EXPECT_EQ(config.networkType, convert_network_type(config.transportTypes));
+    }
+}
+
 namespace {
 
 constexpr int EAI_OK = 0;
diff --git a/stats.proto b/stats.proto
index abca793..403a008 100644
--- a/stats.proto
+++ b/stats.proto
@@ -63,6 +63,13 @@
     NS_R_NOTAUTH = 9;   // Not authoritative for zone
     NS_R_NOTZONE = 10;  // Zone of record different from zone section
     NS_R_MAX = 11;
+    // Define rcode=12~15(UNASSIGNED) in rcode enum type.
+    // Some DNS Servers might return undefined code to devices.
+    // Without the enum definition, that would be noise for our dashboard.
+    NS_R_UNASSIGNED12 = 12; // Unassigned
+    NS_R_UNASSIGNED13 = 13; // Unassigned
+    NS_R_UNASSIGNED14 = 14; // Unassigned
+    NS_R_UNASSIGNED15 = 15; // Unassigned
     // The following are EDNS extended rcodes
     NS_R_BADVERS = 16;
     // The following are TSIG errors
@@ -185,6 +192,8 @@
     NT_BLUETOOTH_VPN = 10;
     // Indicates this network uses an Ethernet+VPN transport.
     NT_ETHERNET_VPN = 11;
+    // Indicates this network uses a Wi-Fi+Cellular+VPN transport.
+    NT_WIFI_CELLULAR_VPN = 12;
 }
 
 enum CacheStatus{
diff --git a/tests/dns_responder/Android.bp b/tests/dns_responder/Android.bp
index b67bcee..a7ef021 100644
--- a/tests/dns_responder/Android.bp
+++ b/tests/dns_responder/Android.bp
@@ -1,14 +1,19 @@
 // TODO: Remove libnetd_test_dnsresponder after eliminating all users.
-cc_library_static {
+cc_test_library {
     name: "libnetd_test_dnsresponder",
     defaults: ["netd_defaults", "resolv_test_defaults"],
     shared_libs: [
-        "dnsresolver_aidl_interface-unstable-cpp",
         "libbinder",
         "libnetd_client",
+    ],
+    static_libs: [
+        "dnsresolver_aidl_interface-unstable-cpp",
+        "libcrypto_static",
         "libnetdutils",
         "libssl",
+        "libutils",
         "netd_aidl_interface-cpp",
+        "netd_event_listener_interface-cpp",
     ],
     srcs: [
         "dns_responder.cpp",
@@ -18,7 +23,7 @@
     export_include_dirs: ["."],
 }
 
-cc_library {
+cc_test_library {
     name: "libnetd_test_dnsresponder_ndk",
     defaults: ["netd_defaults", "resolv_test_defaults"],
     shared_libs: [
@@ -37,8 +42,8 @@
         "libcrypto_static",
         "libnetdutils",
         "libssl",
-        "netd_event_listener_interface-ndk_platform",
         "netd_aidl_interface-ndk_platform",
+        "netd_event_listener_interface-ndk_platform",
     ],
     srcs: [
         "dns_responder.cpp",
diff --git a/tests/dnsresolver_binder_test.cpp b/tests/dnsresolver_binder_test.cpp
index 1c6527f..6fb73f3 100644
--- a/tests/dnsresolver_binder_test.cpp
+++ b/tests/dnsresolver_binder_test.cpp
@@ -24,6 +24,7 @@
 #include <vector>
 
 #include <aidl/android/net/IDnsResolver.h>
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android/binder_manager.h>
@@ -245,6 +246,37 @@
     }
 }
 
+TEST_F(DnsResolverBinderTest, SetResolverConfiguration_TransportTypes) {
+    using ::testing::HasSubstr;
+    auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+    resolverParams.transportTypes = {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_VPN};
+    ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(resolverParams);
+    EXPECT_TRUE(status.isOk()) << status.getMessage();
+    // TODO: Find a way to fix a potential deadlock here if it's larger than pipe buffer
+    // size(65535).
+    android::base::unique_fd writeFd, readFd;
+    EXPECT_TRUE(Pipe(&readFd, &writeFd));
+    EXPECT_EQ(mDnsResolver->dump(writeFd.get(), nullptr, 0), 0);
+    writeFd.reset();
+    std::string str;
+    ASSERT_TRUE(ReadFdToString(readFd, &str)) << strerror(errno);
+    EXPECT_THAT(str, HasSubstr("WIFI_VPN"));
+}
+
+TEST_F(DnsResolverBinderTest, SetResolverConfiguration_TransportTypes_Default) {
+    using ::testing::HasSubstr;
+    auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
+    ::ndk::ScopedAStatus status = mDnsResolver->setResolverConfiguration(resolverParams);
+    EXPECT_TRUE(status.isOk()) << status.getMessage();
+    android::base::unique_fd writeFd, readFd;
+    EXPECT_TRUE(Pipe(&readFd, &writeFd));
+    EXPECT_EQ(mDnsResolver->dump(writeFd.get(), nullptr, 0), 0);
+    writeFd.reset();
+    std::string str;
+    ASSERT_TRUE(ReadFdToString(readFd, &str)) << strerror(errno);
+    EXPECT_THAT(str, HasSubstr("UNKNOWN"));
+}
+
 TEST_F(DnsResolverBinderTest, GetResolverInfo) {
     std::vector<std::string> servers = {"127.0.0.1", "127.0.0.2"};
     std::vector<std::string> domains = {"example.com"};
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index 9dda0f1..270f040 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -1087,7 +1087,7 @@
     test::DNSResponder dns;
     StartDns(dns, {});
     auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
-    resolverParams.experimentalOptions.hosts = invalidCustHosts;
+    resolverParams.resolverOptions.hosts = invalidCustHosts;
     ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
     for (const auto& hostname : {hostnameNoip, hostnameInvalidip}) {
         // The query won't get data from customized table because of invalid customized table
@@ -1162,7 +1162,7 @@
         StartDns(dns, config.dnsserverHosts);
 
         auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
-        resolverParams.experimentalOptions.hosts = config.customizedHosts;
+        resolverParams.resolverOptions.hosts = config.customizedHosts;
         ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
         const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
         ScopedAddrinfo result = safe_getaddrinfo(config.name.c_str(), nullptr, &hints);
@@ -1198,7 +1198,7 @@
     StartDns(dns, dnsSvHostV4V6);
     auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
 
-    resolverParams.experimentalOptions.hosts = custHostV4V6;
+    resolverParams.resolverOptions.hosts = custHostV4V6;
     ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
     const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
     ScopedAddrinfo result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
@@ -1206,7 +1206,7 @@
     EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
     EXPECT_EQ(0U, GetNumQueries(dns, hostnameV4V6));
 
-    resolverParams.experimentalOptions.hosts = {};
+    resolverParams.resolverOptions.hosts = {};
     ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
     result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
     ASSERT_TRUE(result != nullptr);
@@ -4139,7 +4139,7 @@
         ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
         parcel.servers = {listen_addr, listen_addr2};
         if (config.tcMode) {
-            parcel.experimentalOptions.tcMode = config.tcMode.value();
+            parcel.resolverOptions.tcMode = config.tcMode.value();
         }
         ASSERT_EQ(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk(), config.ret);