Snap for 7620055 from a8b0eec0461c195f0c44ab8ede96109b0d628131 to sc-release

Change-Id: I60018099ee401752621112d1dac5427435ad2627
diff --git a/Android.bp b/Android.bp
index 81e456a..696d721 100644
--- a/Android.bp
+++ b/Android.bp
@@ -53,7 +53,7 @@
     ],
 }
 
-dnsresolver_aidl_interface_lateststable_version = "V8"
+dnsresolver_aidl_interface_lateststable_version = "V9"
 
 cc_library_static {
     name: "dnsresolver_aidl_interface-lateststable-ndk_platform",
@@ -106,6 +106,7 @@
         "6",
         "7",
         "8",
+        "9",
     ],
     dumpapi: {
         no_license: true,
diff --git a/DnsResolverService.cpp b/DnsResolverService.cpp
index 899f726..7e29a9d 100644
--- a/DnsResolverService.cpp
+++ b/DnsResolverService.cpp
@@ -36,6 +36,7 @@
 #include "ResolverEventReporter.h"
 #include "resolv_cache.h"
 
+using aidl::android::net::ResolverOptionsParcel;
 using aidl::android::net::ResolverParamsParcel;
 using android::base::Join;
 using android::base::StringPrintf;
@@ -307,5 +308,13 @@
     return statusFromErrcode(res);
 }
 
+::ndk::ScopedAStatus DnsResolverService::setResolverOptions(int32_t netId,
+                                                            const ResolverOptionsParcel& options) {
+    // Locking happens in res_cache.cpp functions.
+    ENFORCE_NETWORK_STACK_PERMISSIONS();
+
+    return statusFromErrcode(resolv_set_options(netId, options));
+}
+
 }  // namespace net
 }  // namespace android
diff --git a/DnsResolverService.h b/DnsResolverService.h
index fe39301..8acd231 100644
--- a/DnsResolverService.h
+++ b/DnsResolverService.h
@@ -55,6 +55,8 @@
     ::ndk::ScopedAStatus destroyNetworkCache(int32_t netId) override;
     ::ndk::ScopedAStatus createNetworkCache(int32_t netId) override;
     ::ndk::ScopedAStatus flushNetworkCache(int32_t netId) override;
+    ::ndk::ScopedAStatus setResolverOptions(
+            int32_t netId, const aidl::android::net::ResolverOptionsParcel& options) override;
 
     // DNS64-related commands
     ::ndk::ScopedAStatus startPrefix64Discovery(int32_t netId) override;
diff --git a/aidl_api/dnsresolver_aidl_interface/9/.hash b/aidl_api/dnsresolver_aidl_interface/9/.hash
new file mode 100644
index 0000000..5b95881
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/.hash
@@ -0,0 +1 @@
+882638dc86e8afd0924ecf7c28db6cce572f7e7d
diff --git a/aidl_api/dnsresolver_aidl_interface/9/android/net/IDnsResolver.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/IDnsResolver.aidl
new file mode 100644
index 0000000..08f3e3a
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/android/net/IDnsResolver.aidl
@@ -0,0 +1,68 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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);
+  void setResolverOptions(int netId, in android.net.ResolverOptionsParcel optionParams);
+  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;
+  const int TRANSPORT_USB = 8;
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/9/android/net/ResolverHostsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/ResolverHostsParcel.aidl
new file mode 100644
index 0000000..c24eb61
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/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/9/android/net/ResolverOptionsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/ResolverOptionsParcel.aidl
new file mode 100644
index 0000000..e806d04
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/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/9/android/net/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/ResolverParamsParcel.aidl
new file mode 100644
index 0000000..8d0bf75
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/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;
+  @nullable android.net.ResolverOptionsParcel resolverOptions;
+  int[] transportTypes = {};
+}
diff --git a/aidl_api/dnsresolver_aidl_interface/9/android/net/resolv/aidl/DnsHealthEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/resolv/aidl/DnsHealthEventParcel.aidl
new file mode 100644
index 0000000..d32be91
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/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/9/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/resolv/aidl/IDnsResolverUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..d8accd1
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/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/9/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/resolv/aidl/Nat64PrefixEventParcel.aidl
new file mode 100644
index 0000000..2daccb0
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/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/9/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl b/aidl_api/dnsresolver_aidl_interface/9/android/net/resolv/aidl/PrivateDnsValidationEventParcel.aidl
new file mode 100644
index 0000000..e66e21c
--- /dev/null
+++ b/aidl_api/dnsresolver_aidl_interface/9/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 a8b3b86..08f3e3a 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/IDnsResolver.aidl
@@ -32,6 +32,7 @@
   void flushNetworkCache(int netId);
   void setPrefix64(int netId, @utf8InCpp String prefix);
   void registerUnsolicitedEventListener(android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener listener);
+  void setResolverOptions(int netId, in android.net.ResolverOptionsParcel optionParams);
   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/ResolverParamsParcel.aidl b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
index 8fec710..8d0bf75 100644
--- a/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
+++ b/aidl_api/dnsresolver_aidl_interface/current/android/net/ResolverParamsParcel.aidl
@@ -33,6 +33,6 @@
   @utf8InCpp String[] tlsFingerprints = {};
   @utf8InCpp String caCertificate = "";
   int tlsConnectTimeoutMs = 0;
-  android.net.ResolverOptionsParcel resolverOptions;
+  @nullable android.net.ResolverOptionsParcel resolverOptions;
   int[] transportTypes = {};
 }
diff --git a/binder/android/net/IDnsResolver.aidl b/binder/android/net/IDnsResolver.aidl
index 8ac1348..457fbfa 100644
--- a/binder/android/net/IDnsResolver.aidl
+++ b/binder/android/net/IDnsResolver.aidl
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.net.ResolverOptionsParcel;
 import android.net.ResolverParamsParcel;
 import android.net.metrics.INetdEventListener;
 import android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener;
@@ -233,4 +234,14 @@
     *         unix errno.
     */
     void registerUnsolicitedEventListener(IDnsResolverUnsolicitedEventListener listener);
+
+    /**
+     * Sets resolver options for the given network.
+     *
+     * @param netId the netId on which to set the options.
+     * @param optionParams the option parameters to be wrapped into parcel.
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         unix errno.
+     */
+    void setResolverOptions(int netId, in ResolverOptionsParcel optionParams);
 }
diff --git a/binder/android/net/ResolverParamsParcel.aidl b/binder/android/net/ResolverParamsParcel.aidl
index 9a8e843..5511f28 100644
--- a/binder/android/net/ResolverParamsParcel.aidl
+++ b/binder/android/net/ResolverParamsParcel.aidl
@@ -103,6 +103,7 @@
     /**
      * Knobs for OEM to control alternative behavior.
      */
+    @nullable
     ResolverOptionsParcel resolverOptions;
 
     /**
diff --git a/res_cache.cpp b/res_cache.cpp
index 5b03147..768dc77 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -67,6 +67,7 @@
 #include "util.h"
 
 using aidl::android::net::IDnsResolver;
+using aidl::android::net::ResolverOptionsParcel;
 using android::base::StringAppendF;
 using android::net::DnsQueryEvent;
 using android::net::DnsStats;
@@ -1003,7 +1004,23 @@
         dns_event_subsampling_map = resolv_get_dns_event_subsampling_map();
     }
     int nameserverCount() { return nameserverSockAddrs.size(); }
+    int setOptions(const ResolverOptionsParcel& resolverOptions) {
+        customizedTable.clear();
+        for (const auto& host : resolverOptions.hosts) {
+            if (!host.hostName.empty() && !host.ipAddr.empty())
+                customizedTable.emplace(host.hostName, host.ipAddr);
+        }
 
+        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;
+        }
+        tc_mode = resolverOptions.tcMode;
+        enforceDnsUid = resolverOptions.enforceDnsUid;
+        return 0;
+    }
     const unsigned netid;
     std::unique_ptr<Cache> cache;
     std::vector<std::string> nameservers;
@@ -1600,7 +1617,7 @@
 
 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::optional<ResolverOptionsParcel> optionalResolverOptions,
                            const std::vector<int32_t>& transportTypes) {
     std::vector<std::string> nameservers = filter_nameservers(servers);
     const int numservers = static_cast<int>(nameservers.size());
@@ -1654,26 +1671,22 @@
         LOG(WARNING) << __func__ << ": netid = " << netid << ", failed to set dns stats";
         return -EINVAL;
     }
-    netconfig->customizedTable.clear();
-    for (const auto& host : resolverOptions.hosts) {
-        if (!host.hostName.empty() && !host.ipAddr.empty())
-            netconfig->customizedTable.emplace(host.hostName, host.ipAddr);
-    }
-
-    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 = resolverOptions.tcMode;
-    netconfig->enforceDnsUid = resolverOptions.enforceDnsUid;
-
     netconfig->transportTypes = transportTypes;
-
+    if (optionalResolverOptions.has_value()) {
+        const ResolverOptionsParcel& resolverOptions = optionalResolverOptions.value();
+        return netconfig->setOptions(resolverOptions);
+    }
     return 0;
 }
 
+int resolv_set_options(unsigned netid, const ResolverOptionsParcel& options) {
+    std::lock_guard guard(cache_mutex);
+    NetConfig* netconfig = find_netconfig_locked(netid);
+
+    if (netconfig == nullptr) return -ENONET;
+    return netconfig->setOptions(options);
+}
+
 static bool resolv_is_nameservers_equal(const std::vector<std::string>& oldServers,
                                         const std::vector<std::string>& newServers) {
     const std::set<std::string> olds(oldServers.begin(), oldServers.end());
diff --git a/resolv_cache.h b/resolv_cache.h
index 15baa14..970459b 100644
--- a/resolv_cache.h
+++ b/resolv_cache.h
@@ -79,12 +79,12 @@
 // 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::ResolverOptionsParcel& resolverOptions =
-                                   {{} /* hosts */,
-                                    aidl::android::net::IDnsResolver::TC_MODE_DEFAULT,
-                                    false /* enforceDnsUid */},
+                           std::optional<aidl::android::net::ResolverOptionsParcel> resolverOptions,
                            const std::vector<int32_t>& transportTypes = {});
 
+// Sets options for a given network.
+int resolv_set_options(unsigned netid, const aidl::android::net::ResolverOptionsParcel& options);
+
 // Creates the cache associated with the given network.
 int resolv_create_cache_for_net(unsigned netid);
 
diff --git a/tests/dns_responder/dns_responder_client_ndk.cpp b/tests/dns_responder/dns_responder_client_ndk.cpp
index 257e815..56a4a8d 100644
--- a/tests/dns_responder/dns_responder_client_ndk.cpp
+++ b/tests/dns_responder/dns_responder_client_ndk.cpp
@@ -30,6 +30,7 @@
 
 using aidl::android::net::IDnsResolver;
 using aidl::android::net::INetd;
+using aidl::android::net::ResolverOptionsParcel;
 using aidl::android::net::ResolverParamsParcel;
 using android::net::ResolverStats;
 
@@ -77,6 +78,7 @@
     paramsParcel.tlsServers = tlsServers;
     paramsParcel.tlsFingerprints = {};
     paramsParcel.caCertificate = caCert;
+    paramsParcel.resolverOptions = ResolverOptionsParcel{};  // optional, must be explicitly set.
 
     // Note, do not remove this otherwise the ResolverTest#ConnectTlsServerTimeout won't pass in M4
     // module.
diff --git a/tests/dnsresolver_binder_test.cpp b/tests/dnsresolver_binder_test.cpp
index 4e6e5da..55a67e1 100644
--- a/tests/dnsresolver_binder_test.cpp
+++ b/tests/dnsresolver_binder_test.cpp
@@ -208,9 +208,10 @@
         return o;
     }
 
-    std::string toString(const ResolverOptionsParcel& parms) {
+    std::string toString(const std::optional<ResolverOptionsParcel>& parms) {
+        if (!parms.has_value()) return "(null)";
         return fmt::format("ResolverOptionsParcel{{hosts: [{}], tcMode: {}, enforceDnsUid: {}}}",
-                           toString(parms.hosts), parms.tcMode, parms.enforceDnsUid);
+                           toString(parms->hosts), parms->tcMode, parms->enforceDnsUid);
     }
 
     std::string toString(const ResolverParamsParcel& parms) {
@@ -625,3 +626,18 @@
     EXPECT_TRUE(mDnsResolver->setLogSeverity(IDnsResolver::DNS_RESOLVER_LOG_WARNING).isOk());
     mExpectedLogData.push_back({"setLogSeverity(3)", "setLogSeverity.*3"});
 }
+
+TEST_F(DnsResolverBinderTest, SetResolverOptions) {
+    SKIP_IF_REMOTE_VERSION_LESS_THAN(mDnsResolver.get(), 9);
+    ResolverOptionsParcel options;
+    options.tcMode = 1;
+    options.enforceDnsUid = true;
+    EXPECT_TRUE(mDnsResolver->setResolverOptions(TEST_NETID, options).isOk());
+    mExpectedLogData.push_back(
+            {"setResolverOptions(30, " + toString(options) + ")", "setResolverOptions.*30"});
+    EXPECT_EQ(ENONET, mDnsResolver->setResolverOptions(-1, options).getServiceSpecificError());
+    mExpectedLogData.push_back({"setResolverOptions(-1, " + toString(options) +
+                                        ") -> ServiceSpecificException(64, \"Machine is not on the "
+                                        "network\")",
+                                "setResolverOptions.*-1.*64"});
+}
diff --git a/tests/resolv_callback_unit_test.cpp b/tests/resolv_callback_unit_test.cpp
index 898a2f9..48e4937 100644
--- a/tests/resolv_callback_unit_test.cpp
+++ b/tests/resolv_callback_unit_test.cpp
@@ -128,7 +128,7 @@
                 .base_timeout_msec = 1000,
                 .retry_count = 2,
         };
-        return resolv_set_nameservers(TEST_NETID, servers, domains, params);
+        return resolv_set_nameservers(TEST_NETID, servers, domains, params, std::nullopt);
     }
 
     const android_net_context mNetcontext = {
diff --git a/tests/resolv_gold_test.cpp b/tests/resolv_gold_test.cpp
index c5b24aa..33acda0 100644
--- a/tests/resolv_gold_test.cpp
+++ b/tests/resolv_gold_test.cpp
@@ -109,7 +109,7 @@
         ASSERT_EQ(privateDnsConfiguration.set(TEST_NETID, fwmark.intValue, tlsServers, tlsHostname,
                                               caCert),
                   0);
-        ASSERT_EQ(resolv_set_nameservers(TEST_NETID, servers, domains, kParams), 0);
+        ASSERT_EQ(resolv_set_nameservers(TEST_NETID, servers, domains, kParams, std::nullopt), 0);
     }
 
     void SetResolvers() { SetResolverConfiguration(kDefaultServers, kDefaultSearchDomains); }
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index 48c3eea..b602d85 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -102,6 +102,7 @@
 
 using aidl::android::net::IDnsResolver;
 using aidl::android::net::INetd;
+using aidl::android::net::ResolverOptionsParcel;
 using aidl::android::net::ResolverParamsParcel;
 using aidl::android::net::metrics::INetdEventListener;
 using aidl::android::net::resolv::aidl::DnsHealthEventParcel;
@@ -237,6 +238,8 @@
         mDnsClient.SetUp();
         sDnsMetricsListener->reset();
         sUnsolicitedEventListener->reset();
+        mIsResolverOptionIPCSupported =
+                DnsResponderClient::isRemoteVersionSupported(mDnsClient.resolvService(), 9);
     }
 
     void TearDown() {
@@ -404,6 +407,8 @@
 
     DnsResponderClient mDnsClient;
 
+    bool mIsResolverOptionIPCSupported = false;
+
     // Use a shared static DNS listener for all tests to avoid registering lots of listeners
     // which may be released late until process terminated. Currently, registered DNS listener
     // is removed by binder death notification which is fired when the process hosting an
@@ -1315,8 +1320,18 @@
     test::DNSResponder dns;
     StartDns(dns, {});
     auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
-    resolverParams.resolverOptions.hosts = invalidCustHosts;
+
+    ResolverOptionsParcel resolverOptions;
+    resolverOptions.hosts = invalidCustHosts;
+    if (!mIsResolverOptionIPCSupported) {
+        resolverParams.resolverOptions = resolverOptions;
+    }
     ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+    if (mIsResolverOptionIPCSupported) {
+        ASSERT_TRUE(mDnsClient.resolvService()
+                            ->setResolverOptions(resolverParams.netId, resolverOptions)
+                            .isOk());
+    }
     for (const auto& hostname : {hostnameNoip, hostnameInvalidip}) {
         // The query won't get data from customized table because of invalid customized table
         // and DNSResponder also has no records. hostnameNoip has never registered and
@@ -1390,8 +1405,18 @@
         StartDns(dns, config.dnsserverHosts);
 
         auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
-        resolverParams.resolverOptions.hosts = config.customizedHosts;
+        ResolverOptionsParcel resolverOptions;
+        resolverOptions.hosts = config.customizedHosts;
+        if (!mIsResolverOptionIPCSupported) {
+            resolverParams.resolverOptions = resolverOptions;
+        }
         ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+
+        if (mIsResolverOptionIPCSupported) {
+            ASSERT_TRUE(mDnsClient.resolvService()
+                                ->setResolverOptions(resolverParams.netId, resolverOptions)
+                                .isOk());
+        }
         const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
         ScopedAddrinfo result = safe_getaddrinfo(config.name.c_str(), nullptr, &hints);
         if (config.customizedHosts.empty() && config.dnsserverHosts.empty()) {
@@ -1426,16 +1451,34 @@
     StartDns(dns, dnsSvHostV4V6);
     auto resolverParams = DnsResponderClient::GetDefaultResolverParamsParcel();
 
-    resolverParams.resolverOptions.hosts = custHostV4V6;
+    ResolverOptionsParcel resolverOptions;
+    resolverOptions.hosts = custHostV4V6;
+    if (!mIsResolverOptionIPCSupported) {
+        resolverParams.resolverOptions = resolverOptions;
+    }
     ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+
+    if (mIsResolverOptionIPCSupported) {
+        ASSERT_TRUE(mDnsClient.resolvService()
+                            ->setResolverOptions(resolverParams.netId, resolverOptions)
+                            .isOk());
+    }
+
     const addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
     ScopedAddrinfo result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
     ASSERT_TRUE(result != nullptr);
     EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
     EXPECT_EQ(0U, GetNumQueries(dns, hostnameV4V6));
 
-    resolverParams.resolverOptions.hosts = {};
-    ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+    resolverOptions.hosts = {};
+    if (!mIsResolverOptionIPCSupported) {
+        resolverParams.resolverOptions = resolverOptions;
+        ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(resolverParams).isOk());
+    } else {
+        ASSERT_TRUE(mDnsClient.resolvService()
+                            ->setResolverOptions(resolverParams.netId, resolverOptions)
+                            .isOk());
+    }
     result = safe_getaddrinfo(hostnameV4V6, nullptr, &hints);
     ASSERT_TRUE(result != nullptr);
     EXPECT_THAT(ToStrings(result), testing::UnorderedElementsAreArray({dnsSvAddrV4, dnsSvAddrV6}));
@@ -4365,8 +4408,17 @@
     }
 
     memset(buf, 0, MAXPACKET);
-    parcel.resolverOptions.enforceDnsUid = true;
-    ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk());
+    ResolverOptionsParcel resolverOptions;
+    resolverOptions.enforceDnsUid = true;
+    if (!mIsResolverOptionIPCSupported) {
+        parcel.resolverOptions = resolverOptions;
+        ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk());
+    } else {
+        ASSERT_TRUE(mDnsClient.resolvService()
+                            ->setResolverOptions(parcel.netId, resolverOptions)
+                            .isOk());
+    }
+
     {
         ScopeBlockedUIDRule scopeBlockUidRule(netdService, TEST_UID);
         // Dns Queries should NOT be blocked
@@ -5042,8 +5094,8 @@
             // clang-format off
             {std::nullopt,                                      true,  0}, /* mode unset */
             {aidl::android::net::IDnsResolver::TC_MODE_DEFAULT, true,  0}, /* default mode */
+            {-666,                                              false, 0}, /* invalid input */
             {aidl::android::net::IDnsResolver::TC_MODE_UDP_TCP, true,  1}, /* alternative mode */
-            {-666,                                              false, 1}, /* invalid input */
             // clang-format on
     };
 
@@ -5052,10 +5104,21 @@
 
         ResolverParamsParcel parcel = DnsResponderClient::GetDefaultResolverParamsParcel();
         parcel.servers = {listen_addr, listen_addr2};
-        if (config.tcMode) {
-            parcel.resolverOptions.tcMode = config.tcMode.value();
+        ResolverOptionsParcel resolverOptions;
+        if (config.tcMode.has_value()) resolverOptions.tcMode = config.tcMode.value();
+        if (!mIsResolverOptionIPCSupported) {
+            parcel.resolverOptions = resolverOptions;
+            ASSERT_EQ(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk(),
+                      config.ret);
+        } else {
+            ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk());
         }
-        ASSERT_EQ(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk(), config.ret);
+        if (mIsResolverOptionIPCSupported) {
+            ASSERT_EQ(mDnsClient.resolvService()
+                              ->setResolverOptions(parcel.netId, resolverOptions)
+                              .isOk(),
+                      config.ret);
+        }
 
         const addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_DGRAM};
         ScopedAddrinfo result = safe_getaddrinfo("hello", nullptr, &hints);
@@ -5075,7 +5138,12 @@
         // Clear the stats to make the resolver always choose the same server for the first query.
         parcel.servers.clear();
         parcel.tlsServers.clear();
-        ASSERT_EQ(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk(), config.ret);
+        if (!mIsResolverOptionIPCSupported) {
+            ASSERT_EQ(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk(),
+                      config.ret);
+        } else {
+            ASSERT_TRUE(mDnsClient.resolvService()->setResolverConfiguration(parcel).isOk());
+        }
     }
 }
 
diff --git a/tests/resolv_unit_test.cpp b/tests/resolv_unit_test.cpp
index befca9e..638dc33 100644
--- a/tests/resolv_unit_test.cpp
+++ b/tests/resolv_unit_test.cpp
@@ -123,7 +123,11 @@
         dns.clearQueries();
     }
 
-    int SetResolvers() { return resolv_set_nameservers(TEST_NETID, servers, domains, params); }
+    int SetResolvers() { return SetResolvers(servers); }
+
+    int SetResolvers(std::vector<std::string> servers) {
+        return resolv_set_nameservers(TEST_NETID, servers, domains, params, std::nullopt);
+    }
 
     const android_net_context mNetcontext = {
             .app_netid = TEST_NETID,