Merge "Add test for updating IKE about IP address changes"
diff --git a/framework-t/src/android/net/NetworkStatsAccess.java b/framework-t/src/android/net/NetworkStatsAccess.java
index 0585756..23902dc 100644
--- a/framework-t/src/android/net/NetworkStatsAccess.java
+++ b/framework-t/src/android/net/NetworkStatsAccess.java
@@ -17,7 +17,6 @@
 package android.net;
 
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_REMOVED;
 import static android.net.TrafficStats.UID_TETHERING;
@@ -33,6 +32,8 @@
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
+import com.android.net.module.util.PermissionUtils;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -100,6 +101,7 @@
          * <li>Device owners.
          * <li>Carrier-privileged applications.
          * <li>The system UID.
+         * <li>NetworkStack application.
          * </ul>
          */
         int DEVICE = 3;
@@ -125,9 +127,9 @@
 
         final int appId = UserHandle.getAppId(callingUid);
 
-        final boolean isNetworkStack = context.checkPermission(
-                android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
-                == PERMISSION_GRANTED;
+        final boolean isNetworkStack = PermissionUtils.checkAnyPermissionOf(
+                context, callingPid, callingUid, android.Manifest.permission.NETWORK_STACK,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
 
         if (hasCarrierPrivileges || isDeviceOwner
                 || appId == Process.SYSTEM_UID || isNetworkStack) {
diff --git a/framework/src/android/net/LinkAddress.java b/framework/src/android/net/LinkAddress.java
index d48b8c7..90f55b3 100644
--- a/framework/src/android/net/LinkAddress.java
+++ b/framework/src/android/net/LinkAddress.java
@@ -487,17 +487,23 @@
      */
     @SystemApi
     public boolean isGlobalPreferred() {
-        /**
-         * Note that addresses flagged as IFA_F_OPTIMISTIC are
-         * simultaneously flagged as IFA_F_TENTATIVE (when the tentative
-         * state has cleared either DAD has succeeded or failed, and both
-         * flags are cleared regardless).
-         */
-        int flags = getFlags();
         return (scope == RT_SCOPE_UNIVERSE
                 && !isIpv6ULA()
-                && (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
-                && ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L));
+                && isPreferred());
+    }
+
+    /**
+     * Checks if the address is a preferred address.
+     *
+     * @hide
+     */
+    public boolean isPreferred() {
+        //  Note that addresses flagged as IFA_F_OPTIMISTIC are simultaneously flagged as
+        //  IFA_F_TENTATIVE (when the tentative state has cleared either DAD has succeeded or
+        //  failed, and both flags are cleared regardless).
+        int flags = getFlags();
+        return (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
+                && ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L);
     }
 
     /**
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 8fe20de..177f7e3 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -281,9 +281,8 @@
      *
      *   arg1 = the hardware slot number of the keepalive to start
      *   arg2 = interval in seconds
-     *   obj = AutomaticKeepaliveInfo object
+     *   obj = KeepalivePacketData object describing the data to be sent
      *
-     * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
      * @hide
      */
     public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
@@ -436,6 +435,14 @@
     public static final int CMD_DSCP_POLICY_STATUS = BASE + 28;
 
     /**
+     * Sent by the NetworkAgent to ConnectivityService to notify that this network is expected to be
+     * replaced within the specified time by a similar network.
+     * arg1 = timeout in milliseconds
+     * @hide
+     */
+    public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29;
+
+    /**
      * DSCP policy was successfully added.
      */
     public static final int DSCP_POLICY_STATUS_SUCCESS = 0;
@@ -477,27 +484,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface DscpPolicyStatus {}
 
-    /**
-     * Sent by the NetworkAgent to ConnectivityService to notify that this network is expected to be
-     * replaced within the specified time by a similar network.
-     * arg1 = timeout in milliseconds
-     * @hide
-     */
-    public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29;
-
-    /**
-     * Sent by AutomaticOnOffKeepaliveTracker periodically (when relevant) to trigger monitor
-     * automatic keepalive request.
-     *
-     * NATT keepalives have an automatic mode where the system only sends keepalive packets when
-     * TCP sockets are open over a VPN. The system will check periodically for presence of
-     * such open sockets, and this message is what triggers the re-evaluation.
-     *
-     * obj = A Binder object associated with the keepalive.
-     * @hide
-     */
-    public static final int CMD_MONITOR_AUTOMATIC_KEEPALIVE = BASE + 30;
-
     private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
         final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
                 config.legacyTypeName, config.legacySubTypeName);
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index a658791..c5104d8 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1330,6 +1330,9 @@
         mDeps = deps;
 
         mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper());
+        // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
+        // address events are received.
+        handler.post(mMdnsSocketProvider::startNetLinkMonitor);
         mMdnsSocketClient =
                 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
         mMdnsDiscoveryManager =
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 0952e88..8017ee0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -36,10 +36,12 @@
 import android.os.Looper;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.SharedLog;
 import com.android.server.connectivity.mdns.util.MdnsLogger;
 
 import java.io.IOException;
@@ -65,6 +67,7 @@
     // Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames.
     private static final int READ_BUFFER_SIZE = 2048;
     private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
+    private static final int IFACE_IDX_NOT_EXIST = -1;
     @NonNull private final Context mContext;
     @NonNull private final Looper mLooper;
     @NonNull private final Handler mHandler;
@@ -81,6 +84,9 @@
             new ArrayMap<>();
     private final List<String> mLocalOnlyInterfaces = new ArrayList<>();
     private final List<String> mTetheredInterfaces = new ArrayList<>();
+    // mIfaceIdxToLinkProperties should not be cleared in maybeStopMonitoringSockets() because
+    // the netlink monitor is never stop and the old states must be kept.
+    private final SparseArray<LinkProperties> mIfaceIdxToLinkProperties = new SparseArray<>();
     private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
     private boolean mMonitoringSockets = false;
     private boolean mRequestStop = false;
@@ -126,8 +132,8 @@
             }
         };
 
-        mSocketNetlinkMonitor = SocketNetLinkMonitorFactory.createNetLinkMonitor(mHandler,
-                LOGGER.mLog);
+        mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler, LOGGER.mLog,
+                new NetLinkMessageProcessor());
     }
 
     /**
@@ -148,8 +154,83 @@
                 @NonNull byte[] packetReadBuffer) throws IOException {
             return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer);
         }
-    }
 
+        /*** Get network interface by given interface name */
+        public int getNetworkInterfaceIndexByName(@NonNull final String ifaceName) {
+            final NetworkInterface iface;
+            try {
+                iface = NetworkInterface.getByName(ifaceName);
+            } catch (SocketException e) {
+                Log.e(TAG, "Error querying interface", e);
+                return IFACE_IDX_NOT_EXIST;
+            }
+            if (iface == null) {
+                Log.e(TAG, "Interface not found: " + ifaceName);
+                return IFACE_IDX_NOT_EXIST;
+            }
+            return iface.getIndex();
+        }
+        /*** Creates a SocketNetlinkMonitor */
+        public ISocketNetLinkMonitor createSocketNetlinkMonitor(@NonNull final Handler handler,
+                @NonNull final SharedLog log,
+                @NonNull final NetLinkMonitorCallBack cb) {
+            return SocketNetLinkMonitorFactory.createNetLinkMonitor(handler, log, cb);
+        }
+    }
+    /**
+     * The callback interface for the netlink monitor messages.
+     */
+    public interface NetLinkMonitorCallBack {
+        /**
+         * Handles the interface address add or update.
+         */
+        void addOrUpdateInterfaceAddress(int ifaceIdx, @NonNull LinkAddress newAddress);
+
+
+        /**
+         * Handles the interface address delete.
+         */
+        void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress);
+    }
+    private class NetLinkMessageProcessor implements NetLinkMonitorCallBack {
+
+        @Override
+        public void addOrUpdateInterfaceAddress(int ifaceIdx,
+                @NonNull final LinkAddress newAddress) {
+
+            LinkProperties linkProperties;
+            linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
+            if (linkProperties == null) {
+                linkProperties = new LinkProperties();
+                mIfaceIdxToLinkProperties.put(ifaceIdx, linkProperties);
+            }
+            boolean updated = linkProperties.addLinkAddress(newAddress);
+
+            if (!updated) {
+                return;
+            }
+            maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
+        }
+
+        @Override
+        public void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress) {
+            LinkProperties linkProperties;
+            boolean updated = false;
+            linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
+            if (linkProperties != null) {
+                updated = linkProperties.removeLinkAddress(deleteAddress);
+                if (linkProperties.getLinkAddresses().isEmpty()) {
+                    mIfaceIdxToLinkProperties.remove(ifaceIdx);
+                }
+            }
+
+            if (linkProperties == null || !updated) {
+                return;
+            }
+            maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
+
+        }
+    }
     /*** Data class for storing socket related info  */
     private static class SocketInfo {
         final MdnsInterfaceSocket mSocket;
@@ -190,6 +271,15 @@
         }
         mMonitoringSockets = true;
     }
+    /**
+     * Start netlink monitor.
+     */
+    public void startNetLinkMonitor() {
+        ensureRunningOnHandlerThread(mHandler);
+        if (mSocketNetlinkMonitor.isSupported()) {
+            mSocketNetlinkMonitor.startMonitoring();
+        }
+    }
 
     private void maybeStopMonitoringSockets() {
         if (!mMonitoringSockets) return; // Already unregistered.
@@ -203,10 +293,6 @@
             final TetheringManager tetheringManager = mContext.getSystemService(
                     TetheringManager.class);
             tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
-
-            if (mSocketNetlinkMonitor.isSupported()) {
-                mHandler.post(mSocketNetlinkMonitor::stopMonitoring);
-            }
             // Clear all saved status.
             mActiveNetworksLinkProperties.clear();
             mNetworkSockets.clear();
@@ -215,6 +301,8 @@
             mTetheredInterfaces.clear();
             mMonitoringSockets = false;
         }
+        // The netlink monitor is not stopped here because the MdnsSocketProvider need to listen
+        // to all the netlink updates when the system is up and running.
     }
 
     /*** Request to stop monitoring sockets and unregister callbacks */
@@ -259,21 +347,38 @@
         if (socketInfo == null) {
             createSocket(networkKey, lp);
         } else {
-            // Update the addresses of this socket.
-            final List<LinkAddress> addresses = lp.getLinkAddresses();
-            socketInfo.mAddresses.clear();
-            socketInfo.mAddresses.addAll(addresses);
-            // Try to join the group again.
-            socketInfo.mSocket.joinGroup(addresses);
-
-            notifyAddressesChanged(network, socketInfo.mSocket, lp);
+            updateSocketInfoAddress(network, socketInfo, lp.getLinkAddresses());
+        }
+    }
+    private void maybeUpdateTetheringSocketAddress(int ifaceIndex,
+            @NonNull final List<LinkAddress> updatedAddresses) {
+        for (int i = 0; i < mTetherInterfaceSockets.size(); ++i) {
+            String tetheringInterfaceName = mTetherInterfaceSockets.keyAt(i);
+            if (mDependencies.getNetworkInterfaceIndexByName(tetheringInterfaceName)
+                    == ifaceIndex) {
+                updateSocketInfoAddress(null /* network */,
+                        mTetherInterfaceSockets.valueAt(i), updatedAddresses);
+                return;
+            }
         }
     }
 
-    private static LinkProperties createLPForTetheredInterface(String interfaceName) {
-        final LinkProperties linkProperties = new LinkProperties();
+    private void updateSocketInfoAddress(@Nullable final Network network,
+            @NonNull final SocketInfo socketInfo,
+            @NonNull final List<LinkAddress> addresses) {
+        // Update the addresses of this socket.
+        socketInfo.mAddresses.clear();
+        socketInfo.mAddresses.addAll(addresses);
+        // Try to join the group again.
+        socketInfo.mSocket.joinGroup(addresses);
+
+        notifyAddressesChanged(network, socketInfo.mSocket, addresses);
+    }
+    private LinkProperties createLPForTetheredInterface(@NonNull final String interfaceName,
+            int ifaceIndex) {
+        final LinkProperties linkProperties =
+                new LinkProperties(mIfaceIdxToLinkProperties.get(ifaceIndex));
         linkProperties.setInterfaceName(interfaceName);
-        // TODO: Use NetlinkMonitor to update addresses for tethering interfaces.
         return linkProperties;
     }
 
@@ -292,7 +397,8 @@
         final CompareResult<String> interfaceDiff = new CompareResult<>(
                 current, updated);
         for (String name : interfaceDiff.added) {
-            createSocket(LOCAL_NET, createLPForTetheredInterface(name));
+            int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name);
+            createSocket(LOCAL_NET, createLPForTetheredInterface(name, ifaceIndex));
         }
         for (String name : interfaceDiff.removed) {
             removeTetherInterfaceSocket(name);
@@ -332,14 +438,10 @@
             final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
                     networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
                     mPacketReadBuffer);
-            final List<LinkAddress> addresses;
+            final List<LinkAddress> addresses = lp.getLinkAddresses();
             if (networkKey == LOCAL_NET) {
-                addresses = CollectionUtils.map(
-                        networkInterface.getInterfaceAddresses(),
-                        i -> new LinkAddress(i.getAddress(), i.getNetworkPrefixLength()));
                 mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
             } else {
-                addresses = lp.getLinkAddresses();
                 mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork,
                         new SocketInfo(socket, addresses));
             }
@@ -422,12 +524,12 @@
     }
 
     private void notifyAddressesChanged(Network network, MdnsInterfaceSocket socket,
-            LinkProperties lp) {
+            List<LinkAddress> addresses) {
         for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
             final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
             if (isNetworkMatched(requestedNetwork, network)) {
                 mCallbacksToRequestedNetworks.keyAt(i)
-                        .onAddressesChanged(network, socket, lp.getLinkAddresses());
+                        .onAddressesChanged(network, socket, addresses);
             }
         }
     }
@@ -451,7 +553,10 @@
     private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
         final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
         if (socketInfo == null) {
-            createSocket(LOCAL_NET, createLPForTetheredInterface(interfaceName));
+            int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(interfaceName);
+            createSocket(
+                    LOCAL_NET,
+                    createLPForTetheredInterface(interfaceName, ifaceIndex));
         } else {
             // Notify the socket for requested network.
             cb.onSocketCreated(
diff --git a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
index 8f6aecc..4650255 100644
--- a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
+++ b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
@@ -31,8 +31,8 @@
      * Creates a new netlink monitor.
      */
     public static ISocketNetLinkMonitor createNetLinkMonitor(@NonNull final Handler handler,
-            @NonNull SharedLog log) {
-        return new SocketNetlinkMonitor(handler, log);
+            @NonNull SharedLog log, @NonNull MdnsSocketProvider.NetLinkMonitorCallBack cb) {
+        return new SocketNetlinkMonitor(handler, log, cb);
     }
 
     private SocketNetLinkMonitorFactory() {
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
index e053413..6395b53 100644
--- a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -17,28 +17,64 @@
 package com.android.server.connectivity.mdns.internal;
 
 import android.annotation.NonNull;
+import android.net.LinkAddress;
 import android.os.Handler;
 import android.system.OsConstants;
+import android.util.Log;
 
 import com.android.net.module.util.SharedLog;
 import com.android.net.module.util.ip.NetlinkMonitor;
 import com.android.net.module.util.netlink.NetlinkConstants;
 import com.android.net.module.util.netlink.NetlinkMessage;
+import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
+import com.android.net.module.util.netlink.StructIfaddrMsg;
 import com.android.server.connectivity.mdns.ISocketNetLinkMonitor;
+import com.android.server.connectivity.mdns.MdnsSocketProvider;
 
 /**
  * The netlink monitor for MdnsSocketProvider.
  */
 public class SocketNetlinkMonitor extends NetlinkMonitor implements ISocketNetLinkMonitor {
 
-    public SocketNetlinkMonitor(@NonNull final Handler handler, @NonNull SharedLog log) {
-        super(handler, log, SocketNetlinkMonitor.class.getSimpleName(), OsConstants.NETLINK_ROUTE,
-                NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
-    }
+    public static final String TAG = SocketNetlinkMonitor.class.getSimpleName();
 
+    @NonNull
+    private final MdnsSocketProvider.NetLinkMonitorCallBack mCb;
+    public SocketNetlinkMonitor(@NonNull final Handler handler,
+            @NonNull SharedLog log,
+            @NonNull final MdnsSocketProvider.NetLinkMonitorCallBack cb) {
+        super(handler, log, TAG, OsConstants.NETLINK_ROUTE,
+                NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
+        mCb = cb;
+    }
     @Override
     public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
+        if (nlMsg instanceof RtNetlinkAddressMessage) {
+            processRtNetlinkAddressMessage((RtNetlinkAddressMessage) nlMsg);
+        }
+    }
 
+    /**
+     * Process the RTM_NEWADDR and RTM_DELADDR netlink message.
+     */
+    private void processRtNetlinkAddressMessage(RtNetlinkAddressMessage msg) {
+        final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
+        final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
+                msg.getFlags(), ifaddrMsg.scope);
+        if (!la.isPreferred()) {
+            // Skip the unusable ip address.
+            return;
+        }
+        switch (msg.getHeader().nlmsg_type) {
+            case NetlinkConstants.RTM_NEWADDR:
+                mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+                break;
+            case NetlinkConstants.RTM_DELADDR:
+                mCb.deleteInterfaceAddress(ifaddrMsg.index, la);
+                break;
+            default:
+                Log.e(TAG, "Unknown rtnetlink address msg type " + msg.getHeader().nlmsg_type);
+        }
     }
 
     @Override
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 2af30dd..ba503e0 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -98,6 +98,7 @@
 
 import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
 import static com.android.net.module.util.NetworkMonitorUtils.isPrivateDnsValidationRequired;
+import static com.android.net.module.util.PermissionUtils.checkAnyPermissionOf;
 import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
 import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
 import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
@@ -463,7 +464,11 @@
     private String mCurrentTcpBufferSizes;
 
     private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
-            new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class });
+            new Class[] {
+                    ConnectivityService.class,
+                    NetworkAgent.class,
+                    NetworkAgentInfo.class,
+                    AutomaticOnOffKeepaliveTracker.class });
 
     private enum ReapUnvalidatedNetworks {
         // Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -2324,11 +2329,12 @@
         if (newNc.getNetworkSpecifier() != null) {
             newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
         }
-        if (!checkAnyPermissionOf(callerPid, callerUid, android.Manifest.permission.NETWORK_STACK,
+        if (!checkAnyPermissionOf(mContext, callerPid, callerUid,
+                android.Manifest.permission.NETWORK_STACK,
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)) {
             newNc.setAdministratorUids(new int[0]);
         }
-        if (!checkAnyPermissionOf(
+        if (!checkAnyPermissionOf(mContext,
                 callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
             newNc.setAllowedUids(new ArraySet<>());
             newNc.setSubscriptionIds(Collections.emptySet());
@@ -2837,15 +2843,6 @@
         setUidBlockedReasons(uid, blockedReasons);
     }
 
-    private boolean checkAnyPermissionOf(int pid, int uid, String... permissions) {
-        for (String permission : permissions) {
-            if (mContext.checkPermission(permission, pid, uid) == PERMISSION_GRANTED) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private void enforceInternetPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.INTERNET,
@@ -3004,13 +3001,13 @@
     }
 
     private boolean checkNetworkStackPermission(int pid, int uid) {
-        return checkAnyPermissionOf(pid, uid,
+        return checkAnyPermissionOf(mContext, pid, uid,
                 android.Manifest.permission.NETWORK_STACK,
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
     private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
-        return checkAnyPermissionOf(pid, uid,
+        return checkAnyPermissionOf(mContext, pid, uid,
                 android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
                 android.Manifest.permission.NETWORK_SETTINGS);
@@ -5008,7 +5005,7 @@
     }
 
     private RequestInfoPerUidCounter getRequestCounter(NetworkRequestInfo nri) {
-        return checkAnyPermissionOf(
+        return checkAnyPermissionOf(mContext,
                 nri.mPid, nri.mUid, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
                 ? mSystemNetworkRequestCounter : mNetworkRequestCounter;
     }
@@ -5605,12 +5602,13 @@
                     handleConfigureAlwaysOnNetworks();
                     break;
                 }
-                // Sent by KeepaliveTracker to process an app request on the state machine thread.
-                case NetworkAgent.CMD_START_SOCKET_KEEPALIVE: {
+                // Sent by AutomaticOnOffKeepaliveTracker to process an app request on the
+                // handler thread.
+                case AutomaticOnOffKeepaliveTracker.CMD_REQUEST_START_KEEPALIVE: {
                     mKeepaliveTracker.handleStartKeepalive(msg);
                     break;
                 }
-                case NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE: {
+                case AutomaticOnOffKeepaliveTracker.CMD_MONITOR_AUTOMATIC_KEEPALIVE: {
                     final AutomaticOnOffKeepalive ki =
                             mKeepaliveTracker.getKeepaliveForBinder((IBinder) msg.obj);
                     if (null == ki) return; // The callback was unregistered before the alarm fired
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index acce95d..881c92d 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -16,7 +16,6 @@
 
 package com.android.server.connectivity;
 
-import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
 import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
 import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
 import static android.net.SocketKeepalive.SUCCESS_PAUSED;
@@ -40,7 +39,6 @@
 import android.net.ISocketKeepaliveCallback;
 import android.net.MarkMaskParcel;
 import android.net.Network;
-import android.net.NetworkAgent;
 import android.net.SocketKeepalive.InvalidSocketException;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -94,6 +92,29 @@
     private static final int ADJUST_TCP_POLLING_DELAY_MS = 2000;
     private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
             "automatic_on_off_keepalive_version";
+
+    // ConnectivityService parses message constants from itself and AutomaticOnOffKeepaliveTracker
+    // with MessageUtils for debugging purposes, and crashes if some messages have the same values.
+    private static final int BASE = 2000;
+    /**
+     * Sent by AutomaticOnOffKeepaliveTracker periodically (when relevant) to trigger monitor
+     * automatic keepalive request.
+     *
+     * NATT keepalives have an automatic mode where the system only sends keepalive packets when
+     * TCP sockets are open over a VPN. The system will check periodically for presence of
+     * such open sockets, and this message is what triggers the re-evaluation.
+     *
+     * obj = A Binder object associated with the keepalive.
+     */
+    public static final int CMD_MONITOR_AUTOMATIC_KEEPALIVE = BASE + 1;
+
+    /**
+     * Sent by AutomaticOnOffKeepaliveTracker to ConnectivityService to start a keepalive.
+     *
+     * obj = AutomaticKeepaliveInfo object
+     */
+    public static final int CMD_REQUEST_START_KEEPALIVE = BASE + 2;
+
     /**
      * States for {@code #AutomaticOnOffKeepalive}.
      *
@@ -202,7 +223,7 @@
                     throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
                 }
                 mAlarmListener = () -> mConnectivityServiceHandler.obtainMessage(
-                        NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
+                        CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
                         .sendToTarget();
             } else {
                 mAutomaticOnOffState = STATE_ALWAYS_ON;
@@ -482,9 +503,8 @@
                     + " → " + dstAddrString + ":" + dstPort
                     + " auto=" + autoKi
                     + " underpinned=" + underpinnedNetwork);
-            mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
-                    // TODO : move ConnectivityService#encodeBool to a static lib.
-                    automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
+            mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
+                    .sendToTarget();
         } catch (InvalidSocketException e) {
             mKeepaliveTracker.notifyErrorCallback(cb, e.error);
         }
@@ -517,9 +537,8 @@
                     + " → " + dstAddrString + ":" + dstPort
                     + " auto=" + autoKi
                     + " underpinned=" + underpinnedNetwork);
-            mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
-                    // TODO : move ConnectivityService#encodeBool to a static lib.
-                    automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
+            mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
+                    .sendToTarget();
         } catch (InvalidSocketException e) {
             mKeepaliveTracker.notifyErrorCallback(cb, e.error);
         }
@@ -547,7 +566,7 @@
             final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
                     false /* autoOnOff, tcp keepalives are never auto on/off */,
                     null /* underpinnedNetwork, tcp keepalives do not refer to this */);
-            mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, autoKi)
+            mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
                     .sendToTarget();
         } catch (InvalidSocketException e) {
             mKeepaliveTracker.notifyErrorCallback(cb, e.error);
diff --git a/tests/unit/java/android/net/NetworkStatsAccessTest.java b/tests/unit/java/android/net/NetworkStatsAccessTest.java
index a74056b..8b86211 100644
--- a/tests/unit/java/android/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/android/net/NetworkStatsAccessTest.java
@@ -78,6 +78,7 @@
         setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
         setHasReadHistoryPermission(false);
         setHasNetworkStackPermission(false);
+        setHasMainlineNetworkStackPermission(false);
     }
 
     @After
@@ -154,6 +155,10 @@
         setHasNetworkStackPermission(false);
         assertEquals(NetworkStatsAccess.Level.DEFAULT,
                 NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
+
+        setHasMainlineNetworkStackPermission(true);
+        assertEquals(NetworkStatsAccess.Level.DEVICE,
+                NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
     }
 
     private void setHasCarrierPrivileges(boolean hasPrivileges) {
@@ -189,4 +194,10 @@
                 TEST_PID, TEST_UID)).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
                 : PackageManager.PERMISSION_DENIED);
     }
+
+    private void setHasMainlineNetworkStackPermission(boolean hasPermission) {
+        when(mContext.checkPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                TEST_PID, TEST_UID)).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
+                : PackageManager.PERMISSION_DENIED);
+    }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index 696eff4..3eb1b26 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -47,7 +47,6 @@
 import android.net.MarkMaskParcel;
 import android.net.NattKeepalivePacketData;
 import android.net.Network;
-import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.os.Binder;
@@ -268,11 +267,11 @@
         @Override
         public void handleMessage(@NonNull final Message msg) {
             switch (msg.what) {
-                case NetworkAgent.CMD_START_SOCKET_KEEPALIVE:
-                    Log.d(TAG, "Test handler received CMD_START_SOCKET_KEEPALIVE : " + msg);
+                case AutomaticOnOffKeepaliveTracker.CMD_REQUEST_START_KEEPALIVE:
+                    Log.d(TAG, "Test handler received CMD_REQUEST_START_KEEPALIVE : " + msg);
                     mAOOKeepaliveTracker.handleStartKeepalive(msg);
                     break;
-                case NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE:
+                case AutomaticOnOffKeepaliveTracker.CMD_MONITOR_AUTOMATIC_KEEPALIVE:
                     Log.d(TAG, "Test handler received CMD_MONITOR_AUTOMATIC_KEEPALIVE : " + msg);
                     mLastAutoKi = mAOOKeepaliveTracker.getKeepaliveForBinder((IBinder) msg.obj);
                     break;
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 426ee1a..1cf0ad3 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -1320,12 +1320,12 @@
     }
 
     private void verifyVpnManagerEvent(String sessionKey, String category, int errorClass,
-            int errorCode, String[] packageName, VpnProfileState... profileState) {
+            int errorCode, String[] packageName, @NonNull VpnProfileState... profileState) {
         final Context userContext =
                 mContext.createContextAsUser(UserHandle.of(PRIMARY_USER.id), 0 /* flags */);
         final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
 
-        final int verifyTimes = (profileState == null) ? 1 : profileState.length;
+        final int verifyTimes = profileState.length;
         verify(userContext, times(verifyTimes)).startService(intentArgumentCaptor.capture());
 
         for (int i = 0; i < verifyTimes; i++) {
@@ -1356,10 +1356,8 @@
                         VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES));
             }
 
-            if (profileState != null) {
-                assertEquals(profileState[i], intent.getParcelableExtra(
-                        VpnManager.EXTRA_VPN_PROFILE_STATE, VpnProfileState.class));
-            }
+            assertEquals(profileState[i], intent.getParcelableExtra(
+                    VpnManager.EXTRA_VPN_PROFILE_STATE, VpnProfileState.class));
         }
         reset(userContext);
     }
@@ -1368,7 +1366,11 @@
         // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
         // errorCode won't be set.
         verifyVpnManagerEvent(sessionKey, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
-                -1 /* errorClass */, -1 /* errorCode */, packageName, null /* profileState */);
+                -1 /* errorClass */, -1 /* errorCode */, packageName,
+                // VPN NetworkAgnet does not switch to CONNECTED in the test, and the state is not
+                // important here. Verify that the state as it is, i.e. CONNECTING state.
+                new VpnProfileState(VpnProfileState.STATE_CONNECTING,
+                        sessionKey, false /* alwaysOn */, false /* lockdown */));
     }
 
     private void verifyAlwaysOnStateChanged(String[] packageName, VpnProfileState... profileState) {
@@ -1610,7 +1612,10 @@
         verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
         reset(mDeviceIdleInternal);
         verifyVpnManagerEvent(sessionKey, category, errorType, errorCode,
-                new String[] {TEST_VPN_PKG}, null /* profileState */);
+                // VPN NetworkAgnet does not switch to CONNECTED in the test, and the state is not
+                // important here. Verify that the state as it is, i.e. CONNECTING state.
+                new String[] {TEST_VPN_PKG}, new VpnProfileState(VpnProfileState.STATE_CONNECTING,
+                        sessionKey, false /* alwaysOn */, false /* lockdown */));
         if (errorType == VpnManager.ERROR_CLASS_NOT_RECOVERABLE) {
             verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
                     .unregisterNetworkCallback(eq(cb));
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index d9420b8..2d73c98 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -21,6 +21,8 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+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 static com.android.testutils.ContextUtils.mockService;
 
 import static org.junit.Assert.assertEquals;
@@ -30,6 +32,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -49,9 +52,19 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.net.module.util.ArrayTrackRecord;
+import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.netlink.NetlinkConstants;
+import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
+import com.android.net.module.util.netlink.StructIfaddrMsg;
+import com.android.net.module.util.netlink.StructNlMsgHdr;
 import com.android.server.connectivity.mdns.MdnsSocketProvider.Dependencies;
+import com.android.server.connectivity.mdns.internal.SocketNetlinkMonitor;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
 import com.android.testutils.HandlerUtils;
@@ -64,20 +77,28 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
 import java.util.Collections;
 import java.util.List;
 
 @RunWith(DevSdkIgnoreRunner.class)
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class MdnsSocketProviderTest {
+    private static final String TAG = MdnsSocketProviderTest.class.getSimpleName();
     private static final String TEST_IFACE_NAME = "test";
     private static final String LOCAL_ONLY_IFACE_NAME = "local_only";
     private static final String TETHERED_IFACE_NAME = "tethered";
+    private static final int TETHERED_IFACE_IDX = 32;
     private static final long DEFAULT_TIMEOUT = 2000L;
     private static final long NO_CALLBACK_TIMEOUT = 200L;
     private static final LinkAddress LINKADDRV4 = new LinkAddress("192.0.2.0/24");
     private static final LinkAddress LINKADDRV6 =
             new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64");
+
+    private static final LinkAddress LINKADDRV6_FLAG_CHANGE =
+            new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64", 1 /* flags */,
+                    0 /* scope */);
     private static final Network TEST_NETWORK = new Network(123);
     @Mock private Context mContext;
     @Mock private Dependencies mDeps;
@@ -91,6 +112,7 @@
     private NetworkCallback mNetworkCallback;
     private TetheringEventCallback mTetheringEventCallback;
 
+    private TestNetLinkMonitor mTestSocketNetLinkMonitor;
     @Before
     public void setUp() throws IOException {
         MockitoAnnotations.initMocks(this);
@@ -116,9 +138,21 @@
         doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
         doReturn(mock(MdnsInterfaceSocket.class))
                 .when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any());
+        doReturn(TETHERED_IFACE_IDX).when(mDeps).getNetworkInterfaceIndexByName(
+                TETHERED_IFACE_NAME);
         final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
         thread.start();
         mHandler = new Handler(thread.getLooper());
+
+        doReturn(mTestSocketNetLinkMonitor).when(mDeps).createSocketNetlinkMonitor(any(), any(),
+                any());
+        doAnswer(inv -> {
+            mTestSocketNetLinkMonitor = new TestNetLinkMonitor(inv.getArgument(0),
+                    inv.getArgument(1),
+                    inv.getArgument(2));
+            return mTestSocketNetLinkMonitor;
+        }).when(mDeps).createSocketNetlinkMonitor(any(), any(),
+                any());
         mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps);
     }
 
@@ -135,6 +169,23 @@
 
         mNetworkCallback = nwCallbackCaptor.getValue();
         mTetheringEventCallback = teCallbackCaptor.getValue();
+
+        mHandler.post(mSocketProvider::startNetLinkMonitor);
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+    }
+
+    private static class TestNetLinkMonitor extends SocketNetlinkMonitor {
+        TestNetLinkMonitor(@NonNull Handler handler,
+                @NonNull SharedLog log,
+                @Nullable MdnsSocketProvider.NetLinkMonitorCallBack cb) {
+            super(handler, log, cb);
+        }
+
+        @Override
+        public void startMonitoring() { }
+
+        @Override
+        public void stopMonitoring() { }
     }
 
     private class TestSocketCallback implements MdnsSocketProvider.SocketCallback {
@@ -301,6 +352,87 @@
         testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
     }
 
+    private RtNetlinkAddressMessage createNetworkAddressUpdateNetLink(
+            short msgType, LinkAddress linkAddress, int ifIndex, int flags) {
+        final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
+        nlmsghdr.nlmsg_type = msgType;
+        nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+        nlmsghdr.nlmsg_seq = 1;
+
+        InetAddress ip = linkAddress.getAddress();
+
+        final byte family =
+                (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
+        StructIfaddrMsg structIfaddrMsg = new StructIfaddrMsg(family,
+                (short) linkAddress.getPrefixLength(),
+                (short) linkAddress.getFlags(), (short) linkAddress.getScope(), ifIndex);
+
+        return new RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg, ip,
+                null /* structIfacacheInfo */, flags);
+    }
+
+    @Test
+    public void testDownstreamNetworkAddressUpdateFromNetlink() {
+        startMonitoringSockets();
+        final TestSocketCallback testCallbackAll = new TestSocketCallback();
+        mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallbackAll));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+
+        // Address add message arrived before the interface is created.
+        RtNetlinkAddressMessage addIpv4AddrMsg = createNetworkAddressUpdateNetLink(
+                NetlinkConstants.RTM_NEWADDR,
+                LINKADDRV4,
+                TETHERED_IFACE_IDX,
+                0 /* flags */);
+        mHandler.post(
+                () -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv4AddrMsg,
+                        0 /* whenMs */));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+
+        // Interface is created.
+        mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
+                List.of(TETHERED_IFACE_NAME)));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        verify(mTetheredIfaceWrapper).getNetworkInterface();
+        testCallbackAll.expectedSocketCreatedForNetwork(null /* network */, List.of(LINKADDRV4));
+
+        // Old Address removed.
+        RtNetlinkAddressMessage removeIpv4AddrMsg = createNetworkAddressUpdateNetLink(
+                NetlinkConstants.RTM_DELADDR,
+                LINKADDRV4,
+                TETHERED_IFACE_IDX,
+                0 /* flags */);
+        mHandler.post(
+                () -> mTestSocketNetLinkMonitor.processNetlinkMessage(removeIpv4AddrMsg,
+                        0 /* whenMs */));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of());
+
+        // New address added.
+        RtNetlinkAddressMessage addIpv6AddrMsg = createNetworkAddressUpdateNetLink(
+                NetlinkConstants.RTM_NEWADDR,
+                LINKADDRV6,
+                TETHERED_IFACE_IDX,
+                0 /* flags */);
+        mHandler.post(() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv6AddrMsg,
+                0 /* whenMs */));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of(LINKADDRV6));
+
+        // Address updated
+        RtNetlinkAddressMessage updateIpv6AddrMsg = createNetworkAddressUpdateNetLink(
+                NetlinkConstants.RTM_NEWADDR,
+                LINKADDRV6,
+                TETHERED_IFACE_IDX,
+                1 /* flags */);
+        mHandler.post(
+                () -> mTestSocketNetLinkMonitor.processNetlinkMessage(updateIpv6AddrMsg,
+                        0 /* whenMs */));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        testCallbackAll.expectedAddressesChangedForNetwork(null /* network */,
+                List.of(LINKADDRV6_FLAG_CHANGE));
+    }
+
     @Test
     public void testAddressesChanged() throws Exception {
         startMonitoringSockets();