Merge "Thread: use test name as mobly-par-file-name" into main
diff --git a/framework/src/android/net/apf/ApfCapabilities.java b/framework/src/android/net/apf/ApfCapabilities.java
index fae2499..8efb182 100644
--- a/framework/src/android/net/apf/ApfCapabilities.java
+++ b/framework/src/android/net/apf/ApfCapabilities.java
@@ -22,6 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible
  * way to drop unwanted network packets to save power.
@@ -102,6 +104,11 @@
                 && apfPacketFormat == other.apfPacketFormat;
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
+    }
+
     /**
      * Determines whether the APF interpreter advertises support for the data buffer access opcodes
      * LDDW (LoaD Data Word) and STDW (STore Data Word). Full LDDW (LoaD Data Word) and
diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
index 02ef8b7..c726dab 100644
--- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
+++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
@@ -118,7 +118,7 @@
      * @hide
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = 35)  // TODO: change to VANILLA_ICE_CREAM.
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     public static final long NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION = 333340911L;
 
     private ConnectivityCompatChanges() {
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index ccb6acb..dc67f83 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -306,6 +306,7 @@
 
         if (bad && !isGSI()) {
             ALOGE("Unsupported kernel version (%07x).", kernelVersion());
+            sleep(60);
         }
     }
 
@@ -432,6 +433,7 @@
             ALOGE("Failed to set bpf.progs_loaded property to 1.");
             return 125;
         }
+        ALOGI("success.");
         return 0;
     }
 
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index b535ebf..91fec90 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "BpfHandler"
+#define LOG_TAG "NetdUpdatable"
 
 #include "BpfHandler.h"
 
diff --git a/remoteauth/service/jni/Android.bp b/remoteauth/service/jni/Android.bp
index c0ac779..fc91e0c 100644
--- a/remoteauth/service/jni/Android.bp
+++ b/remoteauth/service/jni/Android.bp
@@ -12,7 +12,7 @@
     srcs: ["src/lib.rs"],
     rustlibs: [
         "libbinder_rs",
-        "libjni",
+        "libjni_legacy",
         "liblazy_static",
         "liblog_rust",
         "liblogger",
diff --git a/service/Android.bp b/service/Android.bp
index 1d74efc..1dd09a9 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -188,7 +188,7 @@
         "androidx.annotation_annotation",
         "connectivity-net-module-utils-bpf",
         "connectivity_native_aidl_interface-lateststable-java",
-        "dnsresolver_aidl_interface-V14-java",
+        "dnsresolver_aidl_interface-V15-java",
         "modules-utils-shell-command-handler",
         "net-utils-device-common",
         "net-utils-device-common-ip",
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 39ea286..99ed4ce 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -120,6 +120,7 @@
 import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
 import static com.android.net.module.util.PermissionUtils.hasAnyPermissionOf;
 import static com.android.server.ConnectivityStatsLog.CONNECTIVITY_STATE_SAMPLE;
+import static com.android.server.connectivity.ConnectivityFlags.DELAY_DESTROY_SOCKETS;
 import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
 import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
 
@@ -554,6 +555,8 @@
      * default network for each app.
      * Order ints passed to netd must be in the 0~999 range. Larger values code for
      * a lower priority, see {@link NativeUidRangeConfig}.
+     * Note that only the highest priority preference is applied if the uid is the target of
+     * multiple preferences.
      *
      * Requests that don't code for a per-app preference use PREFERENCE_ORDER_INVALID.
      * The default request uses PREFERENCE_ORDER_DEFAULT.
@@ -996,14 +999,18 @@
     // Flag to enable the feature of closing frozen app sockets.
     private final boolean mDestroyFrozenSockets;
 
-    // Flag to optimize closing frozen app sockets by waiting for the cellular modem to wake up.
-    private final boolean mDelayDestroyFrozenSockets;
+    // Flag to optimize closing app sockets by waiting for the cellular modem to wake up.
+    private final boolean mDelayDestroySockets;
 
     // Flag to allow SysUI to receive connectivity reports for wifi picker UI.
     private final boolean mAllowSysUiConnectivityReports;
 
     // Uids that ConnectivityService is pending to close sockets of.
-    private final Set<Integer> mPendingFrozenUids = new ArraySet<>();
+    // Key is uid and value is reasons of socket destroy
+    private final SparseIntArray mDestroySocketPendingUids = new SparseIntArray();
+
+    private static final int DESTROY_SOCKET_REASON_NONE = 0;
+    private static final int DESTROY_SOCKET_REASON_FROZEN = 1 << 0;
 
     // Flag to drop packets to VPN addresses ingressing via non-VPN interfaces.
     private final boolean mIngressToVpnAddressFiltering;
@@ -1952,8 +1959,7 @@
 
         mDestroyFrozenSockets = mDeps.isAtLeastV() || (mDeps.isAtLeastU()
                 && mDeps.isFeatureEnabled(context, KEY_DESTROY_FROZEN_SOCKETS_VERSION));
-        mDelayDestroyFrozenSockets = mDeps.isAtLeastU()
-                && mDeps.isFeatureEnabled(context, DELAY_DESTROY_FROZEN_SOCKETS_VERSION);
+        mDelayDestroySockets = mDeps.isFeatureNotChickenedOut(context, DELAY_DESTROY_SOCKETS);
         mAllowSysUiConnectivityReports = mDeps.isFeatureNotChickenedOut(
                 mContext, ALLOW_SYSUI_CONNECTIVITY_REPORTS);
         if (mDestroyFrozenSockets) {
@@ -3347,44 +3353,51 @@
         return !mNetworkActivityTracker.isDefaultNetworkActive();
     }
 
-    private void handleFrozenUids(int[] uids, int[] frozenStates) {
-        final ArraySet<Integer> ownerUids = new ArraySet<>();
-
-        for (int i = 0; i < uids.length; i++) {
-            if (frozenStates[i] == UID_FROZEN_STATE_FROZEN) {
-                ownerUids.add(uids[i]);
-            } else {
-                mPendingFrozenUids.remove(uids[i]);
-            }
-        }
-
-        if (ownerUids.isEmpty()) {
-            return;
-        }
-
-        if (mDelayDestroyFrozenSockets && isCellNetworkIdle()) {
-            // Delay closing sockets to avoid waking the cell modem up.
-            // Wi-Fi network state is not considered since waking Wi-Fi modem up is much cheaper
-            // than waking cell modem up.
-            mPendingFrozenUids.addAll(ownerUids);
+    private void updateDestroySocketReasons(final int uid, final int reason,
+            final boolean addReason) {
+        final int destroyReasons = mDestroySocketPendingUids.get(uid, DESTROY_SOCKET_REASON_NONE);
+        if (addReason) {
+            mDestroySocketPendingUids.put(uid, destroyReasons | reason);
         } else {
-            try {
-                mDeps.destroyLiveTcpSocketsByOwnerUids(ownerUids);
-            } catch (SocketException | InterruptedIOException | ErrnoException e) {
-                loge("Exception in socket destroy: " + e);
+            final int newDestroyReasons = destroyReasons & ~reason;
+            if (newDestroyReasons == DESTROY_SOCKET_REASON_NONE) {
+                mDestroySocketPendingUids.delete(uid);
+            } else {
+                mDestroySocketPendingUids.put(uid, newDestroyReasons);
             }
         }
     }
 
-    private void closePendingFrozenSockets() {
+    private void handleFrozenUids(int[] uids, int[] frozenStates) {
         ensureRunningOnConnectivityServiceThread();
+        for (int i = 0; i < uids.length; i++) {
+            final int uid = uids[i];
+            final boolean addReason = frozenStates[i] == UID_FROZEN_STATE_FROZEN;
+            updateDestroySocketReasons(uid, DESTROY_SOCKET_REASON_FROZEN, addReason);
+        }
+
+        if (!mDelayDestroySockets || !isCellNetworkIdle()) {
+            destroyPendingSockets();
+        }
+    }
+
+    private void destroyPendingSockets() {
+        ensureRunningOnConnectivityServiceThread();
+        if (mDestroySocketPendingUids.size() == 0) {
+            return;
+        }
+
+        Set<Integer> uids = new ArraySet<>();
+        for (int i = 0; i < mDestroySocketPendingUids.size(); i++) {
+            uids.add(mDestroySocketPendingUids.keyAt(i));
+        }
 
         try {
-            mDeps.destroyLiveTcpSocketsByOwnerUids(mPendingFrozenUids);
+            mDeps.destroyLiveTcpSocketsByOwnerUids(uids);
         } catch (SocketException | InterruptedIOException | ErrnoException e) {
-            loge("Failed to close pending frozen app sockets: " + e);
+            loge("Failed to destroy sockets: " + e);
         }
-        mPendingFrozenUids.clear();
+        mDestroySocketPendingUids.clear();
     }
 
     private void handleReportNetworkActivity(final NetworkActivityParams params) {
@@ -3401,40 +3414,42 @@
             isCellNetworkActivity = params.label == TRANSPORT_CELLULAR;
         }
 
-        if (mDelayDestroyFrozenSockets
-                && params.isActive
-                && isCellNetworkActivity
-                && !mPendingFrozenUids.isEmpty()) {
-            closePendingFrozenSockets();
+        if (mDelayDestroySockets && params.isActive && isCellNetworkActivity) {
+            destroyPendingSockets();
         }
     }
 
     /**
-     * If the cellular network is no longer the default network, close pending frozen sockets.
+     * If the cellular network is no longer the default network, destroy pending sockets.
      *
      * @param newNetwork new default network
      * @param oldNetwork old default network
      */
-    private void maybeClosePendingFrozenSockets(NetworkAgentInfo newNetwork,
+    private void maybeDestroyPendingSockets(NetworkAgentInfo newNetwork,
             NetworkAgentInfo oldNetwork) {
         final boolean isOldNetworkCellular = oldNetwork != null
                 && oldNetwork.networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
         final boolean isNewNetworkCellular = newNetwork != null
                 && newNetwork.networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
 
-        if (isOldNetworkCellular
-                && !isNewNetworkCellular
-                && !mPendingFrozenUids.isEmpty()) {
-            closePendingFrozenSockets();
+        if (isOldNetworkCellular && !isNewNetworkCellular) {
+            destroyPendingSockets();
         }
     }
 
-    private void dumpCloseFrozenAppSockets(IndentingPrintWriter pw) {
-        pw.println("CloseFrozenAppSockets:");
+    private void dumpDestroySockets(IndentingPrintWriter pw) {
+        pw.println("DestroySockets:");
         pw.increaseIndent();
         pw.print("mDestroyFrozenSockets="); pw.println(mDestroyFrozenSockets);
-        pw.print("mDelayDestroyFrozenSockets="); pw.println(mDelayDestroyFrozenSockets);
-        pw.print("mPendingFrozenUids="); pw.println(mPendingFrozenUids);
+        pw.print("mDelayDestroySockets="); pw.println(mDelayDestroySockets);
+        pw.print("mDestroySocketPendingUids:");
+        pw.increaseIndent();
+        for (int i = 0; i < mDestroySocketPendingUids.size(); i++) {
+            final int uid = mDestroySocketPendingUids.keyAt(i);
+            final int reasons = mDestroySocketPendingUids.valueAt(i);
+            pw.print(uid + ": reasons=" + reasons);
+        }
+        pw.decreaseIndent();
         pw.decreaseIndent();
     }
 
@@ -3460,9 +3475,6 @@
 
     @VisibleForTesting
     static final String KEY_DESTROY_FROZEN_SOCKETS_VERSION = "destroy_frozen_sockets_version";
-    @VisibleForTesting
-    static final String DELAY_DESTROY_FROZEN_SOCKETS_VERSION =
-            "delay_destroy_frozen_sockets_version";
 
     @VisibleForTesting
     public static final String ALLOW_SYSUI_CONNECTIVITY_REPORTS =
@@ -4096,7 +4108,7 @@
         dumpAvoidBadWifiSettings(pw);
 
         pw.println();
-        dumpCloseFrozenAppSockets(pw);
+        dumpDestroySockets(pw);
 
         pw.println();
         dumpBpfProgramStatus(pw);
@@ -5209,7 +5221,7 @@
 
                 if (mDefaultRequest == nri) {
                     mNetworkActivityTracker.updateDefaultNetwork(null /* newNetwork */, nai);
-                    maybeClosePendingFrozenSockets(null /* newNetwork */, nai);
+                    maybeDestroyPendingSockets(null /* newNetwork */, nai);
                     ensureNetworkTransitionWakelock(nai.toShortString());
                 }
             }
@@ -10085,7 +10097,7 @@
             mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
         }
         mNetworkActivityTracker.updateDefaultNetwork(newDefaultNetwork, oldDefaultNetwork);
-        maybeClosePendingFrozenSockets(newDefaultNetwork, oldDefaultNetwork);
+        maybeDestroyPendingSockets(newDefaultNetwork, oldDefaultNetwork);
         mProxyTracker.setDefaultProxy(null != newDefaultNetwork
                 ? newDefaultNetwork.linkProperties.getHttpProxy() : null);
         resetHttpProxyForNonDefaultNetwork(oldDefaultNetwork);
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index aec4f24..634a8fa 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -524,31 +524,6 @@
         }
     }
 
-    private void maybeCleanUp(ParcelFileDescriptor tunFd, ParcelFileDescriptor readSock6,
-            ParcelFileDescriptor writeSock6) {
-        if (tunFd != null) {
-            try {
-                tunFd.close();
-            } catch (IOException e) {
-                Log.e(TAG, "Fail to close tun file descriptor " + e);
-            }
-        }
-        if (readSock6 != null) {
-            try {
-                readSock6.close();
-            } catch (IOException e) {
-                Log.e(TAG, "Fail to close read socket " + e);
-            }
-        }
-        if (writeSock6 != null) {
-            try {
-                writeSock6.close();
-            } catch (IOException e) {
-                Log.e(TAG, "Fail to close write socket " + e);
-            }
-        }
-    }
-
     private void tagSocketAsClat(long cookie) throws IOException {
         if (mCookieTagMap == null) {
             throw new IOException("Cookie tag map is not initialized");
@@ -604,42 +579,6 @@
             throw new IOException("Prefix must be 96 bits long: " + nat64Prefix);
         }
 
-        // [1] Pick an IPv4 address from 192.0.0.4, 192.0.0.5, 192.0.0.6 ..
-        final String v4Str;
-        try {
-            v4Str = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN);
-        } catch (IOException e) {
-            throw new IOException("no IPv4 addresses were available for clat: " + e);
-        }
-
-        final Inet4Address v4;
-        try {
-            v4 = (Inet4Address) InetAddresses.parseNumericAddress(v4Str);
-        } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
-            throw new IOException("Invalid IPv4 address " + v4Str);
-        }
-
-        // [2] Generate a checksum-neutral IID.
-        final Integer fwmark = getFwmark(netId);
-        final String pfx96Str = nat64Prefix.getAddress().getHostAddress();
-        final String v6Str;
-        try {
-            v6Str = mDeps.generateIpv6Address(iface, v4Str, pfx96Str, fwmark);
-        } catch (IOException e) {
-            throw new IOException("no IPv6 addresses were available for clat: " + e);
-        }
-
-        final Inet6Address pfx96 = (Inet6Address) nat64Prefix.getAddress();
-        final Inet6Address v6;
-        try {
-            v6 = (Inet6Address) InetAddresses.parseNumericAddress(v6Str);
-        } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
-            throw new IOException("Invalid IPv6 address " + v6Str);
-        }
-
-        // [3] Open, configure and bring up the tun interface.
-        // Create the v4-... tun interface.
-
         // Initialize all required file descriptors with null pointer. This makes the following
         // error handling easier. Simply always call #maybeCleanUp for closing file descriptors,
         // if any valid ones, in error handling.
@@ -647,145 +586,112 @@
         ParcelFileDescriptor readSock6 = null;
         ParcelFileDescriptor writeSock6 = null;
 
-        final String tunIface = CLAT_PREFIX + iface;
-        try {
-            tunFd = mDeps.adoptFd(mDeps.createTunInterface(tunIface));
-        } catch (IOException e) {
-            throw new IOException("Create tun interface " + tunIface + " failed: " + e);
-        }
+        long cookie = 0;
+        boolean isSocketTagged = false;
 
-        final int tunIfIndex = mDeps.getInterfaceIndex(tunIface);
-        if (tunIfIndex == INVALID_IFINDEX) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("Fail to get interface index for interface " + tunIface);
-        }
+        try {
+            // [1] Pick an IPv4 address from 192.0.0.4, 192.0.0.5, 192.0.0.6 ..
+            final String v4Str = mDeps.selectIpv4Address(INIT_V4ADDR_STRING,
+                    INIT_V4ADDR_PREFIX_LEN);
+            final Inet4Address v4 = (Inet4Address) InetAddresses.parseNumericAddress(v4Str);
 
-        // disable IPv6 on it - failing to do so is not a critical error
-        try {
-            mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */);
-        } catch (RemoteException | ServiceSpecificException e) {
-            Log.e(TAG, "Disable IPv6 on " + tunIface + " failed: " + e);
-        }
+            // [2] Generate a checksum-neutral IID.
+            final Integer fwmark = getFwmark(netId);
+            final String pfx96Str = nat64Prefix.getAddress().getHostAddress();
+            final String v6Str = mDeps.generateIpv6Address(iface, v4Str, pfx96Str, fwmark);
+            final Inet6Address pfx96 = (Inet6Address) nat64Prefix.getAddress();
+            final Inet6Address v6 = (Inet6Address) InetAddresses.parseNumericAddress(v6Str);
 
-        // Detect ipv4 mtu.
-        final int detectedMtu;
-        try {
-            detectedMtu = mDeps.detectMtu(pfx96Str,
-                ByteBuffer.wrap(GOOGLE_DNS_4.getAddress()).getInt(), fwmark);
-        } catch (IOException e) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("Detect MTU on " + tunIface + " failed: " + e);
-        }
-        final int mtu = adjustMtu(detectedMtu);
-        Log.i(TAG, "detected ipv4 mtu of " + detectedMtu + " adjusted to " + mtu);
+            // [3] Open and configure local 464xlat read/write sockets.
+            // Opens a packet socket to receive IPv6 packets in clatd.
 
-        // Config tun interface mtu, address and bring up.
-        try {
-            mNetd.interfaceSetMtu(tunIface, mtu);
-        } catch (RemoteException | ServiceSpecificException e) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("Set MTU " + mtu + " on " + tunIface + " failed: " + e);
-        }
-        final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
-        ifConfig.ifName = tunIface;
-        ifConfig.ipv4Addr = v4Str;
-        ifConfig.prefixLength = 32;
-        ifConfig.hwAddr = "";
-        ifConfig.flags = new String[] {IF_STATE_UP};
-        try {
-            mNetd.interfaceSetCfg(ifConfig);
-        } catch (RemoteException | ServiceSpecificException e) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("Setting IPv4 address to " + ifConfig.ipv4Addr + "/"
-                    + ifConfig.prefixLength + " failed on " + ifConfig.ifName + ": " + e);
-        }
-
-        // [4] Open and configure local 464xlat read/write sockets.
-        // Opens a packet socket to receive IPv6 packets in clatd.
-        try {
             // Use a JNI call to get native file descriptor instead of Os.socket() because we would
             // like to use ParcelFileDescriptor to manage file descriptor. But ctor
             // ParcelFileDescriptor(FileDescriptor fd) is a @hide function. Need to use native file
             // descriptor to initialize ParcelFileDescriptor object instead.
             readSock6 = mDeps.adoptFd(mDeps.openPacketSocket());
-        } catch (IOException e) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("Open packet socket failed: " + e);
-        }
 
-        // Opens a raw socket with a given fwmark to send IPv6 packets in clatd.
-        try {
+            // Opens a raw socket with a given fwmark to send IPv6 packets in clatd.
             // Use a JNI call to get native file descriptor instead of Os.socket(). See above
             // reason why we use jniOpenPacketSocket6().
             writeSock6 = mDeps.adoptFd(mDeps.openRawSocket6(fwmark));
-        } catch (IOException e) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("Open raw socket failed: " + e);
-        }
 
-        final int ifIndex = mDeps.getInterfaceIndex(iface);
-        if (ifIndex == INVALID_IFINDEX) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("Fail to get interface index for interface " + iface);
-        }
+            final int ifIndex = mDeps.getInterfaceIndex(iface);
+            if (ifIndex == INVALID_IFINDEX) {
+                throw new IOException("Fail to get interface index for interface " + iface);
+            }
 
-        // Start translating packets to the new prefix.
-        try {
+            // Start translating packets to the new prefix.
             mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6Str, ifIndex);
-        } catch (IOException e) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("add anycast sockopt failed: " + e);
-        }
-
-        // Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting.
-        final long cookie;
-        try {
+            // Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting.
             cookie = mDeps.getSocketCookie(writeSock6.getFileDescriptor());
             tagSocketAsClat(cookie);
-        } catch (IOException e) {
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("tag raw socket failed: " + e);
-        }
-
-        // Update our packet socket filter to reflect the new 464xlat IP address.
-        try {
+            isSocketTagged = true;
+            // Update our packet socket filter to reflect the new 464xlat IP address.
             mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6Str, ifIndex);
-        } catch (IOException e) {
-            try {
-                untagSocket(cookie);
-            } catch (IOException e2) {
-                Log.e(TAG, "untagSocket cookie " + cookie + " failed: " + e2);
+
+            // [4] Open, configure and bring up the tun interface.
+            // Create the v4-... tun interface.
+            final String tunIface = CLAT_PREFIX + iface;
+            tunFd = mDeps.adoptFd(mDeps.createTunInterface(tunIface));
+            final int tunIfIndex = mDeps.getInterfaceIndex(tunIface);
+            if (tunIfIndex == INVALID_IFINDEX) {
+                throw new IOException("Fail to get interface index for interface " + tunIface);
             }
-            maybeCleanUp(tunFd, readSock6, writeSock6);
-            throw new IOException("configure packet socket failed: " + e);
-        }
+            // disable IPv6 on it - failing to do so is not a critical error
+            mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */);
+            // Detect ipv4 mtu.
+            final int detectedMtu = mDeps.detectMtu(pfx96Str,
+                    ByteBuffer.wrap(GOOGLE_DNS_4.getAddress()).getInt(), fwmark);
+            final int mtu = adjustMtu(detectedMtu);
+            Log.i(TAG, "detected ipv4 mtu of " + detectedMtu + " adjusted to " + mtu);
+            // Config tun interface mtu, address and bring up.
+            mNetd.interfaceSetMtu(tunIface, mtu);
+            final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
+            ifConfig.ifName = tunIface;
+            ifConfig.ipv4Addr = v4Str;
+            ifConfig.prefixLength = 32;
+            ifConfig.hwAddr = "";
+            ifConfig.flags = new String[] {IF_STATE_UP};
+            mNetd.interfaceSetCfg(ifConfig);
 
-        // [5] Start clatd.
-        final int pid;
-        try {
-            pid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(),
-                    writeSock6.getFileDescriptor(), iface, pfx96Str, v4Str, v6Str);
-        } catch (IOException e) {
-            try {
-                untagSocket(cookie);
-            } catch (IOException e2) {
-                Log.e(TAG, "untagSocket cookie " + cookie + " failed: " + e2);
+            // [5] Start clatd.
+            final int pid = mDeps.startClatd(tunFd.getFileDescriptor(),
+                    readSock6.getFileDescriptor(), writeSock6.getFileDescriptor(), iface, pfx96Str,
+                    v4Str, v6Str);
+
+            // [6] Initialize and store clatd tracker object.
+            mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96,
+                    pid, cookie);
+
+            // [7] Start BPF
+            maybeStartBpf(mClatdTracker);
+
+            return v6Str;
+        } catch (IOException | RemoteException | ServiceSpecificException | ClassCastException
+                 | IllegalArgumentException | NullPointerException e) {
+            if (isSocketTagged) {
+                try {
+                    untagSocket(cookie);
+                } catch (IOException e2) {
+                    Log.e(TAG, "untagSocket cookie " + cookie + " failed: " + e2);
+                }
             }
-            throw new IOException("Error start clatd on " + iface + ": " + e);
-        } finally {
-            // The file descriptors have been duplicated (dup2) to clatd in native_startClatd().
-            // Close these file descriptor stubs which are unused anymore.
-            maybeCleanUp(tunFd, readSock6, writeSock6);
+            try {
+                if (tunFd != null) {
+                    tunFd.close();
+                }
+                if (readSock6 != null) {
+                    readSock6.close();
+                }
+                if (writeSock6 != null) {
+                    writeSock6.close();
+                }
+            } catch (IOException e2) {
+                Log.e(TAG, "Fail to cleanup fd ", e);
+            }
+            throw new IOException("Failed to start clat ", e);
         }
-
-        // [6] Initialize and store clatd tracker object.
-        mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96,
-                pid, cookie);
-
-        // [7] Start BPF
-        maybeStartBpf(mClatdTracker);
-
-        return v6Str;
     }
 
     private void maybeStopBpf(final ClatdTracker tracker) {
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index 176307d..7ea7f95 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -44,6 +44,8 @@
 
     public static final String BACKGROUND_FIREWALL_CHAIN = "background_firewall_chain";
 
+    public static final String DELAY_DESTROY_SOCKETS = "delay_destroy_sockets";
+
     private boolean mNoRematchAllRequestsOnRegister;
 
     /**
diff --git a/service/src/com/android/server/connectivity/DnsManager.java b/service/src/com/android/server/connectivity/DnsManager.java
index 8e6854a..ac02229 100644
--- a/service/src/com/android/server/connectivity/DnsManager.java
+++ b/service/src/com/android/server/connectivity/DnsManager.java
@@ -390,6 +390,7 @@
                 : new String[0];            // Off
         paramsParcel.transportTypes = nc.getTransportTypes();
         paramsParcel.meteredNetwork = nc.isMetered();
+        paramsParcel.interfaceNames = lp.getAllInterfaceNames().toArray(new String[0]);
         // Prepare to track the validation status of the DNS servers in the
         // resolver config when private DNS is in opportunistic or strict mode.
         if (useTls) {
@@ -403,13 +404,14 @@
         }
 
         Log.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
-                + "%d, %d, %s, %s, %s, %b)", paramsParcel.netId,
+                + "%d, %d, %s, %s, %s, %b, %s)", paramsParcel.netId,
                 Arrays.toString(paramsParcel.servers), Arrays.toString(paramsParcel.domains),
                 paramsParcel.sampleValiditySeconds, paramsParcel.successThreshold,
                 paramsParcel.minSamples, paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec,
                 paramsParcel.retryCount, paramsParcel.tlsName,
                 Arrays.toString(paramsParcel.tlsServers),
-                Arrays.toString(paramsParcel.transportTypes), paramsParcel.meteredNetwork));
+                Arrays.toString(paramsParcel.transportTypes), paramsParcel.meteredNetwork,
+                Arrays.toString(paramsParcel.interfaceNames)));
 
         try {
             mDnsResolver.setResolverConfiguration(paramsParcel);
diff --git a/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java b/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java
index ac479b8..af4aee5 100644
--- a/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java
+++ b/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java
@@ -168,14 +168,18 @@
     public void applyMulticastRoutingConfig(
             final String iifName, final String oifName, final MulticastRoutingConfig newConfig) {
         checkOnHandlerThread();
+        Objects.requireNonNull(iifName, "IifName can't be null");
+        Objects.requireNonNull(oifName, "OifName can't be null");
 
         if (newConfig.getForwardingMode() != FORWARD_NONE) {
             // Make sure iif and oif are added as multicast forwarding interfaces
-            try {
-                maybeAddAndTrackInterface(iifName);
-                maybeAddAndTrackInterface(oifName);
-            } catch (IllegalStateException e) {
-                Log.e(TAG, "Failed to apply multicast routing config, ", e);
+            if (!maybeAddAndTrackInterface(iifName) || !maybeAddAndTrackInterface(oifName)) {
+                Log.e(
+                        TAG,
+                        "Failed to apply multicast routing config from "
+                                + iifName
+                                + " to "
+                                + oifName);
                 return;
             }
         }
@@ -258,9 +262,14 @@
         }
     }
 
+    /**
+     * Returns the next available virtual index for multicast routing, or -1 if the number of
+     * virtual interfaces has reached max value.
+     */
     private int getNextAvailableVirtualIndex() {
         if (mVirtualInterfaces.size() >= MAX_NUM_OF_MULTICAST_VIRTUAL_INTERFACES) {
-            throw new IllegalStateException("Can't allocate new multicast virtual interface");
+            Log.e(TAG, "Can't allocate new multicast virtual interface");
+            return -1;
         }
         for (int i = 0; i < mVirtualInterfaces.size(); i++) {
             if (!mVirtualInterfaces.contains(i)) {
@@ -291,12 +300,23 @@
         return mVirtualInterfaces.get(virtualIndex);
     }
 
-    private void maybeAddAndTrackInterface(String ifName) {
+    /**
+     * Returns {@code true} if the interfaces is added and tracked, or {@code false} when failed
+     * to add the interface.
+     */
+    private boolean maybeAddAndTrackInterface(String ifName) {
         checkOnHandlerThread();
-        if (getIndexForValue(mVirtualInterfaces, ifName) >= 0) return;
+        if (getIndexForValue(mVirtualInterfaces, ifName) >= 0) return true;
 
         int nextVirtualIndex = getNextAvailableVirtualIndex();
+        if (nextVirtualIndex < 0) {
+            return false;
+        }
         int ifIndex = mDependencies.getInterfaceIndex(ifName);
+        if (ifIndex == 0) {
+            Log.e(TAG, "Can't get interface index for " + ifName);
+            return false;
+        }
         final StructMif6ctl mif6ctl =
                     new StructMif6ctl(
                             nextVirtualIndex,
@@ -309,10 +329,11 @@
             Log.d(TAG, "Added mifi " + nextVirtualIndex + " to MIF");
         } catch (ErrnoException e) {
             Log.e(TAG, "failed to add multicast virtual interface", e);
-            return;
+            return false;
         }
         mVirtualInterfaces.put(nextVirtualIndex, ifName);
         mInterfaces.put(ifIndex, ifName);
+        return true;
     }
 
     @VisibleForTesting
@@ -798,13 +819,12 @@
             NetworkUtils.setsockoptBytes(fd, IPPROTO_IPV6, MRT6_DEL_MFC, bytes);
         }
 
-        public Integer getInterfaceIndex(String ifName) {
-            try {
-                NetworkInterface ni = NetworkInterface.getByName(ifName);
-                return ni.getIndex();
-            } catch (NullPointerException | SocketException e) {
-                return null;
-            }
+        /**
+         * Returns the interface index for an interface name, or 0 if the interface index could
+         * not be found.
+         */
+        public int getInterfaceIndex(String ifName) {
+            return Os.if_nametoindex(ifName);
         }
 
         public NetworkInterface getNetworkInterface(int physicalIndex) {
diff --git a/service/src/com/android/server/connectivity/Nat464Xlat.java b/service/src/com/android/server/connectivity/Nat464Xlat.java
index 065922d..489dab5 100644
--- a/service/src/com/android/server/connectivity/Nat464Xlat.java
+++ b/service/src/com/android/server/connectivity/Nat464Xlat.java
@@ -202,13 +202,13 @@
             try {
                 addrStr = mClatCoordinator.clatStart(baseIface, getNetId(), mNat64PrefixInUse);
             } catch (IOException e) {
-                Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+                Log.e(TAG, "Error starting clatd on " + baseIface, e);
             }
         } else {
             try {
                 addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
             } catch (RemoteException | ServiceSpecificException e) {
-                Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+                Log.e(TAG, "Error starting clatd on " + baseIface, e);
             }
         }
         mIface = CLAT_PREFIX + baseIface;
@@ -217,7 +217,7 @@
         try {
             mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr);
         } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
-            Log.e(TAG, "Invalid IPv6 address " + addrStr);
+            Log.e(TAG, "Invalid IPv6 address " + addrStr , e);
         }
         if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
             stopPrefixDiscovery();
diff --git a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
index 92ec0c4..df7010e 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
@@ -41,6 +41,10 @@
     public static final short IFLA_ADDRESS   = 1;
     public static final short IFLA_IFNAME    = 3;
     public static final short IFLA_MTU       = 4;
+    public static final short IFLA_INET6_ADDR_GEN_MODE = 8;
+    public static final short IFLA_AF_SPEC = 26;
+
+    public static final short IN6_ADDR_GEN_MODE_NONE = 1;
 
     private int mMtu;
     @NonNull
diff --git a/staticlibs/device/com/android/net/module/util/netlink/StructIfinfoMsg.java b/staticlibs/device/com/android/net/module/util/netlink/StructIfinfoMsg.java
index 02d1574..28eeaea 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/StructIfinfoMsg.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/StructIfinfoMsg.java
@@ -49,7 +49,7 @@
     @Field(order = 4, type = Type.U32)
     public final long change;
 
-    StructIfinfoMsg(short family, int type, int index, long flags, long change) {
+    public StructIfinfoMsg(short family, int type, int index, long flags, long change) {
         this.family = family;
         this.type = type;
         this.index = index;
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
index 4185b05..d5e91c2 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
@@ -24,6 +24,7 @@
  * A JUnit Rule that sets feature flags based on `@FeatureFlag` annotations.
  *
  * This rule enables dynamic control of feature flag states during testing.
+ * And restores the original values after performing tests.
  *
  * **Usage:**
  * ```kotlin
@@ -31,6 +32,8 @@
  *   @get:Rule
  *   val setFeatureFlagsRule = SetFeatureFlagsRule(setFlagsMethod = (name, enabled) -> {
  *     // Custom handling code.
+ *   }, (name) -> {
+ *     // Custom getter code to retrieve the original values.
  *   })
  *
  *   // ... test methods with @FeatureFlag annotations
@@ -41,7 +44,10 @@
  * }
  * ```
  */
-class SetFeatureFlagsRule(val setFlagsMethod: (name: String, enabled: Boolean) -> Unit) : TestRule {
+class SetFeatureFlagsRule(
+    val setFlagsMethod: (name: String, enabled: Boolean?) -> Unit,
+                          val getFlagsMethod: (name: String) -> Boolean?
+) : TestRule {
     /**
      * This annotation marks a test method as requiring a specific feature flag to be configured.
      *
@@ -69,13 +75,20 @@
                     FeatureFlag::class.java
                 )
 
+                val valuesToBeRestored = mutableMapOf<String, Boolean?>()
                 for (featureFlagAnnotation in featureFlagAnnotations) {
+                    valuesToBeRestored[featureFlagAnnotation.name] =
+                            getFlagsMethod(featureFlagAnnotation.name)
                     setFlagsMethod(featureFlagAnnotation.name, featureFlagAnnotation.enabled)
                 }
 
                 // Execute the test method, which includes methods annotated with
                 // @Before, @Test and @After.
                 base.evaluate()
+
+                valuesToBeRestored.forEach {
+                    setFlagsMethod(it.key, it.value)
+                }
             }
         }
     }
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index b6cb09b..2718961 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -163,7 +163,6 @@
 import static android.telephony.DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
 
 import static com.android.server.ConnectivityService.ALLOW_SATALLITE_NETWORK_FALLBACK;
-import static com.android.server.ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION;
 import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
 import static com.android.server.ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS;
 import static com.android.server.ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION;
@@ -178,6 +177,7 @@
 import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister;
 import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister;
 import static com.android.server.connectivity.ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN;
+import static com.android.server.connectivity.ConnectivityFlags.DELAY_DESTROY_SOCKETS;
 import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
 import static com.android.testutils.Cleanup.testAndCleanup;
 import static com.android.testutils.ConcurrentUtils.await;
@@ -2169,8 +2169,6 @@
                     return true;
                 case KEY_DESTROY_FROZEN_SOCKETS_VERSION:
                     return true;
-                case DELAY_DESTROY_FROZEN_SOCKETS_VERSION:
-                    return true;
                 default:
                     return super.isFeatureEnabled(context, name);
             }
@@ -2187,6 +2185,8 @@
                     return true;
                 case BACKGROUND_FIREWALL_CHAIN:
                     return true;
+                case DELAY_DESTROY_SOCKETS:
+                    return true;
                 default:
                     return super.isFeatureNotChickenedOut(context, name);
             }
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index da7fda3..72dde7f 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -418,21 +419,6 @@
         inOrder.verify(mDeps).generateIpv6Address(eq(BASE_IFACE),
                 eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(NAT64_PREFIX_STRING), eq(MARK));
 
-        // Open, configure and bring up the tun interface.
-        inOrder.verify(mDeps).createTunInterface(eq(STACKED_IFACE));
-        inOrder.verify(mDeps).adoptFd(eq(TUN_FD));
-        inOrder.verify(mDeps).getInterfaceIndex(eq(STACKED_IFACE));
-        inOrder.verify(mNetd).interfaceSetEnableIPv6(eq(STACKED_IFACE), eq(false /* enable */));
-        inOrder.verify(mDeps).detectMtu(eq(NAT64_PREFIX_STRING), eq(GOOGLE_DNS_4), eq(MARK));
-        inOrder.verify(mNetd).interfaceSetMtu(eq(STACKED_IFACE),
-                eq(1472 /* ETHER_MTU(1500) - MTU_DELTA(28) */));
-        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
-                STACKED_IFACE.equals(cfg.ifName)
-                && XLAT_LOCAL_IPV4ADDR_STRING.equals(cfg.ipv4Addr)
-                && (32 == cfg.prefixLength)
-                && "".equals(cfg.hwAddr)
-                && assertContainsFlag(cfg.flags, IF_STATE_UP)));
-
         // Open and configure 464xlat read/write sockets.
         inOrder.verify(mDeps).openPacketSocket();
         inOrder.verify(mDeps).adoptFd(eq(PACKET_SOCK_FD));
@@ -449,6 +435,21 @@
                 argThat(fd -> Objects.equals(PACKET_SOCK_PFD.getFileDescriptor(), fd)),
                 eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(BASE_IFINDEX));
 
+        // Open, configure and bring up the tun interface.
+        inOrder.verify(mDeps).createTunInterface(eq(STACKED_IFACE));
+        inOrder.verify(mDeps).adoptFd(eq(TUN_FD));
+        inOrder.verify(mDeps).getInterfaceIndex(eq(STACKED_IFACE));
+        inOrder.verify(mNetd).interfaceSetEnableIPv6(eq(STACKED_IFACE), eq(false /* enable */));
+        inOrder.verify(mDeps).detectMtu(eq(NAT64_PREFIX_STRING), eq(GOOGLE_DNS_4), eq(MARK));
+        inOrder.verify(mNetd).interfaceSetMtu(eq(STACKED_IFACE),
+                eq(1472 /* ETHER_MTU(1500) - MTU_DELTA(28) */));
+        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
+                STACKED_IFACE.equals(cfg.ifName)
+                        && XLAT_LOCAL_IPV4ADDR_STRING.equals(cfg.ipv4Addr)
+                        && (32 == cfg.prefixLength)
+                        && "".equals(cfg.hwAddr)
+                        && assertContainsFlag(cfg.flags, IF_STATE_UP)));
+
         // Start clatd.
         inOrder.verify(mDeps).startClatd(
                 argThat(fd -> Objects.equals(TUN_PFD.getFileDescriptor(), fd)),
@@ -630,9 +631,13 @@
             public int createTunInterface(@NonNull String tuniface) throws IOException {
                 throw new IOException();
             }
+            @Override
+            public IBpfMap<CookieTagMapKey, CookieTagMapValue> getBpfCookieTagMap() {
+                return  mock(IBpfMap.class);
+            }
         }
         checkNotStartClat(new FailureDependencies(), false /* needToCloseTunFd */,
-                false /* needToClosePacketSockFd */, false /* needToCloseRawSockFd */);
+                true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
     }
 
     @Test
@@ -643,9 +648,14 @@
                     throws IOException {
                 throw new IOException();
             }
+
+            @Override
+            public IBpfMap<CookieTagMapKey, CookieTagMapValue> getBpfCookieTagMap() {
+                return  mock(IBpfMap.class);
+            }
         }
         checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
-                false /* needToClosePacketSockFd */, false /* needToCloseRawSockFd */);
+                true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
     }
 
     @Test
@@ -656,7 +666,7 @@
                 throw new IOException();
             }
         }
-        checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+        checkNotStartClat(new FailureDependencies(), false /* needToCloseTunFd */,
                 false /* needToClosePacketSockFd */, false /* needToCloseRawSockFd */);
     }
 
@@ -668,7 +678,7 @@
                 throw new IOException();
             }
         }
-        checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+        checkNotStartClat(new FailureDependencies(), false /* needToCloseTunFd */,
                 true /* needToClosePacketSockFd */, false /* needToCloseRawSockFd */);
     }
 
@@ -681,7 +691,7 @@
                 throw new IOException();
             }
         }
-        checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+        checkNotStartClat(new FailureDependencies(), false /* needToCloseTunFd */,
                 true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
     }
 
@@ -694,7 +704,7 @@
                 throw new IOException();
             }
         }
-        checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+        checkNotStartClat(new FailureDependencies(), false /* needToCloseTunFd */,
                 true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
     }
 
@@ -721,7 +731,7 @@
                 throw new IOException();
             }
         }
-        checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+        checkNotStartClat(new FailureDependencies(), false /* needToCloseTunFd */,
                 true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
     }
 
@@ -733,7 +743,7 @@
                 return null;
             }
         }
-        checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+        checkNotStartClat(new FailureDependencies(), false /* needToCloseTunFd */,
                 true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
     }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index 44512bb..ea3d2dd 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -29,8 +29,6 @@
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
 
-import static com.android.testutils.MiscAsserts.assertContainsExactly;
-import static com.android.testutils.MiscAsserts.assertContainsStringsExactly;
 import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
 
 import static org.junit.Assert.assertEquals;
@@ -38,12 +36,12 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.ConnectivitySettingsManager;
@@ -74,7 +72,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -122,29 +119,6 @@
         assertFieldCountEquals(3, ResolverOptionsParcel.class);
     }
 
-    private void assertResolverParamsEquals(@NonNull ResolverParamsParcel actual,
-            @NonNull ResolverParamsParcel expected) {
-        assertEquals(actual.netId, expected.netId);
-        assertEquals(actual.sampleValiditySeconds, expected.sampleValiditySeconds);
-        assertEquals(actual.successThreshold, expected.successThreshold);
-        assertEquals(actual.minSamples, expected.minSamples);
-        assertEquals(actual.maxSamples, expected.maxSamples);
-        assertEquals(actual.baseTimeoutMsec, expected.baseTimeoutMsec);
-        assertEquals(actual.retryCount, expected.retryCount);
-        assertContainsStringsExactly(actual.servers, expected.servers);
-        assertContainsStringsExactly(actual.domains, expected.domains);
-        assertEquals(actual.tlsName, expected.tlsName);
-        assertContainsStringsExactly(actual.tlsServers, expected.tlsServers);
-        assertContainsStringsExactly(actual.tlsFingerprints, expected.tlsFingerprints);
-        assertEquals(actual.caCertificate, expected.caCertificate);
-        assertEquals(actual.tlsConnectTimeoutMs, expected.tlsConnectTimeoutMs);
-        assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions);
-        assertContainsExactly(actual.transportTypes, expected.transportTypes);
-        assertEquals(actual.meteredNetwork, expected.meteredNetwork);
-        assertEquals(actual.dohParams, expected.dohParams);
-        assertFieldCountEquals(18, ResolverParamsParcel.class);
-    }
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -365,11 +339,6 @@
         mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
         mDnsManager.flushVmDnsCache();
 
-        final ArgumentCaptor<ResolverParamsParcel> resolverParamsParcelCaptor =
-                ArgumentCaptor.forClass(ResolverParamsParcel.class);
-        verify(mMockDnsResolver, times(1)).setResolverConfiguration(
-                resolverParamsParcelCaptor.capture());
-        final ResolverParamsParcel actualParams = resolverParamsParcelCaptor.getValue();
         final ResolverParamsParcel expectedParams = new ResolverParamsParcel();
         expectedParams.netId = TEST_NETID;
         expectedParams.sampleValiditySeconds = TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS;
@@ -384,7 +353,8 @@
         expectedParams.resolverOptions = null;
         expectedParams.meteredNetwork = true;
         expectedParams.dohParams = null;
-        assertResolverParamsEquals(actualParams, expectedParams);
+        expectedParams.interfaceNames = new String[]{TEST_IFACENAME};
+        verify(mMockDnsResolver, times(1)).setResolverConfiguration(eq(expectedParams));
     }
 
     @Test
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 3b06ad0..63ef86e 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -157,11 +157,11 @@
         it[ConnectivityFlags.NO_REMATCH_ALL_REQUESTS_ON_REGISTER] = true
         it[ConnectivityFlags.REQUEST_RESTRICTED_WIFI] = true
         it[ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION] = true
-        it[ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION] = true
         it[ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS] = true
         it[ConnectivityService.ALLOW_SATALLITE_NETWORK_FALLBACK] = true
         it[ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING] = true
         it[ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN] = true
+        it[ConnectivityFlags.DELAY_DESTROY_SOCKETS] = true
     }
     fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)
 
@@ -171,7 +171,11 @@
     val contentResolver = makeMockContentResolver(context)
 
     val PRIMARY_USER = 0
-    val PRIMARY_USER_INFO = UserInfo(PRIMARY_USER, "" /* name */, UserInfo.FLAG_PRIMARY)
+    val PRIMARY_USER_INFO = UserInfo(
+            PRIMARY_USER,
+            "", // name
+            UserInfo.FLAG_PRIMARY
+    )
     val PRIMARY_USER_HANDLE = UserHandle(PRIMARY_USER)
     val userManager = makeMockUserManager(PRIMARY_USER_INFO, PRIMARY_USER_HANDLE)
     val activityManager = makeActivityManager()
@@ -186,7 +190,11 @@
     val bpfNetMaps = mock<BpfNetMaps>()
     val clatCoordinator = mock<ClatCoordinator>()
     val networkRequestStateStatsMetrics = mock<NetworkRequestStateStatsMetrics>()
-    val proxyTracker = ProxyTracker(context, mock<Handler>(), 16 /* EVENT_PROXY_HAS_CHANGED */)
+    val proxyTracker = ProxyTracker(
+            context,
+            mock<Handler>(),
+            16 // EVENT_PROXY_HAS_CHANGED
+    )
     val systemConfigManager = makeMockSystemConfigManager()
     val batteryStats = mock<IBatteryStats>()
     val batteryManager = BatteryStatsManager(batteryStats)
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 6425daa..9026481 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -314,7 +314,7 @@
             new SetFeatureFlagsRule((name, enabled) -> {
                 mFeatureFlags.put(name, enabled);
                 return null;
-            });
+            }, (name) -> mFeatureFlags.getOrDefault(name, false));
 
     private class MockContext extends BroadcastInterceptingContext {
         private final Context mBaseContext;
diff --git a/thread/service/java/com/android/server/thread/TunInterfaceController.java b/thread/service/java/com/android/server/thread/TunInterfaceController.java
index c3f6ace..976f93d 100644
--- a/thread/service/java/com/android/server/thread/TunInterfaceController.java
+++ b/thread/service/java/com/android/server/thread/TunInterfaceController.java
@@ -16,30 +16,40 @@
 
 package com.android.server.thread;
 
+import static android.system.OsConstants.AF_INET6;
 import static android.system.OsConstants.EADDRINUSE;
+import static android.system.OsConstants.IFF_MULTICAST;
+import static android.system.OsConstants.IFF_NOARP;
+import static android.system.OsConstants.NETLINK_ROUTE;
+
+import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWLINK;
+import static com.android.net.module.util.netlink.RtNetlinkLinkMessage.IFLA_AF_SPEC;
+import static com.android.net.module.util.netlink.RtNetlinkLinkMessage.IFLA_INET6_ADDR_GEN_MODE;
+import static com.android.net.module.util.netlink.RtNetlinkLinkMessage.IN6_ADDR_GEN_MODE_NONE;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
 
 import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
-import android.net.util.SocketUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.OsConstants;
 import android.util.Log;
 
+import com.android.net.module.util.HexDump;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
 import com.android.net.module.util.netlink.NetlinkUtils;
-import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
+import com.android.net.module.util.netlink.StructIfinfoMsg;
+import com.android.net.module.util.netlink.StructNlAttr;
+import com.android.net.module.util.netlink.StructNlMsgHdr;
 import com.android.server.thread.openthread.Ipv6AddressInfo;
 import com.android.server.thread.openthread.OnMeshPrefixConfig;
 
-import java.io.FileDescriptor;
 import java.io.IOException;
-import java.io.InterruptedIOException;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -47,12 +57,15 @@
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.List;
 
 /** Controller for virtual/tunnel network interfaces. */
 public class TunInterfaceController {
     private static final String TAG = "TunIfController";
+    private static final boolean DBG = false;
     private static final long INFINITE_LIFETIME = 0xffffffffL;
     static final int MTU = 1280;
 
@@ -62,14 +75,13 @@
 
     private final String mIfName;
     private final LinkProperties mLinkProperties = new LinkProperties();
-    private ParcelFileDescriptor mParcelTunFd;
-    private FileDescriptor mNetlinkSocket;
-    private static int sNetlinkSeqNo = 0;
     private final MulticastSocket mMulticastSocket; // For join group and leave group
-    private NetworkInterface mNetworkInterface;
     private final List<InetAddress> mMulticastAddresses = new ArrayList<>();
     private final List<RouteInfo> mNetDataPrefixes = new ArrayList<>();
 
+    private ParcelFileDescriptor mParcelTunFd;
+    private NetworkInterface mNetworkInterface;
+
     /** Creates a new {@link TunInterfaceController} instance for given interface. */
     public TunInterfaceController(String interfaceName) {
         mIfName = interfaceName;
@@ -91,26 +103,21 @@
     public void createTunInterface() throws IOException {
         mParcelTunFd = ParcelFileDescriptor.adoptFd(nativeCreateTunInterface(mIfName, MTU));
         try {
-            mNetlinkSocket = NetlinkUtils.netlinkSocketForProto(OsConstants.NETLINK_ROUTE);
-        } catch (ErrnoException e) {
-            throw new IOException("Failed to create netlink socket", e);
-        }
-        try {
             mNetworkInterface = NetworkInterface.getByName(mIfName);
         } catch (SocketException e) {
             throw new IOException("Failed to get NetworkInterface", e);
         }
+
+        setAddrGenModeToNone();
     }
 
     public void destroyTunInterface() {
         try {
             mParcelTunFd.close();
-            SocketUtils.closeSocket(mNetlinkSocket);
         } catch (IOException e) {
             // Should never fail
         }
         mParcelTunFd = null;
-        mNetlinkSocket = null;
         mNetworkInterface = null;
     }
 
@@ -128,6 +135,10 @@
             for (LinkAddress address : mLinkProperties.getAllLinkAddresses()) {
                 removeAddress(address);
             }
+            for (RouteInfo route : mLinkProperties.getAllRoutes()) {
+                mLinkProperties.removeRoute(route);
+            }
+            mNetDataPrefixes.clear();
         }
         nativeSetInterfaceUp(mIfName, isUp);
     }
@@ -138,14 +149,14 @@
     public void addAddress(LinkAddress address) {
         Log.d(TAG, "Adding address " + address + " with flags: " + address.getFlags());
 
-        long validLifetimeSeconds;
         long preferredLifetimeSeconds;
+        long validLifetimeSeconds;
 
         if (address.getDeprecationTime() == LinkAddress.LIFETIME_PERMANENT
                 || address.getDeprecationTime() == LinkAddress.LIFETIME_UNKNOWN) {
-            validLifetimeSeconds = INFINITE_LIFETIME;
+            preferredLifetimeSeconds = INFINITE_LIFETIME;
         } else {
-            validLifetimeSeconds =
+            preferredLifetimeSeconds =
                     Math.max(
                             (address.getDeprecationTime() - SystemClock.elapsedRealtime()) / 1000L,
                             0L);
@@ -153,28 +164,23 @@
 
         if (address.getExpirationTime() == LinkAddress.LIFETIME_PERMANENT
                 || address.getExpirationTime() == LinkAddress.LIFETIME_UNKNOWN) {
-            preferredLifetimeSeconds = INFINITE_LIFETIME;
+            validLifetimeSeconds = INFINITE_LIFETIME;
         } else {
-            preferredLifetimeSeconds =
+            validLifetimeSeconds =
                     Math.max(
                             (address.getExpirationTime() - SystemClock.elapsedRealtime()) / 1000L,
                             0L);
         }
 
-        byte[] message =
-                RtNetlinkAddressMessage.newRtmNewAddressMessage(
-                        sNetlinkSeqNo++,
-                        address.getAddress(),
-                        (short) address.getPrefixLength(),
-                        address.getFlags(),
-                        (byte) address.getScope(),
-                        Os.if_nametoindex(mIfName),
-                        validLifetimeSeconds,
-                        preferredLifetimeSeconds);
-        try {
-            Os.write(mNetlinkSocket, message, 0, message.length);
-        } catch (ErrnoException | InterruptedIOException e) {
-            Log.e(TAG, "Failed to add address " + address, e);
+        if (!NetlinkUtils.sendRtmNewAddressRequest(
+                Os.if_nametoindex(mIfName),
+                address.getAddress(),
+                (short) address.getPrefixLength(),
+                address.getFlags(),
+                (byte) address.getScope(),
+                preferredLifetimeSeconds,
+                validLifetimeSeconds)) {
+            Log.w(TAG, "Failed to add address " + address.getAddress().getHostAddress());
             return;
         }
         mLinkProperties.addLinkAddress(address);
@@ -184,22 +190,17 @@
     /** Removes an address from the interface. */
     public void removeAddress(LinkAddress address) {
         Log.d(TAG, "Removing address " + address);
-        byte[] message =
-                RtNetlinkAddressMessage.newRtmDelAddressMessage(
-                        sNetlinkSeqNo++,
-                        address.getAddress(),
-                        (short) address.getPrefixLength(),
-                        Os.if_nametoindex(mIfName));
 
         // Intentionally update the mLinkProperties before send netlink message because the
         // address is already removed from ot-daemon and apps can't reach to the address even
         // when the netlink request below fails
         mLinkProperties.removeLinkAddress(address);
         mLinkProperties.removeRoute(getRouteForAddress(address));
-        try {
-            Os.write(mNetlinkSocket, message, 0, message.length);
-        } catch (ErrnoException | InterruptedIOException e) {
-            Log.e(TAG, "Failed to remove address " + address, e);
+        if (!NetlinkUtils.sendRtmDelAddressRequest(
+                Os.if_nametoindex(mIfName),
+                (Inet6Address) address.getAddress(),
+                (short) address.getPrefixLength())) {
+            Log.w(TAG, "Failed to remove address " + address.getAddress().getHostAddress());
         }
     }
 
@@ -362,4 +363,66 @@
             Log.e(TAG, "failed to leave group " + address.getHostAddress(), e);
         }
     }
+
+    /**
+     * Sets the address generation mode to {@code IN6_ADDR_GEN_MODE_NONE}.
+     *
+     * <p>So that the "thread-wpan" interface has only one IPv6 link local address which is
+     * generated by OpenThread.
+     */
+    private void setAddrGenModeToNone() {
+        StructNlMsgHdr header = new StructNlMsgHdr();
+        header.nlmsg_type = RTM_NEWLINK;
+        header.nlmsg_pid = 0;
+        header.nlmsg_seq = 0;
+        header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+        StructIfinfoMsg ifInfo =
+                new StructIfinfoMsg(
+                        (short) 0 /* family */,
+                        0 /* type */,
+                        Os.if_nametoindex(mIfName),
+                        (IFF_MULTICAST | IFF_NOARP) /* flags */,
+                        0xffffffff /* change */);
+
+        // Nested attributes
+        // IFLA_AF_SPEC
+        //   AF_INET6
+        //     IFLA_INET6_ADDR_GEN_MODE
+        StructNlAttr addrGenMode =
+                new StructNlAttr(IFLA_INET6_ADDR_GEN_MODE, (byte) IN6_ADDR_GEN_MODE_NONE);
+        StructNlAttr afInet6 = new StructNlAttr((short) AF_INET6, addrGenMode);
+        StructNlAttr afSpec = new StructNlAttr(IFLA_AF_SPEC, afInet6);
+
+        final int msgLength =
+                StructNlMsgHdr.STRUCT_SIZE
+                        + StructIfinfoMsg.STRUCT_SIZE
+                        + afSpec.getAlignedLength();
+        byte[] msg = new byte[msgLength];
+        ByteBuffer buf = ByteBuffer.wrap(msg);
+        buf.order(ByteOrder.nativeOrder());
+
+        header.nlmsg_len = msgLength;
+        header.pack(buf);
+        ifInfo.pack(buf);
+        afSpec.pack(buf);
+
+        if (buf.position() != msgLength) {
+            throw new AssertionError(
+                    String.format(
+                            "Unexpected netlink message size (actual = %d, expected = %d)",
+                            buf.position(), msgLength));
+        }
+
+        if (DBG) {
+            Log.d(TAG, "ADDR_GEN_MODE message is:");
+            Log.d(TAG, HexDump.dumpHexString(msg));
+        }
+
+        try {
+            NetlinkUtils.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed to set ADDR_GEN_MODE to NONE", e);
+        }
+    }
 }
diff --git a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
index 4028014..61b6eac 100644
--- a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
+++ b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
@@ -176,7 +176,7 @@
 
     // TODO (b/323300829): add test for removing an OT address
     @Test
-    public void tunInterface_joinedNetwork_otAddressesAddedToTunInterface() throws Exception {
+    public void tunInterface_joinedNetwork_otAndTunAddressesMatch() throws Exception {
         mController.joinAndWait(DEFAULT_DATASET);
 
         List<Inet6Address> otAddresses = mOtCtl.getAddresses();
@@ -185,9 +185,12 @@
         // that we can write assertThat() in the Predicate
         waitFor(
                 () -> {
-                    String ifconfig = runShellCommand("ifconfig thread-wpan");
-                    return otAddresses.stream()
-                            .allMatch(addr -> ifconfig.contains(addr.getHostAddress()));
+                    List<Inet6Address> tunAddresses =
+                            getIpv6LinkAddresses("thread-wpan").stream()
+                                    .map(linkAddr -> (Inet6Address) linkAddr.getAddress())
+                                    .toList();
+                    return otAddresses.containsAll(tunAddresses)
+                            && tunAddresses.containsAll(otAddresses);
                 },
                 TUN_ADDR_UPDATE_TIMEOUT);
     }
@@ -307,6 +310,23 @@
                 .isFalse();
     }
 
+    @Test
+    public void toggleThreadNetwork_routeFromPreviousNetDataIsRemoved() throws Exception {
+        ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+        mController.joinAndWait(DEFAULT_DATASET);
+        mOtCtl.executeCommand("prefix add " + TEST_NO_SLAAC_PREFIX + " pros med");
+        mOtCtl.executeCommand("netdata register");
+
+        mController.leaveAndWait();
+        mOtCtl.factoryReset();
+        mController.joinAndWait(DEFAULT_DATASET);
+
+        LinkProperties lp = cm.getLinkProperties(getThreadNetwork(CALLBACK_TIMEOUT));
+        assertThat(lp).isNotNull();
+        assertThat(lp.getRoutes().stream().anyMatch(r -> r.matches(TEST_NO_SLAAC_PREFIX_ADDRESS)))
+                .isFalse();
+    }
+
     // TODO (b/323300829): add more tests for integration with linux platform and
     // ConnectivityService
 
diff --git a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
index 78f5770..ada46c8 100644
--- a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
+++ b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
@@ -301,7 +301,7 @@
         return false;
     }
 
-    public static List<LinkAddress> getIpv6LinkAddresses(String interfaceName) throws IOException {
+    public static List<LinkAddress> getIpv6LinkAddresses(String interfaceName) {
         List<LinkAddress> addresses = new ArrayList<>();
         final String cmd = " ip -6 addr show dev " + interfaceName;
         final String output = runShellCommandOrThrow(cmd);
diff --git a/tools/aospify_device.sh b/tools/aospify_device.sh
index f25ac9d..0176093 100755
--- a/tools/aospify_device.sh
+++ b/tools/aospify_device.sh
@@ -3,10 +3,14 @@
 # Script to swap core networking modules in a GMS userdebug device to AOSP modules, by remounting
 # the system partition and replacing module prebuilts. This is only to be used for local testing,
 # and should only be used on userdebug devices that support "adb root" and remounting the system
-# partition using overlayfs.
+# partition using overlayfs. The setup wizard should be cleared before running the script.
 #
 # Usage: aospify_device.sh [device_serial]
-# Reset by wiping data (adb reboot bootloader && fastboot erase userdata && fastboot reboot).
+#
+# Reset with "adb enable-verity", then wiping data (from Settings, or:
+# "adb reboot bootloader && fastboot erase userdata && fastboot reboot").
+# Some devices output errors like "Overlayfs teardown failed" on "enable-verity" but it still works
+# (/mnt/scratch should be deleted).
 #
 # This applies to NetworkStack, CaptivePortalLogin, dnsresolver, tethering, cellbroadcast modules,
 # which generally need to be preloaded together (core networking modules + cellbroadcast which
@@ -37,6 +41,10 @@
     else
         rm -f /tmp/decompressed_$aosp_apex_name.apex
         $ANDROID_HOST_OUT/bin/deapexer decompress --input $ANDROID_PRODUCT_OUT/system/apex/$aosp_apex_name.capex --output /tmp/decompressed_$aosp_apex_name.apex
+        if ! $ADB_CMD shell ls /system/apex/$original_apex_name.apex 1>/dev/null 2>/dev/null; then
+            # Filename observed on some phones, even though it is not actually compressed
+            original_apex_name=${original_apex_name}_compressed
+        fi
         $ADB_CMD shell rm /system/apex/$original_apex_name.apex
         $ADB_CMD push /tmp/decompressed_$aosp_apex_name.apex /system/apex/$aosp_apex_name.apex
         rm /tmp/decompressed_$aosp_apex_name.apex
@@ -47,7 +55,7 @@
     local app_type=$1
     local original_apk_name=$2
     local aosp_apk_name=$3
-    $ADB_CMD shell rm /system/$app_type/$original_apk_name/$original_apk_name.apk
+    $ADB_CMD shell rm /system/$app_type/$original_apk_name/$original_apk_name*.apk
     $ADB_CMD push $ANDROID_PRODUCT_OUT/system/$app_type/$aosp_apk_name/$aosp_apk_name.apk /system/$app_type/$original_apk_name/
 }
 
@@ -97,7 +105,7 @@
     exit 1
 fi
 
-if ! $ADB_CMD wait-for-device shell pm path com.google.android.networkstack; then
+if ! $ADB_CMD wait-for-device shell pm path com.google.android.networkstack 1>/dev/null 2>/dev/null; then
     echo "This device is already not using GMS modules"
     exit 1
 fi
@@ -122,8 +130,7 @@
 $ADB_CMD reboot
 
 echo "Waiting for boot..."
-$ADB_CMD wait-for-device;
-until [[ $($ADB_CMD shell getprop sys.boot_completed) == 1 ]]; do
+until [[ $($ADB_CMD wait-for-device shell getprop sys.boot_completed) == 1 ]]; do
     sleep 1;
 done
 
@@ -146,7 +153,12 @@
 
 # Update the networkstack privapp-permissions allowlist
 rm -f /tmp/pulled_privapp-permissions.xml
-$ADB_CMD pull /system/etc/permissions/privapp-permissions-google.xml /tmp/pulled_privapp-permissions.xml
+networkstack_permissions=/system/etc/permissions/GoogleNetworkStack_permissions.xml
+if ! $ADB_CMD shell ls $networkstack_permissions 1>/dev/null 2>/dev/null; then
+    networkstack_permissions=/system/etc/permissions/privapp-permissions-google.xml
+fi
+
+$ADB_CMD pull $networkstack_permissions /tmp/pulled_privapp-permissions.xml
 
 # Remove last </permission> line, and the permissions for com.google.android.networkstack
 sed -nE '1,/<\/permissions>/p' /tmp/pulled_privapp-permissions.xml \
@@ -156,7 +168,7 @@
     >> /tmp/modified_privapp-permissions.xml
 echo '</permissions>' >> /tmp/modified_privapp-permissions.xml
 
-$ADB_CMD push /tmp/modified_privapp-permissions.xml /system/etc/permissions/privapp-permissions-google.xml
+$ADB_CMD push /tmp/modified_privapp-permissions.xml $networkstack_permissions
 
 rm /tmp/pulled_privapp-permissions.xml /tmp/modified_privapp-permissions.xml