Merge "Update mBpfMap name for clarity" into main
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 2131597..4f2909c 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -227,23 +227,28 @@
                     throw new IllegalArgumentException("fd can't be null with automatic "
                             + "on/off keepalives");
                 }
-                try {
-                    mFd = Os.dup(ki.mFd);
-                } catch (ErrnoException e) {
-                    Log.e(TAG, "Cannot dup fd: ", e);
-                    throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
-                }
                 mAlarmListener = () -> mConnectivityServiceHandler.obtainMessage(
                         CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
                         .sendToTarget();
             } else {
                 mAutomaticOnOffState = STATE_ALWAYS_ON;
-                // A null fd is acceptable in KeepaliveInfo for backward compatibility of
-                // PacketKeepalive API, but it must never happen with automatic keepalives.
-                // TODO : remove mFd from KeepaliveInfo or from this class.
-                mFd = ki.mFd;
                 mAlarmListener = null;
             }
+
+            // A null fd is acceptable in KeepaliveInfo for backward compatibility of
+            // PacketKeepalive API, but it must never happen with automatic keepalives.
+            // TODO : remove mFd from KeepaliveInfo.
+            mFd = dupFd(ki.mFd);
+        }
+
+        private FileDescriptor dupFd(FileDescriptor fd) throws InvalidSocketException {
+            try {
+                if (fd == null) return null;
+                return Os.dup(fd);
+            } catch (ErrnoException e) {
+                Log.e(TAG, "Cannot dup fd: ", e);
+                throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+            }
         }
 
         @VisibleForTesting
@@ -501,11 +506,7 @@
         final AutomaticOnOffKeepalive autoKi;
         try {
             autoKi = target.withKeepaliveInfo(res.second);
-            // Only automatic keepalives duplicate the fd.
-            if (target.mAutomaticOnOffState != STATE_ALWAYS_ON) {
-                // Close the duplicated fd.
-                target.close();
-            }
+            target.close();
         } catch (InvalidSocketException e) {
             Log.wtf(TAG, "Fail to create AutomaticOnOffKeepalive", e);
             return;
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 2ce186f..ae70767 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -34,6 +34,8 @@
 import static android.net.SocketKeepalive.SUCCESS;
 import static android.net.SocketKeepalive.SUCCESS_PAUSED;
 
+import static com.android.net.module.util.FeatureVersions.FEATURE_CLAT_ADDRESS_TRANSLATE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -59,6 +61,7 @@
 import com.android.connectivity.resources.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.DeviceConfigUtils;
 import com.android.net.module.util.HexDump;
 import com.android.net.module.util.IpUtils;
 
@@ -86,6 +89,9 @@
 
     public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
 
+    private static final String CONFIG_DISABLE_CLAT_ADDRESS_TRANSLATE =
+            "disable_clat_address_translate";
+
     /** Keeps track of keepalive requests. */
     private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
             new HashMap<> ();
@@ -113,7 +119,7 @@
     }
 
     @VisibleForTesting
-    KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController,
+    public KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController,
             Dependencies deps) {
         mTcpController = tcpController;
         mContext = context;
@@ -553,6 +559,8 @@
 
     private KeepaliveInfo handleUpdateKeepaliveForClat(KeepaliveInfo ki)
             throws InvalidSocketException, InvalidPacketException {
+        if (!mDependencies.isAddressTranslationEnabled(mContext)) return ki;
+
         // Translation applies to only NAT-T keepalive
         if (ki.mType != KeepaliveInfo.TYPE_NATT) return ki;
         // Only try to translate address if the packet source address is the clat's source address.
@@ -973,5 +981,20 @@
         public ConnectivityResources createConnectivityResources(@NonNull Context context) {
             return new ConnectivityResources(context);
         }
+
+        /**
+         * Return if keepalive address translation with clat feature is supported or not.
+         *
+         * This is controlled by both isFeatureSupported() and isFeatureEnabled(). The
+         * isFeatureSupported() checks whether device contains the minimal required module
+         * version for FEATURE_CLAT_ADDRESS_TRANSLATE. The isTetheringFeatureForceDisabled()
+         * checks the DeviceConfig flag that can be updated via DeviceConfig push to control
+         * the overall feature.
+         */
+        public boolean isAddressTranslationEnabled(@NonNull Context context) {
+            return DeviceConfigUtils.isFeatureSupported(context, FEATURE_CLAT_ADDRESS_TRANSLATE)
+                    && !DeviceConfigUtils.isTetheringFeatureForceDisabled(
+                            CONFIG_DISABLE_CLAT_ADDRESS_TRANSLATE);
+        }
     }
 }
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 4c2cd59..4dadfee 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -402,6 +402,7 @@
 import com.android.server.connectivity.ClatCoordinator;
 import com.android.server.connectivity.ConnectivityFlags;
 import com.android.server.connectivity.ConnectivityResources;
+import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.MultinetworkPolicyTracker;
 import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies;
 import com.android.server.connectivity.Nat464Xlat;
@@ -410,6 +411,7 @@
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.QosCallbackTracker;
+import com.android.server.connectivity.TcpKeepaliveController;
 import com.android.server.connectivity.UidRangeUtils;
 import com.android.server.connectivity.Vpn;
 import com.android.server.connectivity.VpnProfileStore;
@@ -627,6 +629,7 @@
     @Mock ActivityManager mActivityManager;
     @Mock DestroySocketsWrapper mDestroySocketsWrapper;
     @Mock SubscriptionManager mSubscriptionManager;
+    @Mock KeepaliveTracker.Dependencies mMockKeepaliveTrackerDependencies;
 
     // BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
     // underlying binder calls.
@@ -1898,6 +1901,12 @@
         doReturn(mResources).when(mockResContext).getResources();
         ConnectivityResources.setResourcesContextForTest(mockResContext);
         mDeps = new ConnectivityServiceDependencies(mockResContext);
+        doReturn(true).when(mMockKeepaliveTrackerDependencies)
+                .isAddressTranslationEnabled(mServiceContext);
+        doReturn(new ConnectivityResources(mockResContext)).when(mMockKeepaliveTrackerDependencies)
+                .createConnectivityResources(mServiceContext);
+        doReturn(new int[] {1, 3, 0, 0}).when(mMockKeepaliveTrackerDependencies)
+                .getSupportedKeepalives(mServiceContext);
         mAutoOnOffKeepaliveDependencies =
                 new AutomaticOnOffKeepaliveTrackerDependencies(mServiceContext);
         mService = new ConnectivityService(mServiceContext,
@@ -2297,6 +2306,12 @@
             // Assuming enabled here to focus on ConnectivityService tests.
             return true;
         }
+        public KeepaliveTracker newKeepaliveTracker(@NonNull Context context,
+                @NonNull Handler connectivityserviceHander) {
+            return new KeepaliveTracker(context, connectivityserviceHander,
+                    new TcpKeepaliveController(connectivityserviceHander),
+                    mMockKeepaliveTrackerDependencies);
+        }
     }
 
     private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index f4d3915..b69b042 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -78,7 +78,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.connectivity.resources.R;
 import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker.AutomaticOnOffKeepalive;
 import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
 import com.android.testutils.DevSdkIgnoreRule;
@@ -96,6 +95,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.FileDescriptor;
+import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.nio.ByteBuffer;
@@ -128,7 +128,7 @@
     @Mock AlarmManager mAlarmManager;
     @Mock NetworkAgentInfo mNai;
     @Mock SubscriptionManager mSubscriptionManager;
-
+    @Mock KeepaliveTracker.Dependencies mKeepaliveTrackerDeps;
     KeepaliveStatsTracker mKeepaliveStatsTracker;
     TestKeepaliveTracker mKeepaliveTracker;
     AOOTestHandler mTestHandler;
@@ -267,7 +267,7 @@
 
         TestKeepaliveTracker(@NonNull final Context context, @NonNull final Handler handler,
                 @NonNull final TcpKeepaliveController tcpController) {
-            super(context, handler, tcpController, new Dependencies());
+            super(context, handler, tcpController, mKeepaliveTrackerDeps);
         }
 
         public void setReturnedKeepaliveInfo(@NonNull final KeepaliveInfo ki) {
@@ -336,8 +336,6 @@
                 anyInt() /* pid */, anyInt() /* uid */);
         ConnectivityResources.setResourcesContextForTest(mCtx);
         final Resources mockResources = mock(Resources.class);
-        doReturn(new String[] { "0,3", "3,3" }).when(mockResources)
-                .getStringArray(R.array.config_networkSupportedKeepaliveCount);
         doReturn(mockResources).when(mCtx).getResources();
         doReturn(mNetd).when(mDependencies).getNetd();
         doReturn(mAlarmManager).when(mDependencies).getAlarmManager(any());
@@ -345,6 +343,10 @@
                 .getFwmarkForNetwork(TEST_NETID);
 
         doNothing().when(mDependencies).sendRequest(any(), any());
+        doReturn(true).when(mKeepaliveTrackerDeps).isAddressTranslationEnabled(mCtx);
+        doReturn(new ConnectivityResources(mCtx)).when(mKeepaliveTrackerDeps)
+                .createConnectivityResources(mCtx);
+        doReturn(new int[] {3, 0, 0, 3}).when(mKeepaliveTrackerDeps).getSupportedKeepalives(mCtx);
 
         mHandlerThread = new HandlerThread("KeepaliveTrackerTest");
         mHandlerThread.start();
@@ -659,6 +661,25 @@
     }
 
     @Test
+    public void testStartNattKeepalive_addressTranslationOnClatNotSupported() throws Exception {
+        // Disable address translation feature and verify the behavior
+        doReturn(false).when(mKeepaliveTrackerDeps).isAddressTranslationEnabled(mCtx);
+
+        setupTestNaiForClat(InetAddresses.parseNumericAddress("2001:db8::1"),
+                InetAddresses.parseNumericAddress("2001:db8::2"));
+
+        doStartNattKeepalive();
+        final ArgumentCaptor<NattKeepalivePacketData> kpdCaptor =
+                ArgumentCaptor.forClass(NattKeepalivePacketData.class);
+        verify(mNai).onStartNattSocketKeepalive(
+                eq(TEST_SLOT), eq(TEST_KEEPALIVE_INTERVAL_SEC), kpdCaptor.capture());
+        // Verify that address translation is not triggered so the addresses are still v4.
+        final NattKeepalivePacketData kpd = kpdCaptor.getValue();
+        assertTrue(kpd.getSrcAddress() instanceof Inet4Address);
+        assertTrue(kpd.getDstAddress() instanceof Inet4Address);
+    }
+
+    @Test
     public void testStartNattKeepalive_addressTranslationOnClat() throws Exception {
         final InetAddress v6AddrSrc = InetAddresses.parseNumericAddress("2001:db8::1");
         final InetAddress v6AddrDst = InetAddresses.parseNumericAddress("2001:db8::2");