Merge "Fix race when starting NetworkMonitor"
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index f9e0af2..06c32c6 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -22,6 +22,10 @@
 import static android.net.NetworkUtils.resNetworkSend;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -30,12 +34,18 @@
 import android.os.CancellationSignal;
 import android.os.Looper;
 import android.system.ErrnoException;
+import android.system.Os;
 import android.util.Log;
 
+import libcore.io.IoUtils;
+
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
@@ -52,6 +62,7 @@
     private static final String TAG = "DnsResolver";
     private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
     private static final int MAXPACKET = 8 * 1024;
+    private static final int SLEEP_TIME_MS = 2;
 
     @IntDef(prefix = { "CLASS_" }, value = {
             CLASS_IN
@@ -188,9 +199,9 @@
      * Send a raw DNS query.
      * The answer will be provided asynchronously through the provided {@link AnswerCallback}.
      *
-     * @param network {@link Network} specifying which network for querying.
+     * @param network {@link Network} specifying which network to query on.
      *         {@code null} for query on default network.
-     * @param query blob message
+     * @param query blob message to query
      * @param flags flags as a combination of the FLAGS_* constants
      * @param executor The {@link Executor} that the callback should be executed on.
      * @param cancellationSignal used by the caller to signal if the query should be
@@ -211,21 +222,26 @@
             queryfd = resNetworkSend((network != null
                 ? network.netId : NETID_UNSET), query, query.length, flags);
         } catch (ErrnoException e) {
-            callback.onQueryException(e);
+            executor.execute(() -> {
+                callback.onQueryException(e);
+            });
             return;
         }
 
-        maybeAddCancellationSignal(cancellationSignal, queryfd, lock);
-        registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+        synchronized (lock)  {
+            registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+            if (cancellationSignal == null) return;
+            addCancellationSignal(cancellationSignal, queryfd, lock);
+        }
     }
 
     /**
      * Send a DNS query with the specified name, class and query type.
      * The answer will be provided asynchronously through the provided {@link AnswerCallback}.
      *
-     * @param network {@link Network} specifying which network for querying.
+     * @param network {@link Network} specifying which network to query on.
      *         {@code null} for query on default network.
-     * @param domain domain name for querying
+     * @param domain domain name to query
      * @param nsClass dns class as one of the CLASS_* constants
      * @param nsType dns resource record (RR) type as one of the TYPE_* constants
      * @param flags flags as a combination of the FLAGS_* constants
@@ -249,12 +265,152 @@
             queryfd = resNetworkQuery((network != null
                     ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags);
         } catch (ErrnoException e) {
-            callback.onQueryException(e);
+            executor.execute(() -> {
+                callback.onQueryException(e);
+            });
             return;
         }
+        synchronized (lock)  {
+            registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+            if (cancellationSignal == null) return;
+            addCancellationSignal(cancellationSignal, queryfd, lock);
+        }
+    }
 
-        maybeAddCancellationSignal(cancellationSignal, queryfd, lock);
-        registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+    private class InetAddressAnswerAccumulator extends InetAddressAnswerCallback {
+        private final List<InetAddress> mAllAnswers;
+        private ParseException mParseException;
+        private ErrnoException mErrnoException;
+        private final InetAddressAnswerCallback mUserCallback;
+        private final int mTargetAnswerCount;
+        private int mReceivedAnswerCount = 0;
+
+        InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerCallback callback) {
+            mTargetAnswerCount = size;
+            mAllAnswers = new ArrayList<>();
+            mUserCallback = callback;
+        }
+
+        private boolean maybeReportException() {
+            if (mErrnoException != null) {
+                mUserCallback.onQueryException(mErrnoException);
+                return true;
+            }
+            if (mParseException != null) {
+                mUserCallback.onParseException(mParseException);
+                return true;
+            }
+            return false;
+        }
+
+        private void maybeReportAnswer() {
+            if (++mReceivedAnswerCount != mTargetAnswerCount) return;
+            if (mAllAnswers.isEmpty() && maybeReportException()) return;
+            // TODO: Do RFC6724 sort.
+            mUserCallback.onAnswer(mAllAnswers);
+        }
+
+        @Override
+        public void onAnswer(@NonNull List<InetAddress> answer) {
+            mAllAnswers.addAll(answer);
+            maybeReportAnswer();
+        }
+
+        @Override
+        public void onParseException(@NonNull ParseException e) {
+            mParseException = e;
+            maybeReportAnswer();
+        }
+
+        @Override
+        public void onQueryException(@NonNull ErrnoException e) {
+            mErrnoException = e;
+            maybeReportAnswer();
+        }
+    }
+
+    /**
+     * Send a DNS query with the specified name, get back a set of InetAddresses asynchronously.
+     * The answer will be provided asynchronously through the provided
+     * {@link InetAddressAnswerCallback}.
+     *
+     * @param network {@link Network} specifying which network to query on.
+     *         {@code null} for query on default network.
+     * @param domain domain name to query
+     * @param flags flags as a combination of the FLAGS_* constants
+     * @param executor The {@link Executor} that the callback should be executed on.
+     * @param cancellationSignal used by the caller to signal if the query should be
+     *    cancelled. May be {@code null}.
+     * @param callback an {@link InetAddressAnswerCallback} which will be called to notify the
+     *    caller of the result of dns query.
+     */
+    public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
+            @NonNull @CallbackExecutor Executor executor,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull InetAddressAnswerCallback callback) {
+        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+            return;
+        }
+        final Object lock = new Object();
+        final boolean queryIpv6 = haveIpv6(network);
+        final boolean queryIpv4 = haveIpv4(network);
+
+        final FileDescriptor v4fd;
+        final FileDescriptor v6fd;
+
+        int queryCount = 0;
+
+        if (queryIpv6) {
+            try {
+                v6fd = resNetworkQuery((network != null
+                        ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags);
+            } catch (ErrnoException e) {
+                executor.execute(() -> {
+                    callback.onQueryException(e);
+                });
+                return;
+            }
+            queryCount++;
+        } else v6fd = null;
+
+        // TODO: Use device flag to control the sleep time.
+        // Avoiding gateways drop packets if queries are sent too close together
+        try {
+            Thread.sleep(SLEEP_TIME_MS);
+        } catch (InterruptedException ex) { }
+
+        if (queryIpv4) {
+            try {
+                v4fd = resNetworkQuery((network != null
+                        ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags);
+            } catch (ErrnoException e) {
+                if (queryIpv6) resNetworkCancel(v6fd);  // Closes fd, marks it invalid.
+                executor.execute(() -> {
+                    callback.onQueryException(e);
+                });
+                return;
+            }
+            queryCount++;
+        } else v4fd = null;
+
+        final InetAddressAnswerAccumulator accumulator =
+                new InetAddressAnswerAccumulator(queryCount, callback);
+
+        synchronized (lock)  {
+            if (queryIpv6) {
+                registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
+            }
+            if (queryIpv4) {
+                registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
+            }
+            if (cancellationSignal == null) return;
+            cancellationSignal.setOnCancelListener(() -> {
+                synchronized (lock)  {
+                    if (queryIpv4) cancelQuery(v4fd);
+                    if (queryIpv6) cancelQuery(v6fd);
+                }
+            });
+        }
     }
 
     private <T> void registerFDListener(@NonNull Executor executor,
@@ -271,7 +427,7 @@
                             }
                             byte[] answerbuf = null;
                             try {
-                                answerbuf = resNetworkResult(fd);
+                                answerbuf = resNetworkResult(fd);  // Closes fd, marks it invalid.
                             } catch (ErrnoException e) {
                                 Log.e(TAG, "resNetworkResult:" + e.toString());
                                 answerCallback.onQueryException(e);
@@ -291,19 +447,53 @@
                 });
     }
 
-    private void maybeAddCancellationSignal(@Nullable CancellationSignal cancellationSignal,
+    private void cancelQuery(@NonNull FileDescriptor queryfd) {
+        if (!queryfd.valid()) return;
+        Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
+        resNetworkCancel(queryfd);  // Closes fd, marks it invalid.
+    }
+
+    private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
             @NonNull FileDescriptor queryfd, @NonNull Object lock) {
-        if (cancellationSignal == null) return;
         cancellationSignal.setOnCancelListener(() -> {
             synchronized (lock)  {
-                if (!queryfd.valid()) return;
-                Looper.getMainLooper().getQueue()
-                        .removeOnFileDescriptorEventListener(queryfd);
-                resNetworkCancel(queryfd);
+                cancelQuery(queryfd);
             }
         });
     }
 
+    // These two functions match the behaviour of have_ipv4 and have_ipv6 in the native resolver.
+    private boolean haveIpv4(@Nullable Network network) {
+        final SocketAddress addrIpv4 =
+                new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
+        return checkConnectivity(network, AF_INET, addrIpv4);
+    }
+
+    private boolean haveIpv6(@Nullable Network network) {
+        final SocketAddress addrIpv6 =
+                new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
+        return checkConnectivity(network, AF_INET6, addrIpv6);
+    }
+
+    private boolean checkConnectivity(@Nullable Network network,
+            int domain, @NonNull SocketAddress addr) {
+        final FileDescriptor socket;
+        try {
+            socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
+        } catch (ErrnoException e) {
+            return false;
+        }
+        try {
+            if (network != null) network.bindSocket(socket);
+            Os.connect(socket, addr);
+        } catch (IOException | ErrnoException e) {
+            return false;
+        } finally {
+            IoUtils.closeQuietly(socket);
+        }
+        return true;
+    }
+
     private static class DnsAddressAnswer extends DnsPacket {
         private static final String TAG = "DnsResolver.DnsAddressAnswer";
         private static final boolean DBG = false;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 873bd49..971dd23 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -63,6 +63,7 @@
 import android.net.ConnectivityManager;
 import android.net.ICaptivePortal;
 import android.net.IConnectivityManager;
+import android.net.IDnsResolver;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
 import android.net.INetdEventCallback;
@@ -294,6 +295,8 @@
 
     private INetworkManagementService mNMS;
     @VisibleForTesting
+    protected IDnsResolver mDnsResolver;
+    @VisibleForTesting
     protected INetd mNetd;
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
@@ -525,6 +528,11 @@
         return sMagicDecoderRing.get(what, Integer.toString(what));
     }
 
+    private static IDnsResolver getDnsResolver() {
+        return IDnsResolver.Stub
+                .asInterface(ServiceManager.getService("dnsresolver"));
+    }
+
     /** Handler thread used for both of the handlers below. */
     @VisibleForTesting
     protected final HandlerThread mHandlerThread;
@@ -810,13 +818,14 @@
 
     public ConnectivityService(Context context, INetworkManagementService netManager,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
-        this(context, netManager, statsService, policyManager, new IpConnectivityLog());
+        this(context, netManager, statsService, policyManager,
+            getDnsResolver(), new IpConnectivityLog());
     }
 
     @VisibleForTesting
     protected ConnectivityService(Context context, INetworkManagementService netManager,
             INetworkStatsService statsService, INetworkPolicyManager policyManager,
-            IpConnectivityLog logger) {
+            IDnsResolver dnsresolver, IpConnectivityLog logger) {
         if (DBG) log("ConnectivityService starting up");
 
         mSystemProperties = getSystemProperties();
@@ -853,6 +862,7 @@
         mPolicyManagerInternal = checkNotNull(
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
                 "missing NetworkPolicyManagerInternal");
+        mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver");
         mProxyTracker = makeProxyTracker();
 
         mNetd = NetdService.getInstance();
@@ -1006,7 +1016,7 @@
 
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
 
-        mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
+        mDnsManager = new DnsManager(mContext, mDnsResolver, mSystemProperties);
         registerPrivateDnsSettingsCallbacks();
     }
 
@@ -3021,9 +3031,9 @@
             // NetworkFactories, so network traffic isn't interrupted for an unnecessarily
             // long time.
             try {
-                mNMS.removeNetwork(nai.network.netId);
-            } catch (Exception e) {
-                loge("Exception removing network: " + e);
+                mNetd.networkDestroy(nai.network.netId);
+            } catch (RemoteException | ServiceSpecificException e) {
+                loge("Exception destroying network: " + e);
             }
             mDnsManager.removeNetwork(nai.network);
         }
@@ -5379,8 +5389,8 @@
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                 new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
-                mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mNMS,
-                factorySerialNumber);
+                mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver,
+                mNMS, factorySerialNumber);
         // Make sure the network capabilities reflect what the agent info says.
         nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index d8bb635..1913635 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -30,13 +30,15 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.IDnsResolver;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.Uri;
 import android.net.shared.PrivateDnsConfig;
 import android.os.Binder;
-import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -229,7 +231,7 @@
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
-    private final INetworkManagementService mNMS;
+    private final IDnsResolver mDnsResolver;
     private final MockableSystemProperties mSystemProperties;
     // TODO: Replace these Maps with SparseArrays.
     private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
@@ -243,10 +245,10 @@
     private String mPrivateDnsMode;
     private String mPrivateDnsSpecifier;
 
-    public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
+    public DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp) {
         mContext = ctx;
         mContentResolver = mContext.getContentResolver();
-        mNMS = nms;
+        mDnsResolver = dnsResolver;
         mSystemProperties = sp;
         mPrivateDnsMap = new HashMap<>();
         mPrivateDnsValidationMap = new HashMap<>();
@@ -260,6 +262,12 @@
     }
 
     public void removeNetwork(Network network) {
+        try {
+            mDnsResolver.clearResolverConfiguration(network.netId);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Error clearing DNS configuration: " + e);
+            return;
+        }
         mPrivateDnsMap.remove(network.netId);
         mPrivateDnsValidationMap.remove(network.netId);
     }
@@ -344,10 +352,12 @@
         Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
                 netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
                 Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
+        final String[] tlsFingerprints = new String[0];
         try {
-            mNMS.setDnsConfigurationForNetwork(
-                    netId, assignedServers, domainStrs, params, tlsHostname, tlsServers);
-        } catch (Exception e) {
+            mDnsResolver.setResolverConfiguration(
+                    netId, assignedServers, domainStrs, params,
+                    tlsHostname, tlsServers, tlsFingerprints);
+        } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error setting DNS configuration: " + e);
             return;
         }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 262ba7a..66bd27c 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity;
 
 import android.net.ConnectivityManager;
+import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.InetAddresses;
 import android.net.InterfaceConfiguration;
@@ -65,6 +66,7 @@
         NetworkInfo.State.SUSPENDED,
     };
 
+    private final IDnsResolver mDnsResolver;
     private final INetd mNetd;
     private final INetworkManagementService mNMService;
 
@@ -84,7 +86,9 @@
     private Inet6Address mIPv6Address;
     private State mState = State.IDLE;
 
-    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) {
+    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
+            INetworkManagementService nmService) {
+        mDnsResolver = dnsResolver;
         mNetd = netd;
         mNMService = nmService;
         mNetwork = nai;
@@ -269,7 +273,7 @@
 
     private void startPrefixDiscovery() {
         try {
-            mNetd.resolverStartPrefix64Discovery(getNetId());
+            mDnsResolver.startPrefix64Discovery(getNetId());
             mState = State.DISCOVERING;
         } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
@@ -278,7 +282,7 @@
 
     private void stopPrefixDiscovery() {
         try {
-            mNetd.resolverStopPrefix64Discovery(getNetId());
+            mDnsResolver.stopPrefix64Discovery(getNetId());
         } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
         }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index aa05d9a..cfa9131 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity;
 
 import android.content.Context;
+import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.INetworkMonitor;
 import android.net.LinkProperties;
@@ -257,7 +258,7 @@
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
             NetworkMisc misc, ConnectivityService connService, INetd netd,
-            INetworkManagementService nms, int factorySerialNumber) {
+            IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
         this.messenger = messenger;
         asyncChannel = ac;
         network = net;
@@ -265,7 +266,7 @@
         linkProperties = lp;
         networkCapabilities = nc;
         currentScore = score;
-        clatd = new Nat464Xlat(this, netd, nms);
+        clatd = new Nat464Xlat(this, netd, dnsResolver, nms);
         mConnService = connService;
         mContext = context;
         mHandler = handler;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c7d46e6..46a8955 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -104,6 +104,7 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
@@ -240,6 +241,7 @@
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     private MockContext mServiceContext;
     private WrappedConnectivityService mService;
@@ -256,6 +258,7 @@
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
     @Mock INetworkPolicyManager mNpm;
+    @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
     @Mock NetworkStackClient mNetworkStack;
 
@@ -1053,8 +1056,8 @@
 
         public WrappedConnectivityService(Context context, INetworkManagementService netManager,
                 INetworkStatsService statsService, INetworkPolicyManager policyManager,
-                IpConnectivityLog log, INetd netd) {
-            super(context, netManager, statsService, policyManager, log);
+                IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) {
+            super(context, netManager, statsService, policyManager, dnsResolver, log);
             mNetd = netd;
             mLingerDelayMs = TEST_LINGER_DELAY_MS;
         }
@@ -1218,7 +1221,8 @@
                 mStatsService,
                 mNpm,
                 mock(IpConnectivityLog.class),
-                mMockNetd);
+                mMockNetd,
+                mMockDnsResolver);
 
         final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
                 ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -4777,14 +4781,14 @@
         ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
 
         // Clear any interactions that occur as a result of CS starting up.
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
 
-        final String[] EMPTY_STRING_ARRAY = new String[0];
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         waitForIdle();
-        verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
-                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
-        verifyNoMoreInteractions(mNetworkManagementService);
+        verify(mMockDnsResolver, never()).setResolverConfiguration(
+                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""),
+                eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+        verifyNoMoreInteractions(mMockDnsResolver);
 
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4801,28 +4805,29 @@
         mCellNetworkAgent.connect(false);
         waitForIdle();
         // CS tells netd about the empty DNS config for this network.
-        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
-                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
-        reset(mNetworkManagementService);
+        verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
+                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""),
+                eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+        reset(mMockDnsResolver);
 
         cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+        verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                eq(""), tlsServers.capture());
+                eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
         assertEquals(1, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
         // Opportunistic mode.
         assertTrue(ArrayUtils.contains(tlsServers.getValue(), "2001:db8::1"));
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
 
         cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+        verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                eq(""), tlsServers.capture());
+                eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
         assertEquals(2, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
@@ -4830,7 +4835,7 @@
         assertEquals(2, tlsServers.getValue().length);
         assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
 
         final String TLS_SPECIFIER = "tls.example.com";
         final String TLS_SERVER6 = "2001:db8:53::53";
@@ -4840,22 +4845,21 @@
                 new PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS).toParcel());
 
         waitForIdle();
-        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+        verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                eq(TLS_SPECIFIER), eq(TLS_SERVERS));
+                eq(TLS_SPECIFIER), eq(TLS_SERVERS), eq(EMPTY_STRING_ARRAY));
         assertEquals(2, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
     }
 
     @Test
     public void testPrivateDnsSettingsChange() throws Exception {
-        final String[] EMPTY_STRING_ARRAY = new String[0];
         ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
 
         // Clear any interactions that occur as a result of CS starting up.
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
 
         // The default on Android is opportunistic mode ("Automatic").
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
@@ -4868,9 +4872,10 @@
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         waitForIdle();
         // CS tells netd about the empty DNS config for this network.
-        verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
-                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
-        verifyNoMoreInteractions(mNetworkManagementService);
+        verify(mMockDnsResolver, never()).setResolverConfiguration(
+                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""),
+                eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+        verifyNoMoreInteractions(mMockDnsResolver);
 
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4889,9 +4894,9 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         mCellNetworkAgent.connect(false);
         waitForIdle();
-        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+        verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                eq(""), tlsServers.capture());
+                eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
         assertEquals(2, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
@@ -4899,7 +4904,7 @@
         assertEquals(2, tlsServers.getValue().length);
         assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
         cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
         cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
                 mCellNetworkAgent);
@@ -4911,26 +4916,26 @@
         assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
 
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
-        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+        verify(mMockDnsResolver, times(1)).setResolverConfiguration(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                eq(""), eq(EMPTY_STRING_ARRAY));
+                eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
         assertEquals(2, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
         cellNetworkCallback.assertNoCallback();
 
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
-        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+        verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                eq(""), tlsServers.capture());
+                eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
         assertEquals(2, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
         assertEquals(2, tlsServers.getValue().length);
         assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
-        reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
         cellNetworkCallback.assertNoCallback();
 
         setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
@@ -5761,6 +5766,7 @@
         cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
         cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
         reset(mNetworkManagementService);
+        reset(mMockDnsResolver);
         when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
                 .thenReturn(getClatInterfaceConfig(myIpv4));
 
@@ -5768,7 +5774,7 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         mCellNetworkAgent.connect(true);
         networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-        verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+        verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
 
         // Switching default network updates TCP buffer sizes.
         verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
@@ -5778,17 +5784,22 @@
         cellLp.addLinkAddress(myIpv4);
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
-        verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
+        verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
+        verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
+                eq(cellNetId), eq(EMPTY_STRING_ARRAY), any(), any(),
+                eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
 
         verifyNoMoreInteractions(mMockNetd);
+        verifyNoMoreInteractions(mMockDnsResolver);
         reset(mMockNetd);
+        reset(mMockDnsResolver);
 
         // Remove IPv4 address. Expect prefix discovery to be started again.
         cellLp.removeLinkAddress(myIpv4);
         cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
-        verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+        verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
 
         // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
         Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
@@ -5818,6 +5829,12 @@
         assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
         assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
 
+        verify(mMockDnsResolver, times(1)).setResolverConfiguration(
+                eq(cellNetId), mStringArrayCaptor.capture(), any(), any(),
+                eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+        assertEquals(1, mStringArrayCaptor.getValue().length);
+        assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "8.8.8.8"));
+
         // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
         // linkproperties are cleaned up.
         cellLp.addLinkAddress(myIpv4);
@@ -5825,7 +5842,7 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
-        verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
+        verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
 
         // As soon as stop is called, the linkproperties lose the stacked interface.
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
@@ -5840,7 +5857,9 @@
         networkCallback.assertNoCallback();
 
         verifyNoMoreInteractions(mMockNetd);
+        verifyNoMoreInteractions(mMockDnsResolver);
         reset(mMockNetd);
+        reset(mMockDnsResolver);
 
         // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
@@ -5854,7 +5873,7 @@
         cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
-        verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+        verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
                 kNat64PrefixString, 96);
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
@@ -5937,6 +5956,7 @@
 
         // Disconnect cell
         reset(mNetworkManagementService);
+        reset(mMockNetd);
         mCellNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
         // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
@@ -5944,8 +5964,9 @@
         // unexpectedly before network being removed.
         waitForIdle();
         verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
-        verify(mNetworkManagementService, times(1)).removeNetwork(
-                eq(mCellNetworkAgent.getNetwork().netId));
+        verify(mMockNetd, times(1)).networkDestroy(eq(mCellNetworkAgent.getNetwork().netId));
+        verify(mMockDnsResolver, times(1))
+                .clearResolverConfiguration(eq(mCellNetworkAgent.getNetwork().netId));
 
         // Disconnect wifi
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 15ba43d..8fa0ab9 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -29,13 +29,13 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.IDnsResolver;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.RouteInfo;
 import android.net.shared.PrivateDnsConfig;
-import android.os.INetworkManagementService;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 
@@ -73,7 +73,7 @@
     MockContentResolver mContentResolver;
 
     @Mock Context mCtx;
-    @Mock INetworkManagementService mNMService;
+    @Mock IDnsResolver mMockDnsResolver;
     @Mock MockableSystemProperties mSystemProperties;
 
     @Before
@@ -83,7 +83,7 @@
         mContentResolver.addProvider(Settings.AUTHORITY,
                 new FakeSettingsProvider());
         when(mCtx.getContentResolver()).thenReturn(mContentResolver);
-        mDnsManager = new DnsManager(mCtx, mNMService, mSystemProperties);
+        mDnsManager = new DnsManager(mCtx, mMockDnsResolver, mSystemProperties);
 
         // Clear the private DNS settings
         Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "");
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 6de4aa1..142769f 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -69,6 +70,7 @@
     LingerMonitor mMonitor;
 
     @Mock ConnectivityService mConnService;
+    @Mock IDnsResolver mDnsResolver;
     @Mock INetd mNetd;
     @Mock INetworkManagementService mNMS;
     @Mock Context mCtx;
@@ -353,7 +355,7 @@
         caps.addCapability(0);
         caps.addTransportType(transport);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
-                caps, 50, mCtx, null, mMisc, mConnService, mNetd, mNMS,
+                caps, 50, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS,
                 NetworkFactory.SerialNumber.NONE);
         nai.everValidated = true;
         return nai;
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index cc09fb7..b709af1 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.when;
 
 import android.net.ConnectivityManager;
+import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
@@ -63,6 +64,7 @@
 
     @Mock ConnectivityService mConnectivity;
     @Mock NetworkMisc mMisc;
+    @Mock IDnsResolver mDnsResolver;
     @Mock INetd mNetd;
     @Mock INetworkManagementService mNms;
     @Mock InterfaceConfiguration mConfig;
@@ -72,7 +74,7 @@
     Handler mHandler;
 
     Nat464Xlat makeNat464Xlat() {
-        return new Nat464Xlat(mNai, mNetd, mNms) {
+        return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
             @Override protected int getNetId() {
                 return NETID;
             }
@@ -205,7 +207,7 @@
         verify(mNms).unregisterObserver(eq(nat));
         assertTrue(c.getValue().getStackedLinks().isEmpty());
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
-        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+        verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
         // Stacked interface removed notification arrives and is ignored.
@@ -331,7 +333,7 @@
         verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+        verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
         assertTrue(c.getValue().getStackedLinks().isEmpty());
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
         assertIdle(nat);
@@ -358,7 +360,7 @@
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+        verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
         // In-flight interface up notification arrives: no-op
@@ -390,7 +392,7 @@
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+        verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);