Track clat interface status via IpClientLinkObserver for accuracy

Leveraged IpClientLinkObserver.onClatInterfaceStateUpdate() to monitor
clat interface additions/removals, using RTM_NEWLINK/RTM_DELLINK
messages for accurate tracking.

Test: TH
Change-Id: I85c2a8d9e82803a82c1acc93b1fd062d56d520a1
diff --git a/src/android/net/apf/AndroidPacketFilter.java b/src/android/net/apf/AndroidPacketFilter.java
index b1e591e..0aa92ca 100644
--- a/src/android/net/apf/AndroidPacketFilter.java
+++ b/src/android/net/apf/AndroidPacketFilter.java
@@ -86,6 +86,11 @@
      */
     boolean isRunning();
 
+    /**
+     * Indicates whether the clat interface is added or removed.
+     */
+    default void updateClatInterfaceState(boolean add) {}
+
     /** Pause ApfFilter updates for testing purposes. */
     void pause();
 
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java
index 986bd2e..f35487d 100644
--- a/src/android/net/apf/ApfFilter.java
+++ b/src/android/net/apf/ApfFilter.java
@@ -170,6 +170,7 @@
         public int acceptRaMinLft;
         public boolean shouldHandleLightDoze;
         public long minMetricsSessionDurationMs;
+        public boolean hasClatInterface;
     }
 
     /** A wrapper class of {@link SystemClock} to be mocked in unit tests. */
@@ -368,6 +369,7 @@
         mClock = clock;
         mSessionStartMs = mClock.elapsedRealtime();
         mMinMetricsSessionDurationMs = config.minMetricsSessionDurationMs;
+        mHasClat = config.hasClatInterface;
 
         // Now fill the black list from the passed array
         mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
@@ -2379,14 +2381,21 @@
         // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
         final LinkAddress ipv4Address = findIPv4LinkAddress(lp);
         final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null;
-        final int pfx = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
-        final boolean clat = lp.getAllInterfaceNames().contains("v4-" + mInterfaceParams.name);
-        if ((pfx == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address) && (clat == mHasClat)) {
+        final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
+        if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) {
             return;
         }
         mIPv4Address = addr;
-        mIPv4PrefixLength = pfx;
-        mHasClat = clat;
+        mIPv4PrefixLength = prefix;
+        installNewProgramLocked();
+    }
+
+    @Override
+    public synchronized void updateClatInterfaceState(boolean add) {
+        if (mHasClat == add) {
+            return;
+        }
+        mHasClat = add;
         installNewProgramLocked();
     }
 
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index aba4f4e..32b2ea4 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -213,6 +213,8 @@
     private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics();
     private final NetworkQuirkMetrics mNetworkQuirkMetrics;
 
+    private boolean mHasClatInterface = false;
+
     /**
      * Dump all state machine and connectivity packet logs to the specified writer.
      * @param skippedIfaces Interfaces for which logs should not be dumped.
@@ -994,6 +996,10 @@
                         // becomes tied to more things that 464xlat operation.
                         getHandler().post(() -> {
                             mCallback.setNeighborDiscoveryOffload(add ? false : true);
+                            mHasClatInterface = add;
+                            if (mApfFilter != null) {
+                                mApfFilter.updateClatInterfaceState(add);
+                            }
                         });
                     }
                 },
@@ -2551,6 +2557,7 @@
         }
         apfConfig.shouldHandleLightDoze = mApfShouldHandleLightDoze;
         apfConfig.minMetricsSessionDurationMs = mApfCounterPollingIntervalMs;
+        apfConfig.hasClatInterface = mHasClatInterface;
         return mDependencies.maybeCreateApfFilter(mContext, apfConfig, mInterfaceParams,
                 mCallback, mNetworkQuirkMetrics, mUseNewApfFilter);
     }