Call onReachabilityFailure callback on networkstack-aidl-interfaces-v13+.

The ReachabilityLossInfoParcelable parameter in this callback can
be used to tell wifi the specific reachability failure event type
such as NUD failure due to L2 roam or organic probe or Wifi RSSI
check or DHCP roam failure, then WiFi takes corresponding actions
for different situations.

Bug: 204723906
Test: atest NetworkStackTests NetworkStackIntegrationTests
Change-Id: I07c4a567d7befd3698ecfb4536721796bc33a7de
diff --git a/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java
index d3e6cb7..7cf46f5 100644
--- a/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java
+++ b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java
@@ -141,5 +141,9 @@
      *
      * @param lossInfo the specific neighbor reachability loss information.
      */
-    public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) {}
+    public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) {
+        // If the client does not implement this method, call the older
+        // onReachabilityLost method.
+        onReachabilityLost(lossInfo.message);
+    }
 }
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index a81ab5c..77252b4 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -21,6 +21,8 @@
 import static android.net.ip.IIpClient.PROV_IPV4_DISABLED;
 import static android.net.ip.IIpClient.PROV_IPV6_DISABLED;
 import static android.net.ip.IIpClient.PROV_IPV6_LINKLOCAL;
+import static android.net.ip.IpReachabilityMonitor.INVALID_REACHABILITY_LOSS_TYPE;
+import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt;
 import static android.net.util.NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION;
 import static android.net.util.NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION;
 import static android.net.util.NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION;
@@ -63,6 +65,8 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
 import android.net.networkstack.aidl.dhcp.DhcpOption;
+import android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable;
+import android.net.networkstack.aidl.ip.ReachabilityLossReason;
 import android.net.shared.InitialConfiguration;
 import android.net.shared.Layer2Information;
 import android.net.shared.ProvisioningConfiguration;
@@ -81,6 +85,7 @@
 import android.os.SystemClock;
 import android.stats.connectivity.DisconnectCode;
 import android.stats.connectivity.NetworkQuirkEvent;
+import android.stats.connectivity.NudEventType;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -316,6 +321,11 @@
         /**
          * Called when the internal IpReachabilityMonitor (if enabled) has detected the loss of
          * required neighbors (e.g. on-link default gw or dns servers) due to NUD_FAILED.
+         *
+         * Note this method is only supported on networkstack-aidl-interfaces-v12 or below.
+         * For above aidl versions, the caller should call {@link onReachabilityFailure} instead.
+         * For callbacks extending IpClientCallbacks, this method will be called iff the callback
+         * does not implement onReachabilityFailure.
          */
         public void onReachabilityLost(String logMsg) {
             log("onReachabilityLost(" + logMsg + ")");
@@ -402,6 +412,20 @@
         }
 
         /**
+         * Called when Neighbor Unreachability Detection fails, that might be caused by the organic
+         * probe or probeAll from IpReachabilityMonitor (if enabled).
+         */
+        public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) {
+            log("onReachabilityFailure(" + lossInfo.message + ", loss reason: "
+                    + reachabilityLossReasonToString(lossInfo.reason) + ")");
+            try {
+                mCallback.onReachabilityFailure(lossInfo);
+            } catch (RemoteException e) {
+                log("Failed to call onReachabilityFailure", e);
+            }
+        }
+
+        /**
          * Get the version of the IIpClientCallbacks AIDL interface.
          */
         public int getInterfaceVersion() {
@@ -478,6 +502,10 @@
     private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
     private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
 
+    // onReachabilityFailure callback is added since networkstack-aidl-interfaces-v13.
+    @VisibleForTesting
+    static final int VERSION_ADDED_REACHABILITY_FAILURE = 13;
+
     // Specific vendor OUI(3 bytes)/vendor specific type(1 byte) pattern for upstream hotspot
     // device detection. Add new byte array pattern below in turn.
     private static final List<byte[]> METERED_IE_PATTERN_LIST = Collections.unmodifiableList(
@@ -1250,6 +1278,20 @@
         transitionTo(mStoppingState);
     }
 
+    // Convert reachability loss reason enum to a string.
+    private static String reachabilityLossReasonToString(int reason) {
+        switch (reason) {
+            case ReachabilityLossReason.ROAM:
+                return "reachability_loss_after_roam";
+            case ReachabilityLossReason.CONFIRM:
+                return "reachability_loss_after_confirm";
+            case ReachabilityLossReason.ORGANIC:
+                return "reachability_loss_organic";
+            default:
+                return "unknown";
+        }
+    }
+
     private static boolean hasIpv6LinkLocalInterfaceRoute(final LinkProperties lp) {
         for (RouteInfo r : lp.getRoutes()) {
             if (r.getDestination().equals(new IpPrefix("fe80::/64"))
@@ -1859,8 +1901,17 @@
                     mLog,
                     new IpReachabilityMonitor.Callback() {
                         @Override
-                        public void notifyLost(InetAddress ip, String logMsg) {
-                            mCallback.onReachabilityLost(logMsg);
+                        public void notifyLost(InetAddress ip, String logMsg, NudEventType type) {
+                            final int version = mCallback.getInterfaceVersion();
+                            if (version >= VERSION_ADDED_REACHABILITY_FAILURE) {
+                                final int reason = nudEventTypeToInt(type);
+                                if (reason == INVALID_REACHABILITY_LOSS_TYPE) return;
+                                final ReachabilityLossInfoParcelable lossInfo =
+                                        new ReachabilityLossInfoParcelable(logMsg, reason);
+                                mCallback.onReachabilityFailure(lossInfo);
+                            } else {
+                                mCallback.onReachabilityLost(logMsg);
+                            }
                         }
                     },
                     mConfiguration.mUsingMultinetworkPolicyTracker,
diff --git a/src/android/net/ip/IpReachabilityMonitor.java b/src/android/net/ip/IpReachabilityMonitor.java
index b234172..c716fdf 100644
--- a/src/android/net/ip/IpReachabilityMonitor.java
+++ b/src/android/net/ip/IpReachabilityMonitor.java
@@ -32,6 +32,7 @@
 import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpReachabilityEvent;
+import android.net.networkstack.aidl.ip.ReachabilityLossReason;
 import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.ConditionVariable;
@@ -157,6 +158,7 @@
     private static final int INVALID_NUD_MCAST_RESOLICIT_NUM = -1;
 
     private static final int INVALID_LEGACY_NUD_FAILURE_TYPE = -1;
+    public static final int INVALID_REACHABILITY_LOSS_TYPE = -1;
 
     public interface Callback {
         /**
@@ -165,7 +167,7 @@
          *
          * TODO: refactor to something like notifyProvisioningLost(String msg).
          */
-        void notifyLost(InetAddress ip, String logMsg);
+        void notifyLost(InetAddress ip, String logMsg, NudEventType type);
     }
 
     /**
@@ -408,10 +410,11 @@
             final String logMsg = "ALERT neighbor: " + event.ip
                     + " MAC address changed from: " + prev.macAddr
                     + " to: " + event.macAddr;
+            final NudEventType type =
+                    getMacAddressChangedEventType(isFromProbe(), isNudFailureDueToRoam());
             mLog.w(logMsg);
-            mCallback.notifyLost(event.ip, logMsg);
-            logNudFailed(event,
-                    getMacAddressChangedEventType(isFromProbe(), isNudFailureDueToRoam()));
+            mCallback.notifyLost(event.ip, logMsg, type);
+            logNudFailed(event, type);
             return;
         }
         maybeRestoreNeighborParameters();
@@ -458,7 +461,7 @@
             Log.w(TAG, logMsg);
             // TODO: remove |ip| when the callback signature no longer has
             // an InetAddress argument.
-            mCallback.notifyLost(ip, logMsg);
+            mCallback.notifyLost(ip, logMsg, type);
         }
         logNudFailed(event, type);
     }
@@ -693,4 +696,24 @@
                 return INVALID_LEGACY_NUD_FAILURE_TYPE;
         }
     }
+
+    /**
+     * Convert the NUD critical failure event type to a int constant defined in IIpClientCallbacks.
+     */
+    public static int nudEventTypeToInt(final NudEventType type) {
+        switch (type) {
+            case NUD_POST_ROAMING_FAILED_CRITICAL:
+            case NUD_POST_ROAMING_MAC_ADDRESS_CHANGED:
+                return ReachabilityLossReason.ROAM;
+            case NUD_CONFIRM_FAILED_CRITICAL:
+            case NUD_CONFIRM_MAC_ADDRESS_CHANGED:
+                return ReachabilityLossReason.CONFIRM;
+            case NUD_ORGANIC_FAILED_CRITICAL:
+            case NUD_ORGANIC_MAC_ADDRESS_CHANGED:
+                return ReachabilityLossReason.ORGANIC;
+            // For other NudEventType which won't trigger notifyLost, just ignore these events.
+            default:
+                return INVALID_REACHABILITY_LOSS_TYPE;
+        }
+    }
 }
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt
index 748ee5a..fa379d3 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt
@@ -21,8 +21,6 @@
 import android.net.ipmemorystore.Status
 import android.net.ipmemorystore.Status.SUCCESS
 import android.util.ArrayMap
-import java.net.Inet6Address
-import kotlin.test.assertEquals
 import org.mockito.Mockito.any
 import org.mockito.Mockito.doAnswer
 import org.mockito.ArgumentCaptor
@@ -63,17 +61,6 @@
         verify(mIpMemoryStore, never()).storeNetworkAttributes(eq(l2Key), any(), any())
     }
 
-    override fun assertNotifyNeighborLost(targetIp: Inet6Address) {
-        val target = ArgumentCaptor.forClass(Inet6Address::class.java)
-
-        verify(mCallback, timeout(TEST_TIMEOUT_MS)).notifyLost(target.capture(), any())
-        assertEquals(targetIp, target.getValue())
-    }
-
-    override fun assertNeverNotifyNeighborLost() {
-        verify(mCallback, never()).notifyLost(any(), any())
-    }
-
     override fun storeNetworkAttributes(l2Key: String, na: NetworkAttributes) {
         doAnswer { inv ->
             val listener = inv.getArgument<OnNetworkAttributesRetrievedListener>(1)
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
index f4de743..174bb0f 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -29,8 +29,8 @@
 import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
 import static android.net.ip.IpReachabilityMonitor.MIN_NUD_SOLICIT_NUM;
 import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM;
+import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt;
 import static android.net.ipmemorystore.Status.SUCCESS;
-import static android.net.shared.ProvisioningConfiguration.VERSION_ADDED_PROVISIONING_ENUM;
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IFA_F_TEMPORARY;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
@@ -125,6 +125,7 @@
 import android.net.ipmemorystore.Status;
 import android.net.networkstack.TestNetworkStackServiceClient;
 import android.net.networkstack.aidl.dhcp.DhcpOption;
+import android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable;
 import android.net.shared.Layer2Information;
 import android.net.shared.ProvisioningConfiguration;
 import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
@@ -141,6 +142,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.stats.connectivity.NetworkQuirkEvent;
+import android.stats.connectivity.NudEventType;
 import android.system.ErrnoException;
 import android.system.Os;
 
@@ -292,7 +294,6 @@
     @Mock protected NetworkStackIpMemoryStore mIpMemoryStore;
     @Mock private NetworkQuirkMetrics.Dependencies mNetworkQuirkMetricsDeps;
     @Mock private IpReachabilityMonitorMetrics mIpReachabilityMonitorMetrics;
-    @Mock protected IpReachabilityMonitor.Callback mCallback;
 
     @Spy private INetd mNetd;
     private NetworkObserverRegistry mNetworkObserverRegistry;
@@ -438,7 +439,7 @@
                 InterfaceParams ifParams, Handler h, SharedLog log,
                 IpReachabilityMonitor.Callback callback, boolean usingMultinetworkPolicyTracker,
                 IpReachabilityMonitor.Dependencies deps, final INetd netd) {
-            return new IpReachabilityMonitor(context, ifParams, h, log, mCallback,
+            return new IpReachabilityMonitor(context, ifParams, h, log, callback,
                     usingMultinetworkPolicyTracker, deps, netd);
         }
 
@@ -542,10 +543,6 @@
 
     protected abstract void assertIpMemoryNeverStoreNetworkAttributes(String l2Key, long timeout);
 
-    protected abstract void assertNotifyNeighborLost(Inet6Address targetIp);
-
-    protected abstract void assertNeverNotifyNeighborLost();
-
     protected final boolean testSkipped() {
         // TODO: split out a test suite for root tests, and fail hard instead of skipping the test
         // if it is run on devices where TestNetworkStackServiceClient is not supported
@@ -618,7 +615,7 @@
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         when(mNetworkStackServiceManager.getIpMemoryStoreService())
                 .thenReturn(mIpMemoryStoreService);
-        when(mCb.getInterfaceVersion()).thenReturn(VERSION_ADDED_PROVISIONING_ENUM);
+        when(mCb.getInterfaceVersion()).thenReturn(IpClient.VERSION_ADDED_REACHABILITY_FAILURE);
 
         mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67);
         mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
@@ -3467,6 +3464,38 @@
         prepareIpReachabilityMonitorTest(false /* isMulticastResolicitEnabled */);
     }
 
+    private void assertNotifyNeighborLost(Inet6Address targetIp, NudEventType eventType)
+            throws Exception {
+        // For root test suite, rely on the IIpClient aidl interface version constant defined in
+        // {@link IpClientRootTest.BinderCbWrapper}; for privileged integration test suite that
+        // requires signature permission, use the mocked aidl version defined in {@link setUpMocks},
+        // which results in only new callbacks are verified. And add separate test cases to test the
+        // legacy callbacks explicitly as well.
+        assertNeighborReachabilityLoss(targetIp, eventType,
+                useNetworkStackSignature()
+                        ? IpClient.VERSION_ADDED_REACHABILITY_FAILURE
+                        : mIIpClient.getInterfaceVersion());
+    }
+
+    private void assertNeighborReachabilityLoss(Inet6Address targetIp, NudEventType eventType,
+            int targetAidlVersion) throws Exception {
+        if (targetAidlVersion >= IpClient.VERSION_ADDED_REACHABILITY_FAILURE) {
+            final ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor =
+                    ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class);
+            verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityFailure(lossInfoCaptor.capture());
+            assertEquals(nudEventTypeToInt(eventType), lossInfoCaptor.getValue().reason);
+            verify(mCb, never()).onReachabilityLost(any());
+        } else {
+            verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any());
+            verify(mCb, never()).onReachabilityFailure(any());
+        }
+    }
+
+    private void assertNeverNotifyNeighborLost() throws Exception {
+        verify(mCb, never()).onReachabilityFailure(any());
+        verify(mCb, never()).onReachabilityLost(any());
+    }
+
     private void prepareIpReachabilityMonitorTest(boolean isMulticastResolicitEnabled)
             throws Exception {
         final ScanResultInfo info = makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID);
@@ -3487,8 +3516,7 @@
         forceLayer2Roaming();
     }
 
-    @Test
-    public void testIpReachabilityMonitor_probeFailed() throws Exception {
+    private void runIpReachabilityMonitorProbeFailedTest() throws Exception {
         prepareIpReachabilityMonitorTest();
 
         final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
@@ -3497,7 +3525,22 @@
             assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */,
                     ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
         }
-        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */);
+    }
+
+    @Test
+    public void testIpReachabilityMonitor_probeFailed() throws Exception {
+        runIpReachabilityMonitorProbeFailedTest();
+        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
+                NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
+    }
+
+    @Test @SignatureRequiredTest(reason = "requires mock callback object")
+    public void testIpReachabilityMonitor_probeFailed_legacyCallback() throws Exception {
+        when(mCb.getInterfaceVersion()).thenReturn(12 /* assign an older interface aidl version */);
+
+        runIpReachabilityMonitorProbeFailedTest();
+        verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any());
+        verify(mCb, never()).onReachabilityFailure(any());
     }
 
     @Test
@@ -3516,8 +3559,7 @@
         assertNeverNotifyNeighborLost();
     }
 
-    @Test
-    public void testIpReachabilityMonitor_mcastResoclicitProbeFailed() throws Exception {
+    private void runIpReachabilityMonitorMcastResolicitProbeFailedTest() throws Exception {
         prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
 
         final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
@@ -3530,11 +3572,27 @@
         for (NeighborSolicitation ns : nsList.subList(MIN_NUD_SOLICIT_NUM, nsList.size())) {
             assertMulticastNeighborSolicitation(ns, ROUTER_LINK_LOCAL /* targetIp */);
         }
-        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */);
     }
 
     @Test
-    public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithSameLinkLayerAddress()
+    public void testIpReachabilityMonitor_mcastResolicitProbeFailed() throws Exception {
+        runIpReachabilityMonitorMcastResolicitProbeFailedTest();
+        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
+                NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
+    }
+
+    @Test @SignatureRequiredTest(reason = "requires mock callback object")
+    public void testIpReachabilityMonitor_mcastResolicitProbeFailed_legacyCallback()
+            throws Exception {
+        when(mCb.getInterfaceVersion()).thenReturn(12 /* assign an older interface aidl version */);
+
+        runIpReachabilityMonitorMcastResolicitProbeFailedTest();
+        verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any());
+        verify(mCb, never()).onReachabilityFailure(any());
+    }
+
+    @Test
+    public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithSameLinkLayerAddress()
             throws Exception {
         prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
 
@@ -3551,7 +3609,7 @@
     }
 
     @Test
-    public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithDiffLinkLayerAddress()
+    public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithDiffLinkLayerAddress()
             throws Exception {
         prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
 
@@ -3570,7 +3628,8 @@
                 ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
                 ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */);
         mPacketReader.sendResponse(na);
-        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */);
+        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
+                NudEventType.NUD_POST_ROAMING_MAC_ADDRESS_CHANGED);
     }
 
     @Test
diff --git a/tests/integration/src/android/net/ip/IpClientRootTest.kt b/tests/integration/src/android/net/ip/IpClientRootTest.kt
index d861639..7359e05 100644
--- a/tests/integration/src/android/net/ip/IpClientRootTest.kt
+++ b/tests/integration/src/android/net/ip/IpClientRootTest.kt
@@ -33,7 +33,6 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.net.module.util.DeviceConfigUtils
 import java.lang.System.currentTimeMillis
-import java.net.Inet6Address
 import java.util.concurrent.CompletableFuture
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
@@ -46,8 +45,6 @@
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.mockito.ArgumentCaptor
-import org.mockito.Mockito.anyString
-import org.mockito.Mockito.never
 import org.mockito.Mockito.timeout
 import org.mockito.Mockito.verify
 
@@ -265,14 +262,6 @@
         assertNull(listener.getBlockingNetworkAttributes(timeout))
     }
 
-    override fun assertNotifyNeighborLost(targetIp: Inet6Address) {
-        verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(anyString())
-    }
-
-    override fun assertNeverNotifyNeighborLost() {
-        verify(mCb, never()).onReachabilityLost(anyString())
-    }
-
     override fun storeNetworkAttributes(l2Key: String, na: NetworkAttributes) {
         mStore.storeNetworkAttributes(l2Key, na, null /* listener */)
     }
diff --git a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
index 5245cc3..246f000 100644
--- a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
+++ b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
@@ -287,10 +287,15 @@
         reachabilityMonitor.updateLinkProperties(TEST_LINK_PROPERTIES)
 
         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_DNS, NUD_FAILED))
-        verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(TEST_IPV4_DNS), anyString())
+        verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(TEST_IPV4_DNS), anyString(),
+                eq(NUD_ORGANIC_FAILED_CRITICAL))
     }
 
-    private fun runLoseProvisioningTest(newLp: LinkProperties, lostNeighbor: InetAddress) {
+    private fun runLoseProvisioningTest(
+        newLp: LinkProperties,
+        lostNeighbor: InetAddress,
+        eventType: NudEventType
+    ) {
         reachabilityMonitor.updateLinkProperties(newLp)
 
         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_GATEWAY, NUD_STALE))
@@ -299,7 +304,8 @@
         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_DNS, NUD_STALE))
 
         neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED))
-        verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString())
+        verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString(),
+                eq(eventType))
     }
 
     private fun verifyNudFailureMetrics(
@@ -327,7 +333,7 @@
 
         neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED))
         handlerThread.waitForIdle(TEST_TIMEOUT_MS)
-        verify(callback, never()).notifyLost(any(), anyString())
+        verify(callback, never()).notifyLost(any(), anyString(), any(NudEventType::class.java))
         verifyNudFailureMetrics(eventType, ipType, lostNeighborType)
     }
 
@@ -343,27 +349,30 @@
         neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE,
                 "001122334455" /* oldMac */))
         handlerThread.waitForIdle(TEST_TIMEOUT_MS)
-        verify(callback, never()).notifyLost(eq(neighbor), anyString())
+        verify(callback, never()).notifyLost(eq(neighbor), anyString(),
+                any(NudEventType::class.java))
     }
 
     @Test
     fun testLoseProvisioning_Ipv4DnsLost() {
-        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS)
+        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, NUD_ORGANIC_FAILED_CRITICAL)
     }
 
     @Test
     fun testLoseProvisioning_Ipv6DnsLost() {
-        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS)
+        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, NUD_ORGANIC_FAILED_CRITICAL)
     }
 
     @Test
     fun testLoseProvisioning_Ipv4GatewayLost() {
-        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY)
+        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY,
+                NUD_ORGANIC_FAILED_CRITICAL)
     }
 
     @Test
     fun testLoseProvisioning_Ipv6GatewayLost() {
-        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY)
+        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY,
+                NUD_ORGANIC_FAILED_CRITICAL)
     }
 
     private fun runNudProbeFailureMetricsTest(
@@ -373,7 +382,7 @@
         ipType: IpType,
         lostNeighborType: NudNeighborType
     ) {
-        runLoseProvisioningTest(lp, lostNeighbor)
+        runLoseProvisioningTest(lp, lostNeighbor, eventType)
         verifyNudFailureMetrics(eventType, ipType, lostNeighborType)
     }
 
@@ -526,7 +535,8 @@
         handlerThread.waitForIdle(TEST_TIMEOUT_MS)
         Thread.sleep(2)
         reachabilityMonitor.probeAll(false /* dueToRoam */)
-        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY)
+        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY,
+                NUD_POST_ROAMING_FAILED_CRITICAL)
 
         verifyNudFailureMetrics(NUD_POST_ROAMING_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY)
     }
@@ -537,7 +547,8 @@
         handlerThread.waitForIdle(TEST_TIMEOUT_MS)
         Thread.sleep(2)
         reachabilityMonitor.probeAll(true /* dueToRoam */)
-        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY)
+        runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY,
+                NUD_CONFIRM_FAILED_CRITICAL)
 
         verifyNudFailureMetrics(NUD_CONFIRM_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY)
     }
@@ -549,7 +560,8 @@
     ) {
         neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE,
                 "1122334455aa" /* newMac */))
-        verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(neighbor), anyString())
+        verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(neighbor), anyString(),
+                eq(eventType))
         verifyNudFailureMetrics(eventType, ipType, NUD_NEIGHBOR_GATEWAY)
     }