Snap for 9551394 from 21dbe796b59ba679593a416c1e2c4616b1669ea9 to mainline-os-statsd-release

Change-Id: Iadc959de4bf677df0dc835de95b93ee61480507d
diff --git a/apishim/29/com/android/networkstack/apishim/api29/BroadcastOptionsShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/BroadcastOptionsShimImpl.java
new file mode 100644
index 0000000..ab58dc2
--- /dev/null
+++ b/apishim/29/com/android/networkstack/apishim/api29/BroadcastOptionsShimImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.apishim.api29;
+
+import android.app.BroadcastOptions;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.android.networkstack.apishim.common.BroadcastOptionsShim;
+
+/**
+ * Implementation of {@link BroadcastOptionsShim} for API 29.
+ */
+@RequiresApi(Build.VERSION_CODES.Q)
+public class BroadcastOptionsShimImpl implements BroadcastOptionsShim {
+    protected final BroadcastOptions mOptions;
+
+    protected BroadcastOptionsShimImpl(@NonNull BroadcastOptions options) {
+        mOptions = options;
+    }
+
+    /**
+     * Get a new instance of {@link BroadcastOptionsShim}.
+     */
+    public static BroadcastOptionsShim newInstance(@NonNull BroadcastOptions options) {
+        return new BroadcastOptionsShimImpl(options);
+    }
+
+    /** See android.app.BroadcastOptions#toBundle */
+    @Override
+    @NonNull
+    public Bundle toBundle() {
+        return mOptions.toBundle();
+    }
+}
diff --git a/apishim/29/com/android/networkstack/apishim/api29/NsdShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/NsdShimImpl.java
index 51cb2ba..ac0c69a 100644
--- a/apishim/29/com/android/networkstack/apishim/api29/NsdShimImpl.java
+++ b/apishim/29/com/android/networkstack/apishim/api29/NsdShimImpl.java
@@ -86,4 +86,11 @@
             throws UnsupportedApiLevelException {
         throw new UnsupportedApiLevelException("Resolve with executor is only supported on T+");
     }
+
+    @Override
+    public void stopServiceResolution(@NonNull NsdManager nsdManager,
+            @NonNull NsdManager.ResolveListener resolveListener)
+            throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("Stop service resolution is only supported on U+");
+    }
 }
diff --git a/apishim/33/com/android/networkstack/apishim/api33/BroadcastOptionsShimImpl.java b/apishim/33/com/android/networkstack/apishim/api33/BroadcastOptionsShimImpl.java
new file mode 100644
index 0000000..5e38766
--- /dev/null
+++ b/apishim/33/com/android/networkstack/apishim/api33/BroadcastOptionsShimImpl.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.apishim.api33;
+
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
+
+import android.app.BroadcastOptions;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.android.networkstack.apishim.common.BroadcastOptionsShim;
+
+/**
+ * Compatibility implementation of {@link BroadcastOptionsShim}.
+ */
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+public class BroadcastOptionsShimImpl
+        extends com.android.networkstack.apishim.api29.BroadcastOptionsShimImpl {
+    protected BroadcastOptionsShimImpl(@NonNull BroadcastOptions options) {
+        super(options);
+    }
+
+    /**
+     * Get a new instance of {@link BroadcastOptionsShimImpl}.
+     */
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+    public static BroadcastOptionsShim newInstance(@NonNull BroadcastOptions options) {
+        if (!isAtLeastT()) {
+            return com.android.networkstack.apishim.api29.BroadcastOptionsShimImpl.newInstance(
+                    options);
+        }
+        return new BroadcastOptionsShimImpl(options);
+    }
+}
diff --git a/apishim/33/com/android/networkstack/apishim/api33/ConstantsShim.java b/apishim/33/com/android/networkstack/apishim/api33/ConstantsShim.java
index 227b60c..66714c9 100644
--- a/apishim/33/com/android/networkstack/apishim/api33/ConstantsShim.java
+++ b/apishim/33/com/android/networkstack/apishim/api33/ConstantsShim.java
@@ -30,4 +30,7 @@
      */
     @VisibleForTesting
     public static final int VERSION = 33;
+
+    // Constants defined in android.app.BroadcastOptions.
+    public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1;
 }
diff --git a/apishim/34/com/android/networkstack/apishim/BroadcastOptionsShimImpl.java b/apishim/34/com/android/networkstack/apishim/BroadcastOptionsShimImpl.java
new file mode 100644
index 0000000..a6c2c44
--- /dev/null
+++ b/apishim/34/com/android/networkstack/apishim/BroadcastOptionsShimImpl.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.apishim;
+
+import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
+
+import android.app.BroadcastOptions;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.android.networkstack.apishim.common.BroadcastOptionsShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
+
+/**
+ * Implementation of {@link BroadcastOptionsShim} for API 34.
+ */
+// TODO: when available in all active branches: @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+@RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT)
+public class BroadcastOptionsShimImpl extends
+        com.android.networkstack.apishim.api33.BroadcastOptionsShimImpl {
+    protected BroadcastOptionsShimImpl(@NonNull BroadcastOptions options) {
+        super(options);
+    }
+
+    /** Get a new instance of {@link BroadcastOptionsShim}. */
+    @RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT)
+    public static BroadcastOptionsShim newInstance(@NonNull BroadcastOptions options) {
+        if (!isAtLeastU()) {
+            return com.android.networkstack.apishim.api33.BroadcastOptionsShimImpl.newInstance(
+                    options);
+        }
+        return new BroadcastOptionsShimImpl(options);
+    }
+
+    /** See android.app.BroadcastOptions#setDeliveryGroupPolicy */
+    @Override
+    public BroadcastOptionsShim setDeliveryGroupPolicy(int policy)
+            throws UnsupportedApiLevelException {
+        mOptions.setDeliveryGroupPolicy(policy);
+        return this;
+    }
+
+    /** See android.app.BroadcastOptions#setDeliveryGroupMatchingKey */
+    @Override
+    public BroadcastOptionsShim setDeliveryGroupMatchingKey(@NonNull String namespace,
+            @NonNull String key) throws UnsupportedApiLevelException {
+        mOptions.setDeliveryGroupMatchingKey(namespace, key);
+        return this;
+    }
+
+    /** See android.app.BroadcastOptions#setDeferUntilActive */
+    @Override
+    public BroadcastOptionsShim setDeferUntilActive(boolean shouldDefer)
+            throws UnsupportedApiLevelException {
+        mOptions.setDeferUntilActive(shouldDefer);
+        return this;
+    }
+}
diff --git a/apishim/34/com/android/networkstack/apishim/NsdShimImpl.java b/apishim/34/com/android/networkstack/apishim/NsdShimImpl.java
index c7b2b7a..1fbc41e 100644
--- a/apishim/34/com/android/networkstack/apishim/NsdShimImpl.java
+++ b/apishim/34/com/android/networkstack/apishim/NsdShimImpl.java
@@ -16,11 +16,24 @@
 
 package com.android.networkstack.apishim;
 
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
 import android.os.Build;
+import android.util.ArrayMap;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.networkstack.apishim.common.NsdShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
+
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * Implementation of {@link NsdShim}.
@@ -28,5 +41,88 @@
 // TODO: when available in all active branches: @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 @RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT)
 public class NsdShimImpl extends com.android.networkstack.apishim.api33.NsdShimImpl {
-    protected NsdShimImpl() {}
+    private final Map<ServiceInfoCallbackShim, ServiceInfoCallbackWrapper> mCbWrappers =
+            Collections.synchronizedMap(new ArrayMap<>());
+
+    /**
+     * Get a new instance of {@link NsdShim}.
+     */
+    @RequiresApi(Build.VERSION_CODES.Q)
+    public static NsdShim newInstance() {
+        if (SdkLevel.isAtLeastU()) {
+            return new NsdShimImpl();
+        } else {
+            return new com.android.networkstack.apishim.api33.NsdShimImpl();
+        }
+    }
+
+    @Override
+    public void stopServiceResolution(@NonNull NsdManager nsdManager,
+            @NonNull NsdManager.ResolveListener resolveListener)
+            throws UnsupportedApiLevelException {
+        nsdManager.stopServiceResolution(resolveListener);
+    }
+
+    private static class ServiceInfoCallbackWrapper implements NsdManager.ServiceInfoCallback {
+        @NonNull
+        final ServiceInfoCallbackShim mListener;
+
+        ServiceInfoCallbackWrapper(@NonNull ServiceInfoCallbackShim listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onServiceInfoCallbackRegistrationFailed(int errorCode) {
+            mListener.onServiceInfoCallbackRegistrationFailed(errorCode);
+        }
+
+        @Override
+        public void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo) {
+            mListener.onServiceUpdated(serviceInfo);
+        }
+
+        @Override
+        public void onServiceLost() {
+            mListener.onServiceLost();
+        }
+
+        @Override
+        public void onServiceInfoCallbackUnregistered() {
+            mListener.onServiceInfoCallbackUnregistered();
+        }
+    };
+
+    @Override
+    public void registerServiceInfoCallback(@NonNull NsdManager nsdManager,
+            @NonNull NsdServiceInfo serviceInfo, @NonNull Executor executor,
+            @NonNull ServiceInfoCallbackShim listener) throws UnsupportedApiLevelException {
+        Objects.requireNonNull(listener);
+        final ServiceInfoCallbackWrapper wrapper = new ServiceInfoCallbackWrapper(listener);
+        if (null != mCbWrappers.put(listener, wrapper)) {
+            throw new IllegalArgumentException("Listener shims must not be reused");
+        }
+        nsdManager.registerServiceInfoCallback(serviceInfo, executor, wrapper);
+    }
+
+    @Override
+    public void unregisterServiceInfoCallback(@NonNull NsdManager nsdManager,
+            @NonNull ServiceInfoCallbackShim listener) throws UnsupportedApiLevelException {
+        final ServiceInfoCallbackWrapper wrapper = mCbWrappers.remove(listener);
+        if (wrapper == null) {
+            throw new IllegalArgumentException("Listener was not registered");
+        }
+        nsdManager.unregisterServiceInfoCallback(wrapper);
+    }
+
+    @NonNull
+    @Override
+    public List<InetAddress> getHostAddresses(@NonNull NsdServiceInfo serviceInfo) {
+        return serviceInfo.getHostAddresses();
+    }
+
+    @Override
+    public void setHostAddresses(@NonNull NsdServiceInfo serviceInfo,
+            @NonNull List<InetAddress> addresses) {
+        serviceInfo.setHostAddresses(addresses);
+    }
 }
diff --git a/apishim/common/com/android/networkstack/apishim/common/BroadcastOptionsShim.java b/apishim/common/com/android/networkstack/apishim/common/BroadcastOptionsShim.java
new file mode 100644
index 0000000..097270d
--- /dev/null
+++ b/apishim/common/com/android/networkstack/apishim/common/BroadcastOptionsShim.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.apishim.common;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Interface for accessing API methods in {@link android.app.BroadcastOptions} by different SDK
+ * level.
+ */
+public interface BroadcastOptionsShim {
+    /** See android.app.BroadcastOptions#setDeliveryGroupPolicy */
+    default BroadcastOptionsShim setDeliveryGroupPolicy(int policy)
+            throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("Only supported starting from API 34");
+    }
+
+    /** See android.app.BroadcastOptions#setDeliveryGroupMatchingKey */
+    default BroadcastOptionsShim setDeliveryGroupMatchingKey(@NonNull String namespace,
+            @NonNull String key) throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("Only supported starting from API 34");
+    }
+
+    /** See android.app.BroadcastOptions#setDeferUntilActive */
+    default BroadcastOptionsShim setDeferUntilActive(boolean shouldDefer)
+            throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("Only supported starting from API 34");
+    }
+
+    /** See android.app.BroadcastOptions#toBundle */
+    @NonNull
+    Bundle toBundle();
+}
diff --git a/apishim/common/com/android/networkstack/apishim/common/NsdShim.java b/apishim/common/com/android/networkstack/apishim/common/NsdShim.java
index 9e067ee..7281022 100644
--- a/apishim/common/com/android/networkstack/apishim/common/NsdShim.java
+++ b/apishim/common/com/android/networkstack/apishim/common/NsdShim.java
@@ -27,6 +27,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.net.InetAddress;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /** Shim for NSD APIs, including {@link android.net.nsd.NsdManager} and
@@ -72,4 +74,56 @@
             int protocolType, @Nullable NetworkRequest request,
             @NonNull Executor executor, @NonNull DiscoveryListener listener)
             throws UnsupportedApiLevelException;
+
+    /**
+     * @see NsdManager#stopServiceResolution(ResolveListener)
+     */
+    void stopServiceResolution(@NonNull NsdManager nsdManager,
+            @NonNull ResolveListener resolveListener) throws UnsupportedApiLevelException;
+
+    /**
+     * @see NsdManager#ServiceInfoCallback
+     */
+    interface ServiceInfoCallbackShim {
+        default void onServiceInfoCallbackRegistrationFailed(int errorCode) {}
+        default void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo) {}
+        default void onServiceLost() {}
+        default void onServiceInfoCallbackUnregistered() {}
+    }
+
+    /**
+     * @see NsdManager#registerServiceInfoCallback
+     */
+    default void registerServiceInfoCallback(@NonNull NsdManager nsdManager,
+            @NonNull NsdServiceInfo serviceInfo, @NonNull Executor executor,
+            @NonNull ServiceInfoCallbackShim listener)
+            throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("Service callback is only supported on U+");
+    }
+
+    /**
+     * @see NsdManager#unregisterServiceInfoCallback
+     */
+    default void unregisterServiceInfoCallback(@NonNull NsdManager nsdManager,
+            @NonNull ServiceInfoCallbackShim listener)
+            throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("Service callback is only supported on U+");
+    }
+
+    /**
+     * @see NsdServiceInfo#getHostAddresses()
+     */
+    @NonNull
+    default List<InetAddress> getHostAddresses(@NonNull NsdServiceInfo serviceInfo)
+            throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("getHostAddresses is only supported on U+");
+    }
+
+    /**
+     * @see NsdServiceInfo#setHostAddresses(List<InetAddress>)
+     */
+    default void setHostAddresses(@NonNull NsdServiceInfo serviceInfo,
+            @NonNull List<InetAddress> addresses) throws UnsupportedApiLevelException {
+        throw new UnsupportedApiLevelException("setHostAddresses is only supported on U+");
+    }
 }
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp
index d92badd..89de36b 100644
--- a/common/networkstackclient/Android.bp
+++ b/common/networkstackclient/Android.bp
@@ -144,6 +144,7 @@
         "13",
         "14",
         "15",
+        "16",
     ],
     // TODO: have tethering depend on networkstack-client and set visibility to private
     visibility: [
@@ -159,7 +160,7 @@
     min_sdk_version: "29",
     static_libs: [
         "ipmemorystore-aidl-interfaces-V10-java",
-        "networkstack-aidl-interfaces-V15-java",
+        "networkstack-aidl-interfaces-V16-java",
     ],
     visibility: ["//packages/modules/NetworkStack:__subpackages__"],
     apex_available: [
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/.hash b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/.hash
new file mode 100644
index 0000000..1b0fdc0
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/.hash
@@ -0,0 +1 @@
+a7ed197af8532361170ac44595771304b7f04034
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/DataStallReportParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/DataStallReportParcelable.aidl
new file mode 100644
index 0000000..771deda
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/DataStallReportParcelable.aidl
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable DataStallReportParcelable {
+  long timestampMillis = 0;
+  int detectionMethod = 1;
+  int tcpPacketFailRate = 2;
+  int tcpMetricsCollectionPeriodMillis = 3;
+  int dnsConsecutiveTimeouts = 4;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/DhcpResultsParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/DhcpResultsParcelable.aidl
new file mode 100644
index 0000000..31f2194
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable DhcpResultsParcelable {
+  android.net.StaticIpConfiguration baseConfiguration;
+  int leaseDuration;
+  int mtu;
+  String serverAddress;
+  String vendorInfo;
+  @nullable String serverHostName;
+  @nullable String captivePortalApiUrl;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkMonitor.aidl
new file mode 100644
index 0000000..fc6a70e
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkMonitor.aidl
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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 INetworkMonitor {
+  oneway void start();
+  oneway void launchCaptivePortalApp();
+  oneway void notifyCaptivePortalAppFinished(int response);
+  oneway void setAcceptPartialConnectivity();
+  oneway void forceReevaluation(int uid);
+  oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config);
+  oneway void notifyDnsResponse(int returnCode);
+  oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc);
+  oneway void notifyNetworkDisconnected();
+  oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp);
+  oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc);
+  oneway void notifyNetworkConnectedParcel(in android.net.networkstack.aidl.NetworkMonitorParameters params);
+  const int NETWORK_TEST_RESULT_VALID = 0;
+  const int NETWORK_TEST_RESULT_INVALID = 1;
+  const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
+  const int NETWORK_VALIDATION_RESULT_VALID = 1;
+  const int NETWORK_VALIDATION_RESULT_PARTIAL = 2;
+  const int NETWORK_VALIDATION_RESULT_SKIPPED = 4;
+  const int NETWORK_VALIDATION_PROBE_DNS = 4;
+  const int NETWORK_VALIDATION_PROBE_HTTP = 8;
+  const int NETWORK_VALIDATION_PROBE_HTTPS = 16;
+  const int NETWORK_VALIDATION_PROBE_FALLBACK = 32;
+  const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkMonitorCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkMonitorCallbacks.aidl
new file mode 100644
index 0000000..36eda8e
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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 INetworkMonitorCallbacks {
+  oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor) = 0;
+  oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl) = 1;
+  oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config) = 2;
+  oneway void showProvisioningNotification(String action, String packageName) = 3;
+  oneway void hideProvisioningNotification() = 4;
+  oneway void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) = 5;
+  oneway void notifyNetworkTestedWithExtras(in android.net.NetworkTestResultParcelable result) = 6;
+  oneway void notifyDataStallSuspected(in android.net.DataStallReportParcelable report) = 7;
+  oneway void notifyCaptivePortalDataChanged(in android.net.CaptivePortalData data) = 8;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkStackConnector.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkStackConnector.aidl
new file mode 100644
index 0000000..8120ffc
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkStackConnector.aidl
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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 INetworkStackConnector {
+  oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb);
+  oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
+  oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
+  oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
+  oneway void allowTestUid(int uid, in android.net.INetworkStackStatusCallback cb);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkStackStatusCallback.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkStackStatusCallback.aidl
new file mode 100644
index 0000000..0b6b778
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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 INetworkStackStatusCallback {
+  oneway void onStatusAvailable(int statusCode);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/InformationElementParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/InformationElementParcelable.aidl
new file mode 100644
index 0000000..6103774
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/InformationElementParcelable.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable InformationElementParcelable {
+  int id;
+  byte[] payload;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/InitialConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/InitialConfigurationParcelable.aidl
new file mode 100644
index 0000000..6a597e6
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable InitialConfigurationParcelable {
+  android.net.LinkAddress[] ipAddresses;
+  android.net.IpPrefix[] directlyConnectedRoutes;
+  String[] dnsServers;
+  String gateway;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/Layer2InformationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/Layer2InformationParcelable.aidl
new file mode 100644
index 0000000..83796ee
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/Layer2InformationParcelable.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable Layer2InformationParcelable {
+  String l2Key;
+  String cluster;
+  android.net.MacAddress bssid;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/Layer2PacketParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/Layer2PacketParcelable.aidl
new file mode 100644
index 0000000..4b3fff5
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/Layer2PacketParcelable.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable Layer2PacketParcelable {
+  android.net.MacAddress dstMacAddress;
+  byte[] payload;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/NattKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/NattKeepalivePacketDataParcelable.aidl
new file mode 100644
index 0000000..18cf954
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/NattKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable NattKeepalivePacketDataParcelable {
+  byte[] srcAddress;
+  int srcPort;
+  byte[] dstAddress;
+  int dstPort;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/NetworkTestResultParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/NetworkTestResultParcelable.aidl
new file mode 100644
index 0000000..4d6d5a2
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/NetworkTestResultParcelable.aidl
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable NetworkTestResultParcelable {
+  long timestampMillis;
+  int result;
+  int probesSucceeded;
+  int probesAttempted;
+  String redirectUrl;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/PrivateDnsConfigParcel.aidl
new file mode 100644
index 0000000..1457caf
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable PrivateDnsConfigParcel {
+  String hostname;
+  String[] ips;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ProvisioningConfigurationParcelable.aidl
new file mode 100644
index 0000000..fba524b
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,63 @@
+/*
+**
+** Copyright (C) 2019 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable ProvisioningConfigurationParcelable {
+  /**
+   * @deprecated use ipv4ProvisioningMode instead.
+   */
+  boolean enableIPv4;
+  /**
+   * @deprecated use ipv6ProvisioningMode instead.
+   */
+  boolean enableIPv6;
+  boolean usingMultinetworkPolicyTracker;
+  boolean usingIpReachabilityMonitor;
+  int requestedPreDhcpActionMs;
+  android.net.InitialConfigurationParcelable initialConfig;
+  android.net.StaticIpConfiguration staticIpConfig;
+  android.net.apf.ApfCapabilities apfCapabilities;
+  int provisioningTimeoutMs;
+  int ipv6AddrGenMode;
+  android.net.Network network;
+  String displayName;
+  boolean enablePreconnection;
+  @nullable android.net.ScanResultInfoParcelable scanResultInfo;
+  @nullable android.net.Layer2InformationParcelable layer2Info;
+  @nullable List<android.net.networkstack.aidl.dhcp.DhcpOption> options;
+  int ipv4ProvisioningMode;
+  int ipv6ProvisioningMode;
+  boolean uniqueEui64AddressesOnly;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ScanResultInfoParcelable.aidl
new file mode 100644
index 0000000..94fc27f
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ScanResultInfoParcelable.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable ScanResultInfoParcelable {
+  String ssid;
+  String bssid;
+  android.net.InformationElementParcelable[] informationElements;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/TcpKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/TcpKeepalivePacketDataParcelable.aidl
new file mode 100644
index 0000000..0e1c21c
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+@JavaDerive(toString=true)
+parcelable TcpKeepalivePacketDataParcelable {
+  byte[] srcAddress;
+  int srcPort;
+  byte[] dstAddress;
+  int dstPort;
+  int seq;
+  int ack;
+  int rcvWnd;
+  int rcvWndScale;
+  int tos;
+  int ttl;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/DhcpLeaseParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/DhcpLeaseParcelable.aidl
new file mode 100644
index 0000000..3cd8860
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/DhcpLeaseParcelable.aidl
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.dhcp;
+@JavaDerive(toString=true)
+parcelable DhcpLeaseParcelable {
+  byte[] clientId;
+  byte[] hwAddr;
+  int netAddr;
+  int prefixLength;
+  long expTime;
+  String hostname;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/DhcpServingParamsParcel.aidl
new file mode 100644
index 0000000..7997936
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,49 @@
+/**
+ *
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.dhcp;
+@JavaDerive(toString=true)
+parcelable DhcpServingParamsParcel {
+  int serverAddr;
+  int serverAddrPrefixLength;
+  int[] defaultRouters;
+  int[] dnsServers;
+  int[] excludedAddrs;
+  long dhcpLeaseTimeSecs;
+  int linkMtu;
+  boolean metered;
+  int singleClientAddr = 0;
+  boolean changePrefixOnDecline = false;
+  int leasesSubnetPrefixLength = 0;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpEventCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpEventCallbacks.aidl
new file mode 100644
index 0000000..9312f47
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpEventCallbacks.aidl
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.dhcp;
+interface IDhcpEventCallbacks {
+  oneway void onLeasesChanged(in List<android.net.dhcp.DhcpLeaseParcelable> newLeases);
+  oneway void onNewPrefixRequest(in android.net.IpPrefix currentPrefix);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpServer.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpServer.aidl
new file mode 100644
index 0000000..1109f35
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.dhcp;
+/* @hide */
+interface IDhcpServer {
+  oneway void start(in android.net.INetworkStackStatusCallback cb) = 0;
+  oneway void startWithCallbacks(in android.net.INetworkStackStatusCallback statusCb, in android.net.dhcp.IDhcpEventCallbacks eventCb) = 3;
+  oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb) = 1;
+  oneway void stop(in android.net.INetworkStackStatusCallback cb) = 2;
+  const int STATUS_UNKNOWN = 0;
+  const int STATUS_SUCCESS = 1;
+  const int STATUS_INVALID_ARGUMENT = 2;
+  const int STATUS_UNKNOWN_ERROR = 3;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpServerCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpServerCallbacks.aidl
new file mode 100644
index 0000000..ab8577c
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.dhcp;
+/* @hide */
+interface IDhcpServerCallbacks {
+  oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ip/IIpClient.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ip/IIpClient.aidl
new file mode 100644
index 0000000..a97511e
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ip/IIpClient.aidl
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.ip;
+/* @hide */
+interface IIpClient {
+  oneway void completedPreDhcpAction();
+  oneway void confirmConfiguration();
+  oneway void readPacketFilterComplete(in byte[] data);
+  oneway void shutdown();
+  oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req);
+  oneway void stop();
+  oneway void setTcpBufferSizes(in String tcpBufferSizes);
+  oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo);
+  oneway void setMulticastFilter(boolean enabled);
+  oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt);
+  oneway void removeKeepalivePacketFilter(int slot);
+  oneway void setL2KeyAndGroupHint(in String l2Key, in String cluster);
+  oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt);
+  oneway void notifyPreconnectionComplete(boolean success);
+  oneway void updateLayer2Information(in android.net.Layer2InformationParcelable info);
+  const int PROV_IPV4_DISABLED = 0;
+  const int PROV_IPV4_STATIC = 1;
+  const int PROV_IPV4_DHCP = 2;
+  const int PROV_IPV6_DISABLED = 0;
+  const int PROV_IPV6_SLAAC = 1;
+  const int PROV_IPV6_LINKLOCAL = 2;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ip/IIpClientCallbacks.aidl
new file mode 100644
index 0000000..24bbf64
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.ip;
+/* @hide */
+interface IIpClientCallbacks {
+  oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient);
+  oneway void onPreDhcpAction();
+  oneway void onPostDhcpAction();
+  oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults);
+  oneway void onProvisioningSuccess(in android.net.LinkProperties newLp);
+  oneway void onProvisioningFailure(in android.net.LinkProperties newLp);
+  oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp);
+  oneway void onReachabilityLost(in String logMsg);
+  oneway void onQuit();
+  oneway void installPacketFilter(in byte[] filter);
+  oneway void startReadPacketFilter();
+  oneway void setFallbackMulticastFilter(boolean enabled);
+  oneway void setNeighborDiscoveryOffload(boolean enable);
+  oneway void onPreconnectionStart(in List<android.net.Layer2PacketParcelable> packets);
+  oneway void onReachabilityFailure(in android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable lossInfo);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/NetworkMonitorParameters.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/NetworkMonitorParameters.aidl
new file mode 100644
index 0000000..2ab9db0
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/NetworkMonitorParameters.aidl
@@ -0,0 +1,41 @@
+/**
+ *
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.networkstack.aidl;
+@JavaDerive(equals=true, toString=true)
+parcelable NetworkMonitorParameters {
+  android.net.NetworkAgentConfig networkAgentConfig;
+  android.net.NetworkCapabilities networkCapabilities;
+  android.net.LinkProperties linkProperties;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/dhcp/DhcpOption.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/dhcp/DhcpOption.aidl
new file mode 100644
index 0000000..eea3e0d
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/dhcp/DhcpOption.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.networkstack.aidl.dhcp;
+@JavaDerive(toString=true)
+parcelable DhcpOption {
+  byte type;
+  @nullable byte[] value;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl
new file mode 100644
index 0000000..bb88434
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.networkstack.aidl.ip;
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable ReachabilityLossInfoParcelable {
+  String message;
+  android.net.networkstack.aidl.ip.ReachabilityLossReason reason;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl
new file mode 100644
index 0000000..70a7db2
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/16/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.networkstack.aidl.ip;
+@Backing(type="int")
+enum ReachabilityLossReason {
+  ROAM = 0,
+  CONFIRM = 1,
+  ORGANIC = 2,
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ProvisioningConfigurationParcelable.aidl
index 9ecd110..fba524b 100644
--- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ProvisioningConfigurationParcelable.aidl
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ProvisioningConfigurationParcelable.aidl
@@ -59,4 +59,5 @@
   @nullable List<android.net.networkstack.aidl.dhcp.DhcpOption> options;
   int ipv4ProvisioningMode;
   int ipv6ProvisioningMode;
+  boolean uniqueEui64AddressesOnly;
 }
diff --git a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl
index 54a5729..c4d7866 100644
--- a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl
+++ b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl
@@ -49,4 +49,5 @@
     @nullable List<DhcpOption> options;
     int ipv4ProvisioningMode;
     int ipv6ProvisioningMode;
+    boolean uniqueEui64AddressesOnly;
 }
diff --git a/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java b/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java
index 3a2a1bd..f3631f2 100644
--- a/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java
+++ b/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java
@@ -270,6 +270,18 @@
         }
 
         /**
+         * Specify that the configuration is for a network that only uses unique EUI-64
+         * addresses (e.g., a link-local-only network where addresses are generated via
+         * EUI-64 and where MAC addresses are guaranteed to be unique).
+         * This will disable duplicate address detection if withLinkLocalOnly() and
+         * withRandomMacAddress are also called.
+         */
+        public Builder withUniqueEui64AddressesOnly() {
+            mConfig.mUniqueEui64AddressesOnly = true;
+            return this;
+        }
+
+        /**
          * Build the configuration using previously specified parameters.
          */
         public ProvisioningConfiguration build() {
@@ -463,6 +475,7 @@
         }
     }
 
+    public boolean mUniqueEui64AddressesOnly = false;
     public boolean mEnablePreconnection = false;
     public boolean mUsingMultinetworkPolicyTracker = true;
     public boolean mUsingIpReachabilityMonitor = true;
@@ -483,6 +496,7 @@
     public ProvisioningConfiguration() {} // used by Builder
 
     public ProvisioningConfiguration(ProvisioningConfiguration other) {
+        mUniqueEui64AddressesOnly = other.mUniqueEui64AddressesOnly;
         mEnablePreconnection = other.mEnablePreconnection;
         mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker;
         mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
@@ -512,6 +526,7 @@
         p.ipv4ProvisioningMode = mIPv4ProvisioningMode;
         p.enableIPv6 = (mIPv6ProvisioningMode != PROV_IPV6_DISABLED);
         p.ipv6ProvisioningMode = mIPv6ProvisioningMode;
+        p.uniqueEui64AddressesOnly = mUniqueEui64AddressesOnly;
         p.enablePreconnection = mEnablePreconnection;
         p.usingMultinetworkPolicyTracker = mUsingMultinetworkPolicyTracker;
         p.usingIpReachabilityMonitor = mUsingIpReachabilityMonitor;
@@ -543,6 +558,7 @@
             @Nullable ProvisioningConfigurationParcelable p, int interfaceVersion) {
         if (p == null) return null;
         final ProvisioningConfiguration config = new ProvisioningConfiguration();
+        config.mUniqueEui64AddressesOnly = p.uniqueEui64AddressesOnly;
         config.mEnablePreconnection = p.enablePreconnection;
         config.mUsingMultinetworkPolicyTracker = p.usingMultinetworkPolicyTracker;
         config.mUsingIpReachabilityMonitor = p.usingIpReachabilityMonitor;
@@ -602,6 +618,7 @@
         final String ipv4ProvisioningMode = ipv4ProvisioningModeToString(mIPv4ProvisioningMode);
         final String ipv6ProvisioningMode = ipv6ProvisioningModeToString(mIPv6ProvisioningMode);
         return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
+                .add("mUniqueEui64AddressesOnly: " + mUniqueEui64AddressesOnly)
                 .add("mEnablePreconnection: " + mEnablePreconnection)
                 .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
                 .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
@@ -647,7 +664,8 @@
     public boolean equals(Object obj) {
         if (!(obj instanceof ProvisioningConfiguration)) return false;
         final ProvisioningConfiguration other = (ProvisioningConfiguration) obj;
-        return mEnablePreconnection == other.mEnablePreconnection
+        return mUniqueEui64AddressesOnly == other.mUniqueEui64AddressesOnly
+                && mEnablePreconnection == other.mEnablePreconnection
                 && mUsingMultinetworkPolicyTracker == other.mUsingMultinetworkPolicyTracker
                 && mUsingIpReachabilityMonitor == other.mUsingIpReachabilityMonitor
                 && mRequestedPreDhcpActionMs == other.mRequestedPreDhcpActionMs
diff --git a/jni/network_stack_utils_jni.cpp b/jni/network_stack_utils_jni.cpp
index 20eb051..e63483f 100644
--- a/jni/network_stack_utils_jni.cpp
+++ b/jni/network_stack_utils_jni.cpp
@@ -100,9 +100,9 @@
 }
 
 static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jclass clazz, jobject javaFd, jboolean dropMF) {
-    static const __u32 frag_mask = static_cast<__u32>((dropMF ? IP_MF : 0) | IP_OFFMASK);
+    const __u32 frag_mask = static_cast<__u32>((dropMF ? IP_MF : 0) | IP_OFFMASK);
 
-    static sock_filter filter_code[] = {
+    sock_filter filter_code[] = {
         // Check the protocol is UDP.
         BPF_STMT(BPF_LD  | BPF_B    | BPF_ABS, kIPv4Protocol),
         BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   IPPROTO_UDP, 0, 6),
@@ -124,7 +124,7 @@
         // Reject.
         BPF_STMT(BPF_RET | BPF_K,              0)
     };
-    static const sock_fprog filter = {
+    const sock_fprog filter = {
         sizeof(filter_code) / sizeof(filter_code[0]),
         filter_code,
     };
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java
index ccc80b0..70fa673 100644
--- a/src/android/net/apf/ApfFilter.java
+++ b/src/android/net/apf/ApfFilter.java
@@ -66,6 +66,7 @@
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.ConnectivityUtils;
 import com.android.net.module.util.InterfaceParams;
+import com.android.net.module.util.SocketUtils;
 import com.android.networkstack.util.NetworkStackUtils;
 
 import java.io.FileDescriptor;
@@ -212,7 +213,7 @@
         public void halt() {
             mStopped = true;
             // Interrupts the read() call the thread is blocked in.
-            NetworkStackUtils.closeSocketQuietly(mSocket);
+            SocketUtils.closeSocketQuietly(mSocket);
         }
 
         @Override
@@ -903,13 +904,21 @@
 
         // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
         // Jump to the next filter if packet doesn't match this RA.
+        // Return Long.MAX_VALUE if we don't install any filter program for this RA. As the return
+        // value of this function is used to calculate the program min lifetime (which corresponds
+        // to the smallest generated filter lifetime). Returning Long.MAX_VALUE in the case no
+        // filter gets generated makes sure the program lifetime stays unaffected.
         @GuardedBy("ApfFilter.this")
         long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+            // Filter for a fraction of the lifetime and adjust for the age of the RA.
+            int filterLifetime = (int) (mMinLifetime / FRACTION_OF_LIFETIME_TO_FILTER)
+                    - (int) (mProgramBaseTime - mLastSeen);
+            if (filterLifetime <= 0) return Long.MAX_VALUE;
+
             String nextFilterLabel = "Ra" + getUniqueNumberLocked();
             // Skip if packet is not the right size
             gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
             gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel);
-            int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
             // Skip filter if expired
             gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
             gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
@@ -1177,6 +1186,11 @@
     // packets may be dropped, so let's use 6.
     private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
 
+    // The base time for this filter program. In seconds since Unix Epoch.
+    // This is the time when the APF program was generated. All filters in the program should use
+    // this base time as their current time for consistency purposes.
+    @GuardedBy("this")
+    private long mProgramBaseTime;
     // When did we last install a filter program? In seconds since Unix Epoch.
     @GuardedBy("this")
     private long mLastTimeInstalledProgram;
@@ -1631,6 +1645,7 @@
             maximumApfProgramSize -= Counter.totalSize();
         }
 
+        mProgramBaseTime = currentTimeSeconds();
         try {
             // Step 1: Determine how many RA filters we can fit in the program.
             ApfGenerator gen = emitPrologueLocked();
@@ -1667,8 +1682,8 @@
             Log.e(TAG, "Failed to generate APF program.", e);
             return;
         }
-        final long now = currentTimeSeconds();
-        mLastTimeInstalledProgram = now;
+        mIpClientCallback.installPacketFilter(program);
+        mLastTimeInstalledProgram = mProgramBaseTime;
         mLastInstalledProgramMinLifetime = programMinLifetime;
         mLastInstalledProgram = program;
         mNumProgramUpdates++;
@@ -1676,8 +1691,7 @@
         if (VDBG) {
             hexDump("Installing filter: ", program, program.length);
         }
-        mIpClientCallback.installPacketFilter(program);
-        logApfProgramEventLocked(now);
+        logApfProgramEventLocked(mProgramBaseTime);
         mLastInstallEvent = new ApfProgramEvent.Builder()
                 .setLifetime(programMinLifetime)
                 .setFilteredRas(rasToFilter.size())
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index e494451..8f59839 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -51,13 +51,13 @@
 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
 import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_ANNOUNCE_NUM;
 import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_PROBE_NUM;
+import static com.android.net.module.util.SocketUtils.closeSocketQuietly;
 import static com.android.networkstack.util.NetworkStackUtils.DHCP_DISABLE_DROP_MF;
 import static com.android.networkstack.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION;
 import static com.android.networkstack.util.NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION;
 import static com.android.networkstack.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION;
 import static com.android.networkstack.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
 import static com.android.networkstack.util.NetworkStackUtils.DHCP_SLOW_RETRANSMISSION_VERSION;
-import static com.android.networkstack.util.NetworkStackUtils.closeSocketQuietly;
 
 import android.content.Context;
 import android.net.DhcpResults;
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index c6c637d..fa03e08 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -107,6 +107,7 @@
 import com.android.net.module.util.DeviceConfigUtils;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.SocketUtils;
 import com.android.net.module.util.ip.InterfaceController;
 import com.android.networkstack.R;
 import com.android.networkstack.apishim.NetworkInformationShimImpl;
@@ -584,6 +585,7 @@
     private long mStartTimeMillis;
     private MacAddress mCurrentBssid;
     private boolean mHasDisabledIpv6OrAcceptRaOnProvLoss;
+    private Integer mDadTransmits = null;
 
     /**
      * Reading the snapshot is an asynchronous operation initiated by invoking
@@ -756,6 +758,7 @@
                         // consistent with relying on the non-blocking NetworkObserver callbacks,
                         // see {@link registerObserverForNonblockingCallback}. This can be done
                         // by either sending a message to StateMachine or posting a handler.
+                        if (address.isLinkLocalAddress()) return;
                         getHandler().post(() -> {
                             mLog.log("Remove IPv6 GUA " + address
                                     + " from both Gratuituous NA and Multicast NS sets");
@@ -1333,6 +1336,25 @@
         }
     }
 
+    private Integer getIpv6DadTransmits() {
+        try {
+            return Integer.parseUnsignedInt(mNetd.getProcSysNet(INetd.IPV6, INetd.CONF,
+                    mInterfaceName, "dad_transmits"));
+        } catch (RemoteException | ServiceSpecificException e) {
+            logError("Couldn't read dad_transmits on " + mInterfaceName, e);
+            return null;
+        }
+    }
+
+    private void setIpv6DadTransmits(int dadTransmits) {
+        try {
+            mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mInterfaceParams.name,
+                    "dad_transmits", Integer.toString(dadTransmits));
+        } catch (Exception e) {
+            Log.e(mTag, "Failed to set dad_transmits to " + dadTransmits + ": " + e);
+        }
+    }
+
     private void restartIpv6WithAcceptRaDisabled() {
         mInterfaceCtrl.disableIPv6();
         startIPv6(0 /* acceptRa */);
@@ -1618,7 +1640,7 @@
         } catch (SocketException | ErrnoException e) {
             logError(msg, e);
         } finally {
-            NetworkStackUtils.closeSocketQuietly(sock);
+            SocketUtils.closeSocketQuietly(sock);
         }
     }
 
@@ -1925,8 +1947,22 @@
         return true;
     }
 
+    private boolean shouldDisableDad() {
+        return mConfiguration.mUniqueEui64AddressesOnly
+                && mConfiguration.mIPv6ProvisioningMode == PROV_IPV6_LINKLOCAL
+                && mConfiguration.mIPv6AddrGenMode
+                        == ProvisioningConfiguration.IPV6_ADDR_GEN_MODE_EUI64;
+    }
+
     private boolean startIPv6(int acceptRa) {
         setIpv6AcceptRa(acceptRa);
+        if (shouldDisableDad()) {
+            final Integer dadTransmits = getIpv6DadTransmits();
+            if (dadTransmits != null) {
+                mDadTransmits = dadTransmits;
+                setIpv6DadTransmits(0 /* dad_transmits */);
+            }
+        }
         return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
                 && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
                 && mInterfaceCtrl.enableIPv6();
@@ -2018,6 +2054,13 @@
         }
     }
 
+    private void maybeRestoreDadTransmits() {
+        if (mDadTransmits == null) return;
+
+        setIpv6DadTransmits(mDadTransmits);
+        mDadTransmits = null;
+    }
+
     private void handleUpdateL2Information(@NonNull Layer2InformationParcelable info) {
         mL2Key = info.l2Key;
         mCluster = info.cluster;
@@ -2151,6 +2194,8 @@
 
             // Restore the interface MTU to initial value if it has changed.
             maybeRestoreInterfaceMtu();
+            // Reset number of dad_transmits to default value if changed.
+            maybeRestoreDadTransmits();
         }
 
         @Override
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index 654d390..0771bff 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -45,7 +45,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
+import com.android.net.module.util.HexDump;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.SharedLog;
 import com.android.net.module.util.ip.NetlinkMonitor;
@@ -61,7 +61,6 @@
 import com.android.net.module.util.netlink.StructNdOptRdnss;
 import com.android.networkstack.apishim.NetworkInformationShimImpl;
 import com.android.networkstack.apishim.common.NetworkInformationShim;
-import com.android.networkstack.util.NetworkStackUtils;
 import com.android.server.NetworkObserver;
 
 import java.net.Inet6Address;
@@ -122,7 +121,7 @@
         void update(boolean linkState);
 
         /**
-         * Called when an IPv6 global unicast address was removed from the interface.
+         * Called when an IPv6 address was removed from the interface.
          *
          * @param addr The removed IPv6 address.
          */
@@ -327,7 +326,7 @@
         }
         if (changed) {
             mCallback.update(linkState);
-            if (!add && NetworkStackUtils.isIPv6GUA(address)) {
+            if (!add && address.isIpv6()) {
                 final Inet6Address addr = (Inet6Address) address.getAddress();
                 mCallback.onIpv6AddressRemoved(addr);
             }
diff --git a/src/android/net/util/DataStallUtils.java b/src/android/net/util/DataStallUtils.java
index 4b25967..c005e7d 100644
--- a/src/android/net/util/DataStallUtils.java
+++ b/src/android/net/util/DataStallUtils.java
@@ -116,13 +116,6 @@
      */
     public static final String CONFIG_TCP_PACKETS_FAIL_PERCENTAGE = "tcp_packets_fail_percentage";
 
-    /** Corresponds to enum from bionic/libc/include/netinet/tcp.h. */
-    public static final int TCP_ESTABLISHED = 1;
-    public static final int TCP_SYN_SENT = 2;
-    public static final int TCP_SYN_RECV = 3;
-    public static final int TCP_MONITOR_STATE_FILTER =
-            (1 << TCP_ESTABLISHED) | (1 << TCP_SYN_SENT) | (1 << TCP_SYN_RECV);
-
     /**
      * Threshold for the minimal tcp packets count to evaluate data stall via tcp info.
      */
diff --git a/src/com/android/networkstack/netlink/TcpSocketTracker.java b/src/com/android/networkstack/netlink/TcpSocketTracker.java
index acf716b..a3a58e6 100644
--- a/src/com/android/networkstack/netlink/TcpSocketTracker.java
+++ b/src/com/android/networkstack/netlink/TcpSocketTracker.java
@@ -19,25 +19,17 @@
 import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_PERCENTAGE;
 import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD;
 import static android.net.util.DataStallUtils.DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE;
-import static android.net.util.DataStallUtils.TCP_MONITOR_STATE_FILTER;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.AF_NETLINK;
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.NETLINK_INET_DIAG;
-import static android.system.OsConstants.SOCK_CLOEXEC;
-import static android.system.OsConstants.SOCK_DGRAM;
 import static android.system.OsConstants.SOL_SOCKET;
 import static android.system.OsConstants.SO_SNDTIMEO;
 
-import static com.android.net.module.util.netlink.InetDiagMessage.inetDiagReqV2;
-import static com.android.net.module.util.netlink.NetlinkConstants.INET_DIAG_MEMINFO;
 import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE;
 import static com.android.net.module.util.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE;
 import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
-import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
-import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static com.android.net.module.util.netlink.NetlinkUtils.DEFAULT_RECV_BUFSIZE;
+import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -46,7 +38,6 @@
 import android.net.INetd;
 import android.net.MarkMaskParcel;
 import android.net.Network;
-import android.net.util.SocketUtils;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.IBinder;
@@ -67,14 +58,15 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.SocketUtils;
+import com.android.net.module.util.netlink.InetDiagMessage;
 import com.android.net.module.util.netlink.NetlinkConstants;
-import com.android.net.module.util.netlink.NetlinkSocket;
+import com.android.net.module.util.netlink.NetlinkUtils;
 import com.android.net.module.util.netlink.StructInetDiagMsg;
 import com.android.net.module.util.netlink.StructNlMsgHdr;
 import com.android.networkstack.apishim.NetworkShimImpl;
 import com.android.networkstack.apishim.common.ShimUtils;
 import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
-import com.android.networkstack.util.NetworkStackUtils;
 
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
@@ -94,14 +86,9 @@
     private static final String TAG = TcpSocketTracker.class.getSimpleName();
     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
-    // This is for individual message. The individual messages should not be excessively large.
-    private static final int DEFAULT_RECV_BUFSIZE = 8192;
-    // Default I/O timeout time in ms of the socket request.
-    private static final long IO_TIMEOUT = 3_000L;
+
     /** Cookie offset of an InetMagMessage header. */
     private static final int IDIAG_COOKIE_OFFSET = 44;
-    private static final int UNKNOWN_MARK = 0xffffffff;
-    private static final int NULL_MASK = 0;
     private static final int END_OF_PARSING = -1;
     /**
      *  Gather the socket info.
@@ -172,10 +159,10 @@
         mNetd = mDependencies.getNetd();
 
         // If the parcel is null, nothing should be matched which is achieved by the combination of
-        // {@code NULL_MASK} and {@code UNKNOWN_MARK}.
+        // {@code NetlinkUtils#NULL_MASK} and {@code NetlinkUtils#UNKNOWN_MARK}.
         final MarkMaskParcel parcel = getNetworkMarkMask();
-        mNetworkMark = (parcel != null) ? parcel.mark : UNKNOWN_MARK;
-        mNetworkMask = (parcel != null) ? parcel.mask : NULL_MASK;
+        mNetworkMark = (parcel != null) ? parcel.mark : NetlinkUtils.UNKNOWN_MARK;
+        mNetworkMask = (parcel != null) ? parcel.mask : NetlinkUtils.NULL_MASK;
 
         // Request tcp info from NetworkStack directly needs extra SELinux permission added after Q
         // release.
@@ -183,15 +170,7 @@
         // Build SocketDiag messages.
         for (final int family : ADDRESS_FAMILIES) {
             mSockDiagMsg.put(
-                    family,
-                    inetDiagReqV2(IPPROTO_TCP,
-                            null /* local addr */,
-                            null /* remote addr */,
-                            family,
-                            (short) (NLM_F_REQUEST | NLM_F_DUMP) /* flag */,
-                            0 /* pad */,
-                            1 << INET_DIAG_MEMINFO /* idiagExt */,
-                            TCP_MONITOR_STATE_FILTER));
+                    family, InetDiagMessage.buildInetDiagReqForAliveTcpSockets(family));
         }
         mDependencies.addDeviceConfigChangedListener(mConfigListener);
         mDependencies.addDeviceIdleReceiver(mDeviceIdleReceiver);
@@ -253,7 +232,7 @@
         } catch (ErrnoException | SocketException | InterruptedIOException e) {
             Log.e(TAG, "Fail to get TCP info via netlink.", e);
         } finally {
-            NetworkStackUtils.closeSocketQuietly(fd);
+            SocketUtils.closeSocketQuietly(fd);
         }
 
         return false;
@@ -359,15 +338,15 @@
             final int nlmsgLen, final long time) {
         final int remainingDataSize = bytes.position() + nlmsgLen - SOCKDIAG_MSG_HEADER_SIZE;
         TcpInfo tcpInfo = null;
-        int mark = SocketInfo.INIT_MARK_VALUE;
+        int mark = NetlinkUtils.INIT_MARK_VALUE;
         // Get a tcp_info.
         while (bytes.position() < remainingDataSize) {
             final RoutingAttribute rtattr =
                     new RoutingAttribute(bytes.getShort(), bytes.getShort());
             final short dataLen = rtattr.getDataLength();
-            if (rtattr.rtaType == RoutingAttribute.INET_DIAG_INFO) {
+            if (rtattr.rtaType == NetlinkUtils.INET_DIAG_INFO) {
                 tcpInfo = TcpInfo.parse(bytes, dataLen);
-            } else if (rtattr.rtaType == RoutingAttribute.INET_DIAG_MARK) {
+            } else if (rtattr.rtaType == NetlinkUtils.INET_DIAG_MARK) {
                 mark = bytes.getInt();
             } else {
                 // Data provided by kernel will include both valid data and padding data. The data
@@ -524,9 +503,6 @@
      */
     class RoutingAttribute {
         public static final int HEADER_LENGTH = 4;
-        // Corresponds to enum definition in bionic/libc/kernel/uapi/linux/inet_diag.h
-        public static final int INET_DIAG_INFO = 2;
-        public static final int INET_DIAG_MARK = 15;
 
         public final short rtaLen;  // The whole valid size of the struct.
         public final short rtaType;
@@ -545,8 +521,6 @@
      */
     @VisibleForTesting
     class SocketInfo {
-        // Initial mark value corresponds to the initValue in system/netd/include/Fwmark.h.
-        public static final int INIT_MARK_VALUE = 0;
         @Nullable
         public final TcpInfo tcpInfo;
         // One of {@code AF_INET6, AF_INET}.
@@ -633,11 +607,10 @@
          * Throw ErrnoException, SocketException if the exception is thrown.
          */
         public FileDescriptor connectToKernel() throws ErrnoException, SocketException {
-            final FileDescriptor fd =
-                    Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_INET_DIAG);
-            Os.connect(
-                    fd, SocketUtils.makeNetlinkSocketAddress(0 /* portId */, 0 /* groupMask */));
-
+            final FileDescriptor fd = NetlinkUtils.createNetLinkInetDiagSocket();
+            NetlinkUtils.connectSocketToNetlink(fd);
+            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO,
+                    StructTimeval.fromMillis(IO_TIMEOUT_MS));
             return fd;
         }
 
@@ -650,8 +623,6 @@
          */
         public void sendPollingRequest(@NonNull final FileDescriptor fd, @NonNull final byte[] msg)
                 throws ErrnoException, InterruptedIOException {
-            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO,
-                    StructTimeval.fromMillis(IO_TIMEOUT));
             Os.write(fd, msg, 0 /* byteOffset */, msg.length);
         }
 
@@ -682,7 +653,7 @@
          */
         public ByteBuffer recvMessage(@NonNull final FileDescriptor fd)
                 throws ErrnoException, InterruptedIOException {
-            return NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT);
+            return NetlinkUtils.recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
         }
 
         public Context getContext() {
diff --git a/src/com/android/networkstack/util/NetworkStackUtils.java b/src/com/android/networkstack/util/NetworkStackUtils.java
index 67a67c2..5b329dd 100755
--- a/src/com/android/networkstack/util/NetworkStackUtils.java
+++ b/src/com/android/networkstack/util/NetworkStackUtils.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.net.LinkAddress;
 import android.net.MacAddress;
-import android.net.util.SocketUtils;
 import android.system.ErrnoException;
 import android.util.Log;
 
@@ -261,16 +260,6 @@
     }
 
     /**
-     * Close a socket, ignoring any exception while closing.
-     */
-    public static void closeSocketQuietly(FileDescriptor fd) {
-        try {
-            SocketUtils.closeSocket(fd);
-        } catch (IOException ignored) {
-        }
-    }
-
-    /**
      * Convert IPv6 multicast address to ethernet multicast address in network order.
      */
     public static MacAddress ipv6MulticastToEthernetMulticast(@NonNull final Inet6Address addr) {
@@ -309,7 +298,7 @@
     }
 
     /**
-     * Check whether a link address is IPv6 global unicast address.
+     * Check whether a link address is IPv6 global preferred unicast address.
      */
     public static boolean isIPv6GUA(@NonNull final LinkAddress address) {
         return address.isIpv6() && address.isGlobalPreferred();
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java
index 8e0130e..4be1d89 100755
--- a/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -279,7 +279,12 @@
 
     static {
         // CTC
+        // This is a wrong config, but it may need to be here for a while since the
+        // carrier_list.textpb in OEM side may still wrong.
+        // TODO: Remove this wrong config when the carrier_list.textpb is corrected everywhere.
         sCarrierIdToMccMnc.put(1854, new MccMncOverrideInfo(460, 03));
+        // China telecom.
+        sCarrierIdToMccMnc.put(2237, new MccMncOverrideInfo(460, 03));
     }
 
     /**
@@ -471,8 +476,7 @@
     @NonNull
     private LinkProperties mLinkProperties;
 
-    @VisibleForTesting
-    protected boolean mIsCaptivePortalCheckEnabled;
+    private final boolean mIsCaptivePortalCheckEnabled;
 
     private boolean mUseHttps;
     /**
@@ -572,7 +576,8 @@
     public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
             SharedLog validationLog, @NonNull NetworkStackServiceManager serviceManager) {
         this(context, cb, network, new IpConnectivityLog(), validationLog, serviceManager,
-                Dependencies.DEFAULT, getTcpSocketTrackerOrNull(context, network));
+                Dependencies.DEFAULT, getTcpSocketTrackerOrNull(context, network,
+                        Dependencies.DEFAULT));
     }
 
     @VisibleForTesting
@@ -620,7 +625,7 @@
         mTestCaptivePortalHttpsUrl =
                 getTestUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL, validationLogs, deps);
         mTestCaptivePortalHttpUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTP_URL, validationLogs, deps);
-        mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
+        mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled(context, deps);
         mPrivateIpNoInternetEnabled = getIsPrivateIpNoInternetEnabled();
         mMetricsEnabled = deps.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY,
                 NetworkStackUtils.VALIDATION_METRICS_VERSION, true /* defaultEnabled */);
@@ -634,8 +639,8 @@
         mDataStallMinEvaluateTime = getDataStallMinEvaluateTime();
         mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
         mDataStallEvaluationType = getDataStallEvaluationType();
-        mDnsStallDetector = initDnsStallDetectorIfRequired(mDataStallEvaluationType,
-                mConsecutiveDnsTimeoutThreshold);
+        mDnsStallDetector = initDnsStallDetectorIfRequired(mIsCaptivePortalCheckEnabled,
+                mDataStallEvaluationType, mConsecutiveDnsTimeoutThreshold);
         mTcpTracker = tst;
         // Read the configurations of evaluating network bandwidth.
         mEvaluatingBandwidthUrl = getResStringConfig(mContext,
@@ -791,6 +796,10 @@
                 mNetworkAgentConfig.isVpnValidationRequired(), mNetworkCapabilities);
     }
 
+    private boolean isDataStallDetectionRequired() {
+        return mIsCaptivePortalCheckEnabled && isValidationRequired();
+    }
+
     private boolean isPrivateDnsValidationRequired() {
         return NetworkMonitorUtils.isPrivateDnsValidationRequired(mNetworkCapabilities);
     }
@@ -1009,7 +1018,7 @@
                             mUserDoesNotWant = true;
                             mEvaluationState.reportEvaluationResult(
                                     NETWORK_VALIDATION_RESULT_INVALID, null);
-                            // TODO: Should teardown network.
+
                             mUidResponsibleForReeval = 0;
                             transitionTo(mEvaluatingState);
                             break;
@@ -1143,7 +1152,7 @@
         }
 
         private void initSocketTrackingIfRequired() {
-            if (!isValidationRequired()) return;
+            if (!isDataStallDetectionRequired()) return;
 
             final TcpSocketTracker tst = getTcpSocketTracker();
             if (tst != null) {
@@ -1206,7 +1215,7 @@
 
     @VisibleForTesting
     void sendTcpPollingEvent() {
-        if (isValidationRequired()) {
+        if (isDataStallDetectionRequired()) {
             sendMessageDelayed(EVENT_POLL_TCPINFO, getTcpPollingInterval());
         }
     }
@@ -1950,10 +1959,11 @@
         return true;
     }
 
-    private boolean getIsCaptivePortalCheckEnabled() {
+    private static boolean getIsCaptivePortalCheckEnabled(@NonNull Context context,
+            @NonNull Dependencies dependencies) {
         String symbol = CAPTIVE_PORTAL_MODE;
         int defaultValue = CAPTIVE_PORTAL_MODE_PROMPT;
-        int mode = mDependencies.getSetting(mContext, symbol, defaultValue);
+        int mode = dependencies.getSetting(context, symbol, defaultValue);
         return mode != CAPTIVE_PORTAL_MODE_IGNORE;
     }
 
@@ -3439,7 +3449,7 @@
 
     @VisibleForTesting
     protected boolean isDataStall() {
-        if (!isValidationRequired()) {
+        if (!isDataStallDetectionRequired()) {
             return false;
         }
 
@@ -3693,8 +3703,10 @@
     }
 
     @Nullable
-    private static TcpSocketTracker getTcpSocketTrackerOrNull(Context context, Network network) {
-        return ((Dependencies.DEFAULT.getDeviceConfigPropertyInt(
+    private static TcpSocketTracker getTcpSocketTrackerOrNull(Context context, Network network,
+                Dependencies deps) {
+        return (getIsCaptivePortalCheckEnabled(context, deps)
+                && (deps.getDeviceConfigPropertyInt(
                 NAMESPACE_CONNECTIVITY,
                 CONFIG_DATA_STALL_EVALUATION_TYPE,
                 DEFAULT_DATA_STALL_EVALUATION_TYPES)
@@ -3704,8 +3716,9 @@
     }
 
     @Nullable
-    private DnsStallDetector initDnsStallDetectorIfRequired(int type, int threshold) {
-        return ((type & DATA_STALL_EVALUATION_TYPE_DNS) != 0)
+    private DnsStallDetector initDnsStallDetectorIfRequired(boolean isCaptivePortalCheckEnabled,
+                int type, int threshold) {
+        return (isCaptivePortalCheckEnabled && (type & DATA_STALL_EVALUATION_TYPE_DNS) != 0)
                 ? new DnsStallDetector(threshold) : null;
     }
 
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp
index 7f8a9d3..6b98d4a 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -99,7 +99,7 @@
         "signature/**/*.kt",
     ],
     static_libs: [
-        "NetworkStackApiCurrentLib",
+        "NetworkStackApiStableLib",
     ],
     certificate: "networkstack",
     platform_apis: true,
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
index 50f25e6..3cb651e 100644
--- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -59,6 +59,7 @@
 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
@@ -88,6 +89,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.eq;
@@ -2850,7 +2852,7 @@
         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
         assertTrue(lp.getDnsServers().contains(SERVER_ADDR));
 
-        reset(mCb);
+        clearInvocations(mCb);
     }
 
     private void doDualStackProvisioning(boolean shouldDisableAcceptRa) throws Exception {
@@ -3711,6 +3713,23 @@
         return nsList;
     }
 
+    private NeighborSolicitation expectDadNeighborSolicitationForLinkLocal(boolean shouldDisableDad)
+            throws Exception {
+        final NeighborSolicitation ns = getNextNeighborSolicitation();
+        if (!shouldDisableDad) {
+            final Inet6Address solicitedNodeMulticast =
+                    NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(ns.nsHdr.target);
+            assertNotNull("No multicast NS received on interface within timeout", ns);
+            assertEquals(IPV6_ADDR_ANY, ns.ipv6Hdr.srcIp);     // srcIp: ::/
+            assertTrue(ns.ipv6Hdr.dstIp.isMulticastAddress()); // dstIp: solicited-node mcast
+            assertTrue(ns.ipv6Hdr.dstIp.equals(solicitedNodeMulticast));
+            assertTrue(ns.nsHdr.target.isLinkLocalAddress());  // targetIp: IPv6 LL address
+        } else {
+            assertNull(ns);
+        }
+        return ns;
+    }
+
     // Override this function with disabled experiment flag by default, in order not to
     // affect those tests which are just related to basic IpReachabilityMonitor infra.
     private void prepareIpReachabilityMonitorTest() throws Exception {
@@ -3957,6 +3976,59 @@
         );
     }
 
+    private void runIpv6LinkLocalOnlyDadTransmitsCheckTest(boolean shouldDisableDad)
+            throws Exception {
+        ProvisioningConfiguration.Builder config = new ProvisioningConfiguration.Builder()
+                .withoutIPv4()
+                .withIpv6LinkLocalOnly()
+                .withRandomMacAddress();
+        if (shouldDisableDad) config.withUniqueEui64AddressesOnly();
+
+        // dad_transmits has been set to 0 in disableIpv6ProvisioningDelays, re-enable dad_transmits
+        // for testing, but production code could disable dad again later, we should never see any
+        // multicast NS for duplicate address detection then.
+        mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "1");
+        startIpClientProvisioning(config.build());
+        verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetEnableIPv6(mIfaceName, true);
+        // Check dad_transmits should be set to 0 if UniqueEui64AddressesOnly mode is enabled.
+        int dadTransmits = Integer.parseUnsignedInt(
+                mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits"));
+        if (shouldDisableDad) {
+            assertEquals(0, dadTransmits);
+        } else {
+            assertEquals(1, dadTransmits);
+        }
+
+        final NeighborSolicitation ns =
+                expectDadNeighborSolicitationForLinkLocal(shouldDisableDad);
+        if (shouldDisableDad) {
+            assertNull(ns);
+        } else {
+            assertNotNull(ns);
+        }
+
+        // Shutdown IpClient and check if the dad_transmits always equals to default value 1 (if
+        // dad_transmit was set to 0 before, it should get recovered to default value 1 after
+        // shutting down IpClient)
+        mIpc.shutdown();
+        awaitIpClientShutdown();
+        dadTransmits = Integer.parseUnsignedInt(
+                mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits"));
+        assertEquals(1, dadTransmits);
+    }
+
+    @Test
+    @SignatureRequiredTest(reason = "requires mocked netd")
+    public void testIPv6LinkLocalOnly_enableDad() throws Exception {
+        runIpv6LinkLocalOnlyDadTransmitsCheckTest(false /* shouldDisableDad */);
+    }
+
+    @Test
+    @SignatureRequiredTest(reason = "requires mocked netd")
+    public void testIPv6LinkLocalOnly_disableDad() throws Exception {
+        runIpv6LinkLocalOnlyDadTransmitsCheckTest(true /* shouldDisableDad */);
+    }
+
     // Since createTapInterface(boolean, String) method was introduced since T, this method
     // cannot be found on Q/R/S platform, ignore this test on T- platform.
     @Test
@@ -4088,8 +4160,31 @@
         assertFalse(lp.hasGlobalIpv6Address());
         assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local
         for (LinkAddress la : lp.getLinkAddresses()) {
-            final Inet6Address address = (Inet6Address) la.getAddress();
             assertFalse(NetworkStackUtils.isIPv6GUA(la));
         }
     }
+
+    @Test @SignatureRequiredTest(reason = "requires mNetd to delete IPv6 GUAs")
+    public void testOnIpv6AddressRemoved() throws Exception {
+        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+                .withoutIPv4()
+                .build();
+        startIpClientProvisioning(config);
+
+        LinkProperties lp = doIpv6OnlyProvisioning();
+        assertNotNull(lp);
+        assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local
+        for (LinkAddress la : lp.getLinkAddresses()) {
+            final Inet6Address address = (Inet6Address) la.getAddress();
+            if (address.isLinkLocalAddress()) continue;
+            // Remove IPv6 GUAs from interface.
+            mNetd.interfaceDelAddress(mIfaceName, address.getHostAddress(), la.getPrefixLength());
+        }
+
+        final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
+        verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
+        lp = captor.getValue();
+        assertFalse(lp.hasGlobalIpv6Address());
+        assertEquals(1, lp.getLinkAddresses().size()); // only link-local
+    }
 }
diff --git a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
index 3436cb6..0a80d70 100644
--- a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
+++ b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
@@ -25,7 +25,9 @@
 import android.net.TestNetworkManager
 import android.net.dhcp.DhcpPacket
 import android.os.HandlerThread
+import android.system.ErrnoException
 import android.system.Os
+import android.system.OsConstants
 import android.system.OsConstants.AF_INET
 import android.system.OsConstants.AF_PACKET
 import android.system.OsConstants.ARPHRD_ETHER
@@ -38,10 +40,16 @@
 import android.system.OsConstants.SO_RCVTIMEO
 import android.system.StructTimeval
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.util.HexDump
 import com.android.net.module.util.InterfaceParams
+import com.android.net.module.util.IpUtils
 import com.android.net.module.util.Ipv6Utils
 import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN
 import com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY
+import com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET
+import com.android.net.module.util.NetworkStackConstants.IPV4_FLAG_DF
+import com.android.net.module.util.NetworkStackConstants.IPV4_FLAG_MF
+import com.android.net.module.util.NetworkStackConstants.IPV4_FLAGS_OFFSET
 import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST
 import com.android.net.module.util.structs.PrefixInformationOption
 import com.android.networkstack.util.NetworkStackUtils
@@ -216,6 +224,78 @@
         val addr3 = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(TEST_INET6ADDR_3)
         assertSolicitedNodeMulticastAddress(addr3, TEST_INET6ADDR_3)
     }
+
+    private fun assertSocketReadErrno(msg: String, fd: FileDescriptor, errno: Int) {
+        val received = ByteBuffer.allocate(TEST_MTU)
+        try {
+            val len = Os.read(fd, received)
+            fail(msg + ": " + toHexString(received, len))
+        } catch (expected: ErrnoException) {
+            assertEquals(errno.toLong(), expected.errno.toLong())
+        }
+    }
+
+    private fun assertNextPacketOnSocket(fd: FileDescriptor, expectedPacket: ByteBuffer) {
+        val received = ByteBuffer.allocate(TEST_MTU)
+        val len = Os.read(fd, received)
+        assertEquals(toHexString(expectedPacket, expectedPacket.limit()),
+            toHexString(received, len))
+    }
+
+    private fun setMfBit(packet: ByteBuffer, set: Boolean) {
+        val offset = ETHER_HEADER_LENGTH + IPV4_FLAGS_OFFSET
+        var flagOff: Int = packet.getShort(offset).toInt()
+        if (set) {
+            flagOff = (flagOff or IPV4_FLAG_MF) and IPV4_FLAG_DF.inv()
+        } else {
+            flagOff = (flagOff or IPV4_FLAG_DF) and IPV4_FLAG_MF.inv()
+        }
+        packet.putShort(offset, flagOff.toShort())
+        // Recalculate the checksum, which requires first clearing the checksum field.
+        val checksumOffset = ETHER_HEADER_LENGTH + IPV4_CHECKSUM_OFFSET
+        packet.putShort(checksumOffset, 0)
+        packet.putShort(checksumOffset, IpUtils.ipChecksum(packet, ETHER_HEADER_LENGTH))
+    }
+
+    private fun doTestDhcpResponseWithMfBit(dropMf: Boolean) {
+        val ifindex = InterfaceParams.getByName(iface.interfaceName).index
+        val packetSock = Os.socket(AF_PACKET, SOCK_RAW or SOCK_NONBLOCK, /*protocol=*/0)
+        try {
+            NetworkStackUtils.attachDhcpFilter(packetSock, dropMf)
+            val addr = SocketUtils.makePacketSocketAddress(OsConstants.ETH_P_IP, ifindex)
+            Os.bind(packetSock, addr)
+            val packet = DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, 42,
+                TEST_TARGET_IPV4_ADDR, /*relayIp=*/ IPV4_ADDR_ANY, TEST_TARGET_MAC.toByteArray(),
+                /*broadcast=*/ false, "NAK")
+            setMfBit(packet, true)
+            reader.sendResponse(packet)
+
+            // Packet with MF bit set is received iff dropMf is false.
+            if (dropMf) {
+                assertSocketReadErrno("Packet with MF bit should have been dropped",
+                    packetSock, OsConstants.EAGAIN)
+            } else {
+                assertNextPacketOnSocket(packetSock, packet)
+            }
+
+            // Identical packet, except with MF bit cleared, should always be received.
+            setMfBit(packet, false)
+            reader.sendResponse(packet)
+            assertNextPacketOnSocket(packetSock, packet)
+        } finally {
+            Os.close(packetSock)
+        }
+    }
+
+    @Test
+    fun testDhcpResponseWithMfBitDropped() {
+        doTestDhcpResponseWithMfBit(/*dropMf=*/ true)
+    }
+
+    @Test
+    fun testDhcpResponseWithMfBitReceived() {
+        doTestDhcpResponseWithMfBit(/*dropMf=*/ false)
+    }
 }
 
 private fun ByteBuffer.readAsArray(): ByteArray {
@@ -224,5 +304,9 @@
     return out
 }
 
+private fun toHexString(b: ByteBuffer, len: Int): String {
+    return HexDump.toHexString(Arrays.copyOf(b.array(), len))
+}
+
 private fun <T : Any> Context.assertHasService(manager: KClass<T>) = getSystemService(manager.java)
         ?: fail("Could not find service $manager")
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java
index bf3b87d..0846127 100644
--- a/tests/unit/src/android/net/apf/ApfTest.java
+++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -965,7 +965,7 @@
         public static final byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
 
         private FileDescriptor mWriteSocket;
-        private final long mFixedTimeMs = SystemClock.elapsedRealtime();
+        private long mCurrentTimeMs = SystemClock.elapsedRealtime();
 
         public TestApfFilter(Context context, ApfConfiguration config,
                 IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) throws Exception {
@@ -978,13 +978,18 @@
             Os.write(mWriteSocket, packet, 0, packet.length);
         }
 
-        @Override
-        protected long currentTimeSeconds() {
-            return mFixedTimeMs / DateUtils.SECOND_IN_MILLIS;
+        // Simulate current time changes
+        public void increaseCurrentTimeSeconds(int delta) {
+            mCurrentTimeMs += delta * DateUtils.SECOND_IN_MILLIS;
         }
 
         @Override
-        public void maybeStartFilter() {
+        protected long currentTimeSeconds() {
+            return mCurrentTimeMs / DateUtils.SECOND_IN_MILLIS;
+        }
+
+        @Override
+        public synchronized void maybeStartFilter() {
             mHardwareAddress = MOCK_MAC_ADDR;
             installNewProgramLocked();
 
@@ -1951,8 +1956,16 @@
     // Verify that the last program pushed to the IpClient.Callback properly filters the
     // given packet for the given lifetime.
     private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
+        verifyRaLifetime(program, packet, lifetime, 0);
+    }
+
+    // Verify that the last program pushed to the IpClient.Callback properly filters the
+    // given packet for the given lifetime and programInstallTime. programInstallTime is
+    // the time difference between when RA is last seen and the program is installed.
+    private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime,
+            int programInstallTime) {
         final int FRACTION_OF_LIFETIME = 6;
-        final int ageLimit = lifetime / FRACTION_OF_LIFETIME;
+        final int ageLimit = lifetime / FRACTION_OF_LIFETIME - programInstallTime;
 
         // Verify new program should drop RA for 1/6th its lifetime and pass afterwards.
         assertDrop(program, packet.array());
@@ -2207,6 +2220,48 @@
         assertPass(program, raPacket.array());
     }
 
+    // The ByteBuffer is always created by ByteBuffer#wrap in the helper functions
+    @SuppressWarnings("ByteBufferBackingArray")
+    @Test
+    public void testRaWithProgramInstalledSomeTimeAfterLastSeen() throws Exception {
+        final MockIpClientCallback ipClientCallback = new MockIpClientCallback();
+        final ApfConfiguration config = getDefaultConfig();
+        config.multicastFilter = DROP_MULTICAST;
+        config.ieee802_3Filter = DROP_802_3_FRAMES;
+        final TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+        byte[] program = ipClientCallback.getApfProgram();
+
+        final int routerLifetime = 1000;
+        final int timePassedSeconds = 12;
+
+        // Verify that when the program is generated and installed some time after RA is last seen
+        // it should be installed with the correct remaining lifetime.
+        ByteBuffer basePacket = makeBaseRaPacket();
+        verifyRaLifetime(apfFilter, ipClientCallback, basePacket, routerLifetime);
+        apfFilter.increaseCurrentTimeSeconds(timePassedSeconds);
+        synchronized (apfFilter) {
+            apfFilter.installNewProgramLocked();
+        }
+        program = ipClientCallback.getApfProgram();
+        verifyRaLifetime(program, basePacket, routerLifetime, timePassedSeconds);
+
+        // Packet should be passed if the program is installed after 1/6 * lifetime from last seen
+        apfFilter.increaseCurrentTimeSeconds((int) (routerLifetime / 6) - timePassedSeconds - 1);
+        synchronized (apfFilter) {
+            apfFilter.installNewProgramLocked();
+        }
+        program = ipClientCallback.getApfProgram();
+        assertDrop(program, basePacket.array());
+        apfFilter.increaseCurrentTimeSeconds(1);
+        synchronized (apfFilter) {
+            apfFilter.installNewProgramLocked();
+        }
+        program = ipClientCallback.getApfProgram();
+        assertPass(program, basePacket.array());
+
+        apfFilter.shutdown();
+    }
+
     /**
      * Stage a file for testing, i.e. make it native accessible. Given a resource ID,
      * copy that resource into the app's data directory and return the path to it.
diff --git a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java
index 2734fd2..00c544b 100644
--- a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java
+++ b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java
@@ -115,6 +115,7 @@
                 new String("android-dhcp-11").getBytes());
         config.mIPv4ProvisioningMode = PROV_IPV4_DHCP;
         config.mIPv6ProvisioningMode = PROV_IPV6_SLAAC;
+        config.mUniqueEui64AddressesOnly = false;
         return config;
     }
 
@@ -122,6 +123,7 @@
         final ProvisioningConfigurationParcelable p = new ProvisioningConfigurationParcelable();
         p.enableIPv4 = true;
         p.enableIPv6 = true;
+        p.uniqueEui64AddressesOnly = false;
         p.usingMultinetworkPolicyTracker = true;
         p.usingIpReachabilityMonitor = true;
         p.requestedPreDhcpActionMs = 42;
@@ -151,7 +153,7 @@
     public void setUp() {
         mConfig = makeTestProvisioningConfiguration();
         // Any added field must be included in equals() to be tested properly
-        assertFieldCountEquals(16, ProvisioningConfiguration.class);
+        assertFieldCountEquals(17, ProvisioningConfiguration.class);
     }
 
     @Test
@@ -282,7 +284,8 @@
         assertNotEqualsAfterChange(c -> c.mIPv4ProvisioningMode = PROV_IPV4_STATIC);
         assertNotEqualsAfterChange(c -> c.mIPv6ProvisioningMode = PROV_IPV6_DISABLED);
         assertNotEqualsAfterChange(c -> c.mIPv6ProvisioningMode = PROV_IPV6_LINKLOCAL);
-        assertFieldCountEquals(16, ProvisioningConfiguration.class);
+        assertNotEqualsAfterChange(c -> c.mUniqueEui64AddressesOnly = true);
+        assertFieldCountEquals(17, ProvisioningConfiguration.class);
     }
 
     private void assertNotEqualsAfterChange(Consumer<ProvisioningConfiguration> mutator) {
@@ -315,7 +318,7 @@
             + DhcpOption.class.getName()
             + "{type: 60,"
             + " value: [97, 110, 100, 114, 111, 105, 100, 45, 100, 104, 99, 112, 45, 49, 49]}],"
-            + " ipv4ProvisioningMode: 2, ipv6ProvisioningMode: 1}";
+            + " ipv4ProvisioningMode: 2, ipv6ProvisioningMode: 1, uniqueEui64AddressesOnly: false}";
 
     @Test
     public void testParcelableToString() {
diff --git a/tests/unit/src/android/net/testutils/DnsAnswerProviderTest.kt b/tests/unit/src/android/net/testutils/DnsAnswerProviderTest.kt
new file mode 100644
index 0000000..5e625e5
--- /dev/null
+++ b/tests/unit/src/android/net/testutils/DnsAnswerProviderTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.testutils
+
+import android.net.DnsResolver.CLASS_IN
+import android.net.DnsResolver.TYPE_A
+import android.net.DnsResolver.TYPE_AAAA
+import androidx.test.filters.SmallTest
+import com.android.net.module.util.DnsPacket
+import com.android.testutils.DnsAnswerProvider
+import libcore.net.InetAddressUtils
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@RunWith(JUnit4::class)
+@SmallTest
+class DnsAnswerProviderTest {
+    val ansProvider = DnsAnswerProvider()
+
+    private fun listOfAddresses(vararg addresses: String) =
+        addresses.map { address -> InetAddressUtils.parseNumericAddress(address) }
+
+    @Test
+    fun testIpv4Answers() {
+        val record1 = getTestDnsRecord("www.google.com", "1.2.3.4")
+        val record2 = getTestDnsRecord("www.google.com", "5.6.7.8")
+
+        // Verifies that empty response is returned before mocking.
+        assertEquals(emptyList(), ansProvider.getAnswer("www.google.com", TYPE_A))
+
+        // Verifies that single response can be returned correctly.
+        ansProvider.setAnswer("www.google.com", listOfAddresses("1.2.3.4"))
+        assertEquals(listOf(record1), ansProvider.getAnswer("www.google.com", TYPE_A))
+
+        // Verifies that multiple responses can be returned correctly.
+        ansProvider.setAnswer("www.google.com", listOfAddresses("1.2.3.4", "5.6.7.8"))
+        assertEquals(listOf(record1, record2), ansProvider.getAnswer("www.google.com", TYPE_A))
+
+        // Verifies that null response is returned if queried with wrong type or wrong name.
+        assertEquals(emptyList(), ansProvider.getAnswer("www.google.com", TYPE_AAAA))
+        assertEquals(emptyList(), ansProvider.getAnswer("www.android.com", TYPE_A))
+
+        // Verifies that the answers for different entry has no effect to the testing one.
+        ansProvider.setAnswer("www.example.com", listOfAddresses("8.8.8.8"))
+        assertEquals(listOf(record1, record2), ansProvider.getAnswer("www.google.com", TYPE_A))
+
+        // Verifies that the responses can be cleared.
+        ansProvider.clearAnswer("www.google.com")
+        assertEquals(emptyList(), ansProvider.getAnswer("www.google.com", TYPE_A))
+    }
+
+    @Test
+    fun testIpv4v6Answers() {
+        val record1 = getTestDnsRecord("www.google.com", "2001:db8::1")
+        val record2 = getTestDnsRecord("www.google.com", "2001:db8::2")
+        val record3 = getTestDnsRecord("www.google.com", "2001:db8::4")
+        val v4Record1 = getTestDnsRecord("www.google.com", "1.2.3.4")
+
+        // Verifies that null response is returned before mocking
+        assertEquals(emptyList(), ansProvider.getAnswer("www.google.com", TYPE_AAAA))
+
+        ansProvider.setAnswer("www.google.com", listOfAddresses("2001:db8::1", "2001:db8::2"))
+        assertEquals(listOf(record1, record2), ansProvider.getAnswer("www.google.com", TYPE_AAAA))
+
+        // Verifies that the answers for different entry has no effect to the testing one.
+        ansProvider.setAnswer("www.example.com", listOfAddresses("2001:db8::3"))
+        assertEquals(listOf(record1, record2), ansProvider.getAnswer("www.google.com", TYPE_AAAA))
+
+        // Verifies that null response is returned if queried with wrong type or wrong name.
+        assertEquals(emptyList(), ansProvider.getAnswer("www.google.com", TYPE_A))
+        assertEquals(emptyList(), ansProvider.getAnswer("www.android.com", TYPE_AAAA))
+
+        // Verifies that the responses can be replaced. And different types can be mixed.
+        ansProvider.setAnswer("www.google.com", listOfAddresses("2001:db8::4", "1.2.3.4"))
+        assertEquals(listOf(record3), ansProvider.getAnswer("www.google.com", TYPE_AAAA))
+        assertEquals(listOf(v4Record1), ansProvider.getAnswer("www.google.com", TYPE_A))
+
+        // Verifies that the responses can be cleared.
+        ansProvider.clearAnswer("www.google.com")
+        assertEquals(emptyList(), ansProvider.getAnswer("www.google.com", TYPE_AAAA))
+    }
+
+    private fun getTestDnsRecord(dName: String, address: String) =
+            DnsPacket.DnsRecord.makeAOrAAAARecord(DnsPacket.ANSECTION, dName, CLASS_IN, 5 /* ttl */,
+                    InetAddressUtils.parseNumericAddress(address))
+}
\ No newline at end of file
diff --git a/tests/unit/src/android/net/testutils/HandlerUtilsTest.kt b/tests/unit/src/android/net/testutils/HandlerUtilsTest.kt
deleted file mode 100644
index c885874..0000000
--- a/tests/unit/src/android/net/testutils/HandlerUtilsTest.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.testutils
-
-import android.os.HandlerThread
-import com.android.testutils.waitForIdle
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import kotlin.test.assertEquals
-
-const val ATTEMPTS = 50 // Causes the test to take about 150ms on aosp_crosshatch-eng.
-const val TIMEOUT_MS = 200
-
-@RunWith(JUnit4::class)
-class HandlerUtilsTest {
-    @Test
-    fun testWaitForIdle() {
-        val handlerThread = HandlerThread("testHandler").apply { start() }
-
-        // Tests that waitForIdle can be called many times without ill impact if the service is
-        // already idle.
-        repeat(ATTEMPTS) {
-            handlerThread.waitForIdle(TIMEOUT_MS)
-        }
-
-        // Tests that calling waitForIdle waits for messages to be processed. Use both an
-        // inline runnable that's instantiated at each loop run and a runnable that's instantiated
-        // once for all.
-        val tempRunnable = object : Runnable {
-            // Use StringBuilder preferentially to StringBuffer because StringBuilder is NOT
-            // thread-safe. It's part of the point that both runnables run on the same thread
-            // so if anything is wrong in that space it's better to opportunistically use a class
-            // where things might go wrong, even if there is no guarantee of failure.
-            var memory = StringBuilder()
-            override fun run() {
-                memory.append("b")
-            }
-        }
-        repeat(ATTEMPTS) { i ->
-            handlerThread.threadHandler.post({ tempRunnable.memory.append("a"); })
-            handlerThread.threadHandler.post(tempRunnable)
-            handlerThread.waitForIdle(TIMEOUT_MS)
-            assertEquals(tempRunnable.memory.toString(), "ab".repeat(i + 1))
-        }
-    }
-}
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
index 043ec49..85d3842 100644
--- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
+++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -52,6 +53,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.netlink.StructNlMsgHdr;
 import com.android.networkstack.apishim.ConstantsShim;
 import com.android.testutils.DevSdkIgnoreRule;
@@ -88,91 +90,15 @@
     private static final byte[] SOCK_DIAG_MSG_BYTES =
             HexEncoding.decode(DIAG_MSG_HEX.toCharArray(), false);
     // Hexadecimal representation of a SOCK_DIAG response with tcp info.
-    private static final String SOCK_DIAG_TCP_INET_HEX =
-            // struct nlmsghdr.
-            "14010000" +        // length = 276
-            "1400" +            // type = SOCK_DIAG_BY_FAMILY
-            "0301" +            // flags = NLM_F_REQUEST | NLM_F_DUMP
-            "00000000" +        // seqno
-            "00000000" +        // pid (0 == kernel)
-            // struct inet_diag_req_v2
-            "02" +              // family = AF_INET
-            "06" +              // state
-            "00" +              // timer
-            "00" +              // retrans
-            // inet_diag_sockid
-            "DEA5" +            // idiag_sport = 42462
-            "71B9" +            // idiag_dport = 47473
-            "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2
-            "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
-            "00000000" +            // idiag_if
-            "34ED000076270000" +    // idiag_cookie = 43387759684916
-            "00000000" +            // idiag_expires
-            "00000000" +            // idiag_rqueue
-            "00000000" +            // idiag_wqueue
-            "00000000" +            // idiag_uid
-            "00000000" +            // idiag_inode
-            // rtattr
-            "0500" +            // len = 5
-            "0800" +            // type = 8
-            "00000000" +        // data
-            "0800" +            // len = 8
-            "0F00" +            // type = 15(INET_DIAG_MARK)
-            "850A0C00" +        // data, socket mark=789125
-            "AC00" +            // len = 172
-            "0200" +            // type = 2(INET_DIAG_INFO)
-            // tcp_info
-            "01" +              // state = TCP_ESTABLISHED
-            "00" +              // ca_state = TCP_CA_OPEN
-            "05" +              // retransmits = 5
-            "00" +              // probes = 0
-            "00" +              // backoff = 0
-            "07" +              // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS
-            "88" +              // wscale = 8
-            "00" +              // delivery_rate_app_limited = 0
-            "4A911B00" +        // rto = 1806666
-            "00000000" +        // ato = 0
-            "2E050000" +        // sndMss = 1326
-            "18020000" +        // rcvMss = 536
-            "00000000" +        // unsacked = 0
-            "00000000" +        // acked = 0
-            "00000000" +        // lost = 0
-            "00000000" +        // retrans = 0
-            "00000000" +        // fackets = 0
-            "BB000000" +        // lastDataSent = 187
-            "00000000" +        // lastAckSent = 0
-            "BB000000" +        // lastDataRecv = 187
-            "BB000000" +        // lastDataAckRecv = 187
-            "DC050000" +        // pmtu = 1500
-            "30560100" +        // rcvSsthresh = 87600
-            "3E2C0900" +        // rttt = 601150
-            "1F960400" +        // rttvar = 300575
-            "78050000" +        // sndSsthresh = 1400
-            "0A000000" +        // sndCwnd = 10
-            "A8050000" +        // advmss = 1448
-            "03000000" +        // reordering = 3
-            "00000000" +        // rcvrtt = 0
-            "30560100" +        // rcvspace = 87600
-            "00000000" +        // totalRetrans = 0
-            "53AC000000000000" +    // pacingRate = 44115
-            "FFFFFFFFFFFFFFFF" +    // maxPacingRate = 18446744073709551615
-            "0100000000000000" +    // bytesAcked = 1
-            "0000000000000000" +    // bytesReceived = 0
-            "0A000000" +        // SegsOut = 10
-            "00000000" +        // SegsIn = 0
-            "00000000" +        // NotSentBytes = 0
-            "3E2C0900" +        // minRtt = 601150
-            "00000000" +        // DataSegsIn = 0
-            "00000000" +        // DataSegsOut = 0
-            "0000000000000000"; // deliverRate = 0
-    private static final byte[] SOCK_DIAG_TCP_INET_BYTES =
-            HexEncoding.decode(SOCK_DIAG_TCP_INET_HEX.toCharArray(), false);
+    private static final String SOCK_DIAG_TCP_ZERO_LOST_HEX =
+            composeSockDiagTcpHex(0 /* lost */, 10 /* sent */);
+    private static final byte[] SOCK_DIAG_TCP_INET_ZERO_LOST_BYTES =
+            HexEncoding.decode(SOCK_DIAG_TCP_ZERO_LOST_HEX.toCharArray(), false);
     private static final TcpInfo TEST_TCPINFO =
             new TcpInfo(5 /* retransmits */, 0 /* lost */, 10 /* segsOut */, 0 /* segsIn */);
-
-    private static final String TEST_RESPONSE_HEX = SOCK_DIAG_TCP_INET_HEX
+    private static final String NLMSG_DONE_HEX =
             // struct nlmsghdr
-            + "14000000"     // length = 20
+            "14000000"     // length = 20
             + "0300"         // type = NLMSG_DONE
             + "0301"         // flags = NLM_F_REQUEST | NLM_F_DUMP
             + "00000000"     // seqno
@@ -182,6 +108,7 @@
             + "06"           // state
             + "00"           // timer
             + "00";          // retrans
+    private static final String TEST_RESPONSE_HEX = SOCK_DIAG_TCP_ZERO_LOST_HEX + NLMSG_DONE_HEX;
     private static final byte[] TEST_RESPONSE_BYTES =
             HexEncoding.decode(TEST_RESPONSE_HEX.toCharArray(), false);
     private static final int TEST_NETID1 = 0xA85;
@@ -232,15 +159,20 @@
         return parcel;
     }
 
+    private ByteBuffer getByteBufferFromHexString(String hexStr) {
+        final byte[] bytes = HexEncoding.decode(hexStr.toCharArray(), false);
+        return getByteBuffer(bytes);
+    }
+
     private ByteBuffer getByteBuffer(final byte[] bytes) {
         final ByteBuffer buffer = ByteBuffer.wrap(bytes);
-        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        buffer.order(ByteOrder.nativeOrder());
         return buffer;
     }
 
     @Test
     public void testParseSockInfo() {
-        final ByteBuffer buffer = getByteBuffer(SOCK_DIAG_TCP_INET_BYTES);
+        final ByteBuffer buffer = getByteBuffer(SOCK_DIAG_TCP_INET_ZERO_LOST_BYTES);
         final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork);
         buffer.position(SOCKDIAG_MSG_HEADER_SIZE);
         final TcpSocketTracker.SocketInfo parsed =
@@ -277,7 +209,7 @@
         when(mDependencies.isTcpInfoParsingSupported()).thenReturn(true);
         // No enough bytes remain for a valid NlMsg.
         final ByteBuffer invalidBuffer = ByteBuffer.allocate(1);
-        invalidBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        invalidBuffer.order(ByteOrder.nativeOrder());
         when(mDependencies.recvMessage(any())).thenReturn(invalidBuffer);
         assertTrue(tst.pollSocketsInfo());
         assertEquals(-1, tst.getLatestPacketFailPercentage());
@@ -329,6 +261,221 @@
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testTcpInfoParsingWithMultipleMsgs() throws Exception {
+        final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork);
+
+        // Case 1: A message about 5 sockets, then a message about 2 sockets,
+        // then a message about 2 sockets together with DONE
+        //
+        // Mocking 6 return results for different IP families(3 for IPv6; 3 for Ipv4). Use the same
+        // message for different IP families to reduce the complexity.
+        doReturn(getByteBufferFromHexString(repeat(composeSockDiagTcpHex(0, 10), 5)),
+                getByteBufferFromHexString(repeat(composeSockDiagTcpHex(0, 10), 2)),
+                getByteBufferFromHexString(
+                        repeat(composeSockDiagTcpHex(0, 10), 2) + NLMSG_DONE_HEX),
+                getByteBufferFromHexString(repeat(composeSockDiagTcpHex(0, 10), 5)),
+                getByteBufferFromHexString(repeat(composeSockDiagTcpHex(0, 10), 2)),
+                getByteBufferFromHexString(
+                        repeat(composeSockDiagTcpHex(0, 10), 2) + NLMSG_DONE_HEX))
+                .when(mDependencies).recvMessage(any());
+
+        assertTrue(tst.pollSocketsInfo());
+        // Verify that code reads all the messages. (3 times for IPv4, 3 times for IPv6)
+        verify(mDependencies, times(6)).recvMessage(any());
+        // Calculated from (retransmits + lost) / segsout.
+        assertEquals(50, tst.getLatestPacketFailPercentage());
+        // Lower than the 80% threshold
+        assertFalse(tst.isDataStallSuspected());
+
+        // Case 2: A message about 1 socket, then a message about 5 sockets,
+        // then a message about 1 socket with DONE.
+        // "Sent" increases by 5. No change for lost and retrans.
+        //
+        // Mocking 6 return results for different IP families(3 for IPv6; 3 for Ipv4). Use the same
+        // message for different IP families to reduce the complexity.
+        doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(5, 15)),
+                getByteBufferFromHexString(repeat(composeSockDiagTcpHex(5, 15), 5)),
+                getByteBufferFromHexString(composeSockDiagTcpHex(5, 15) + NLMSG_DONE_HEX),
+                getByteBufferFromHexString(composeSockDiagTcpHex(5, 15)),
+                getByteBufferFromHexString(repeat(composeSockDiagTcpHex(5, 15), 5)),
+                getByteBufferFromHexString(composeSockDiagTcpHex(5, 15) + NLMSG_DONE_HEX))
+                .when(mDependencies).recvMessage(any());
+
+        assertTrue(tst.pollSocketsInfo());
+        // Not reset mDependencies because it will reset other mocks.
+        // Another 3 times for IPv6 and 3 times for IPv4
+        verify(mDependencies, times(12)).recvMessage(any());
+        // (5 lost + 0 retrans)/5 sent
+        assertEquals(100, tst.getLatestPacketFailPercentage());
+        assertTrue(tst.isDataStallSuspected());
+
+        // Case 3: A message about 5 sockets, then a message about 1 socket,
+        // then a message about 1 socket with DONE.
+        // No change for sent, lost and retrans.
+        //
+        // Mocking 4 return results for different IP families(2 for IPv6; 2 for Ipv4). Use the same
+        // message for different IP families to reduce the complexity.
+        doReturn(getByteBufferFromHexString(repeat(composeSockDiagTcpHex(5, 15), 5)),
+                getByteBufferFromHexString(composeSockDiagTcpHex(5, 15)),
+                getByteBufferFromHexString(composeSockDiagTcpHex(5, 15) + NLMSG_DONE_HEX),
+                getByteBufferFromHexString(repeat(composeSockDiagTcpHex(5, 15), 5)),
+                getByteBufferFromHexString(composeSockDiagTcpHex(5, 15)),
+                getByteBufferFromHexString(composeSockDiagTcpHex(5, 15) + NLMSG_DONE_HEX))
+                .when(mDependencies).recvMessage(any());
+
+        assertTrue(tst.pollSocketsInfo());
+        // Another 3 times for IPv6 and 3 times for IPv4
+        verify(mDependencies, times(18)).recvMessage(any());
+        // (0 lost + 0 retrans)/0 sent
+        assertEquals(0, tst.getLatestPacketFailPercentage());
+        // Lower than the 80% threshold
+        assertFalse(tst.isDataStallSuspected());
+
+        // Case 4: A message about 8 sockets with DONE.
+        // "lost" increases by 3 and "sent" increases by 5
+        //
+        // Mocking 2 return results for different IP families(1 for IPv6; 1 for Ipv4). Use the same
+        // message for different IP families to reduce the complexity.
+        doReturn(getByteBufferFromHexString(
+                        repeat(composeSockDiagTcpHex(9, 20), 8) + NLMSG_DONE_HEX),
+                getByteBufferFromHexString(
+                        repeat(composeSockDiagTcpHex(9, 20), 8) + NLMSG_DONE_HEX))
+                .when(mDependencies).recvMessage(any());
+
+        assertTrue(tst.pollSocketsInfo());
+        // Another 1 time for IPv6 and 1 time for IPv4
+        verify(mDependencies, times(20)).recvMessage(any());
+        // (4 lost + 0 retrans)/5 sent
+        assertEquals(80, tst.getLatestPacketFailPercentage());
+        //Reach 80% threshold
+        assertTrue(tst.isDataStallSuspected());
+
+        // Case 5: A message about DONE with 2 sockets.
+        // No socket information will be parsed though "lost" increases by 6 and "sent"
+        // increases by 6.
+        //
+        // Mocking 2 return results for different IP families(1 for IPv6; 1 for Ipv4). Use the same
+        // message for different IP families to reduce the complexity.
+        doReturn(getByteBufferFromHexString(
+                        NLMSG_DONE_HEX + repeat(composeSockDiagTcpHex(15, 26), 2)),
+                getByteBufferFromHexString(
+                        NLMSG_DONE_HEX + repeat(composeSockDiagTcpHex(15, 26), 2)))
+                .when(mDependencies).recvMessage(any());
+        assertTrue(tst.pollSocketsInfo());
+        // Another 1 time for IPv6 and 1 time for IPv4
+        verify(mDependencies, times(22)).recvMessage(any());
+        // (0 lost + 0 retrans)/0 sent.
+        // Parsing will be stopped in DONE message. No socket information will be parsed.
+        assertEquals(0, tst.getLatestPacketFailPercentage());
+        // Lower than the 80% threshold
+        assertFalse(tst.isDataStallSuspected());
+    }
+
+    private String repeat(String orig, int times) {
+        if (SdkLevel.isAtLeastT()) {
+            // Only supported from Java 11
+            return orig.repeat(times);
+        } else {
+            String repeated = "";
+            for (int i = 0; i < times; i++) {
+                repeated += orig;
+            }
+            return repeated;
+        }
+    }
+
+    private static String getHexStringFromInt(int v) {
+        final ByteBuffer bb = ByteBuffer.allocate(4);
+        // Android is always little-endian. Refer to https://developer.android.com/ndk/guides/abis.
+        bb.order(ByteOrder.nativeOrder());
+        bb.putInt(v);
+        String s = "";
+        for (byte b : bb.array()) {
+            s += String.format("%02X", b);
+        }
+        return s;
+    }
+
+    private static String composeSockDiagTcpHex(int lost, int sent) {
+        return // struct nlmsghdr.
+                "14010000" +        // length = 276
+                "1400" +            // type = SOCK_DIAG_BY_FAMILY
+                "0301" +            // flags = NLM_F_REQUEST | NLM_F_DUMP
+                "00000000" +        // seqno
+                "00000000" +        // pid (0 == kernel)
+                // struct inet_diag_req_v2
+                "02" +              // family = AF_INET
+                "06" +              // state
+                "00" +              // timer
+                "00" +              // retrans
+                // inet_diag_sockid
+                "DEA5" +            // idiag_sport = 42462
+                "71B9" +            // idiag_dport = 47473
+                "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2
+                "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
+                "00000000" +            // idiag_if
+                "34ED000076270000" +    // idiag_cookie = 43387759684916
+                "00000000" +            // idiag_expires
+                "00000000" +            // idiag_rqueue
+                "00000000" +            // idiag_wqueue
+                "00000000" +            // idiag_uid
+                "00000000" +            // idiag_inode
+                // rtattr
+                "0500" +            // len = 5
+                "0800" +            // type = 8
+                "00000000" +        // data
+                "0800" +            // len = 8
+                "0F00" +            // type = 15(INET_DIAG_MARK)
+                "850A0C00" +        // data, socket mark=789125
+                "AC00" +            // len = 172
+                "0200" +            // type = 2(INET_DIAG_INFO)
+                // tcp_info
+                "01" +              // state = TCP_ESTABLISHED
+                "00" +              // ca_state = TCP_CA_OPEN
+                "05" +              // retransmits = 5
+                "00" +              // probes = 0
+                "00" +              // backoff = 0
+                "07" +              // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS
+                "88" +              // wscale = 8
+                "00" +              // delivery_rate_app_limited = 0
+                "4A911B00" +        // rto = 1806666
+                "00000000" +        // ato = 0
+                "2E050000" +        // sndMss = 1326
+                "18020000" +        // rcvMss = 536
+                "00000000" +        // unsacked = 0
+                "00000000" +        // acked = 0
+                getHexStringFromInt(lost) + // lost
+                "00000000" +        // retrans = 0
+                "00000000" +        // fackets = 0
+                "BB000000" +        // lastDataSent = 187
+                "00000000" +        // lastAckSent = 0
+                "BB000000" +        // lastDataRecv = 187
+                "BB000000" +        // lastDataAckRecv = 187
+                "DC050000" +        // pmtu = 1500
+                "30560100" +        // rcvSsthresh = 87600
+                "3E2C0900" +        // rttt = 601150
+                "1F960400" +        // rttvar = 300575
+                "78050000" +        // sndSsthresh = 1400
+                "0A000000" +        // sndCwnd = 10
+                "A8050000" +        // advmss = 1448
+                "03000000" +        // reordering = 3
+                "00000000" +        // rcvrtt = 0
+                "30560100" +        // rcvspace = 87600
+                "00000000" +        // totalRetrans = 0
+                "53AC000000000000" +    // pacingRate = 44115
+                "FFFFFFFFFFFFFFFF" +    // maxPacingRate = 18446744073709551615
+                "0100000000000000" +    // bytesAcked = 1
+                "0000000000000000" +    // bytesReceived = 0
+                getHexStringFromInt(sent) + // SegsOut
+                "00000000" +        // SegsIn = 0
+                "00000000" +        // NotSentBytes = 0
+                "3E2C0900" +        // minRtt = 601150
+                "00000000" +        // DataSegsIn = 0
+                "00000000" +        // DataSegsOut = 0
+                "0000000000000000"; // deliverRate = 0
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testTcpInfoParsingWithDozeMode() throws Exception {
         // This test requires shims that provide API 30 access
         assumeTrue(ConstantsShim.VERSION >= Build.VERSION_CODES.R);
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
index 122ec6e..a9e5950 100644
--- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -55,6 +55,9 @@
 import static com.android.net.module.util.NetworkStackConstants.TEST_URL_EXPIRATION_TIME;
 import static com.android.networkstack.util.DnsUtils.PRIVATE_DNS_PROBE_HOST_SUFFIX;
 import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
+import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE;
+import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_IGNORE;
+import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_PROMPT;
 import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
 import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
 import static com.android.networkstack.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT;
@@ -617,8 +620,18 @@
         }
     }
 
-    private TcpSocketTracker getTcpSocketTrackerOrNull(NetworkMonitor.Dependencies dp) {
-        return ((dp.getDeviceConfigPropertyInt(
+    private boolean getIsCaptivePortalCheckEnabled(Context context,
+                NetworkMonitor.Dependencies dp) {
+        String symbol = CAPTIVE_PORTAL_MODE;
+        int defaultValue = CAPTIVE_PORTAL_MODE_PROMPT;
+        int mode = dp.getSetting(context, symbol, defaultValue);
+        return mode != CAPTIVE_PORTAL_MODE_IGNORE;
+    }
+
+    private TcpSocketTracker getTcpSocketTrackerOrNull(Context context,
+                NetworkMonitor.Dependencies dp) {
+        return (getIsCaptivePortalCheckEnabled(context, dp)
+                && (dp.getDeviceConfigPropertyInt(
                 NAMESPACE_CONNECTIVITY,
                 CONFIG_DATA_STALL_EVALUATION_TYPE,
                 DEFAULT_DATA_STALL_EVALUATION_TYPES)
@@ -631,7 +644,7 @@
 
         WrappedNetworkMonitor() {
             super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, mServiceManager,
-                    mDependencies, getTcpSocketTrackerOrNull(mDependencies));
+                    mDependencies, getTcpSocketTrackerOrNull(mContext, mDependencies));
         }
 
         @Override
@@ -936,6 +949,16 @@
         assertEquals(wnm.getContext(), wnm.getCustomizedContextOrDefault());
     }
 
+    private void checkCustomizedContextByCarrierId(WrappedNetworkMonitor wnm, int carrierId) {
+        doReturn(carrierId).when(mTelephony).getSimCarrierId();
+        assertNotNull(wnm.getMccMncOverrideInfo());
+        // Check if the mcc & mnc has changed as expected.
+        assertEquals(460,
+                wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mcc);
+        assertEquals(03,
+                wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mnc);
+    }
+
     @Test
     public void testGetMccMncOverrideInfo() {
         final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
@@ -944,13 +967,10 @@
         doReturn(1839).when(mTelephony).getSimCarrierId();
         assertNull(wnm.getMccMncOverrideInfo());
         // 1854 is CTC's carrier id.
-        doReturn(1854).when(mTelephony).getSimCarrierId();
-        assertNotNull(wnm.getMccMncOverrideInfo());
-        // Check if the mcc & mnc has changed as expected.
-        assertEquals(460,
-                wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mcc);
-        assertEquals(03,
-                wnm.getCustomizedContextOrDefault().getResources().getConfiguration().mnc);
+        // This line should be removed when the production code is corrected.
+        checkCustomizedContextByCarrierId(wnm, 1854);
+        // 2237 is CT's carrier id.
+        checkCustomizedContextByCarrierId(wnm, 2237);
         // Every mcc and mnc should be set in sCarrierIdToMccMnc.
         // Check if there is any unset value in mcc or mnc.
         for (int i = 0; i < wnm.sCarrierIdToMccMnc.size(); i++) {
@@ -1752,6 +1772,17 @@
     }
 
     @Test
+    public void testIsDataStall_EvaluationDisabledOnIgnorePortal() {
+        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
+        setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
+        final WrappedNetworkMonitor nm = makeCellMeteredNetworkMonitor();
+        nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+
+        assertNull(nm.getDnsStallDetector());
+        assertFalse(nm.isDataStall());
+    }
+
+    @Test
     public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() throws Exception {
         WrappedNetworkMonitor wrappedMonitor = makeCellNotMeteredNetworkMonitor();
         wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
@@ -3272,4 +3303,3 @@
         assertEquals(isPortal ? 2 : 1, mRegisteredReceivers.size());
     }
 }
-