Allow NATT Keepalive delay to be configured.

This CL allows callers to configure the NATT Keepalive delay in seconds
through IkeSessionParams.Builder. The configured delay must be between
10 and 3600 seconds, inclusive. This range is mandated by
SocketKeepalive.

Bug: 158036773
Test: atest FrameworksIkeTests
Change-Id: I9e19c0d6e056b5994889fb6d703d4c8799ecbec2
diff --git a/src/java/android/net/ipsec/ike/IkeSessionParams.java b/src/java/android/net/ipsec/ike/IkeSessionParams.java
index 698d379..b093219 100644
--- a/src/java/android/net/ipsec/ike/IkeSessionParams.java
+++ b/src/java/android/net/ipsec/ike/IkeSessionParams.java
@@ -179,6 +179,13 @@
     @VisibleForTesting static final int IKE_DPD_DELAY_SEC_DEFAULT = 120; // 2 minutes
 
     /** @hide */
+    @VisibleForTesting static final int IKE_NATT_KEEPALIVE_DELAY_SEC_MIN = 10;
+    /** @hide */
+    @VisibleForTesting static final int IKE_NATT_KEEPALIVE_DELAY_SEC_MAX = 3600;
+    /** @hide */
+    @VisibleForTesting static final int IKE_NATT_KEEPALIVE_DELAY_SEC_DEFAULT = 10;
+
+    /** @hide */
     @VisibleForTesting static final int IKE_RETRANS_TIMEOUT_MS_MIN = 500;
     /** @hide */
     @VisibleForTesting
@@ -202,6 +209,7 @@
     private static final String HARD_LIFETIME_SEC_KEY = "mHardLifetimeSec";
     private static final String SOFT_LIFETIME_SEC_KEY = "mSoftLifetimeSec";
     private static final String DPD_DELAY_SEC_KEY = "mDpdDelaySec";
+    private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "mNattKeepaliveDelaySec";
     private static final String IS_IKE_FRAGMENT_SUPPORTED_KEY = "mIsIkeFragmentationSupported";
 
     @NonNull private final String mServerHostname;
@@ -229,6 +237,8 @@
 
     private final int mDpdDelaySec;
 
+    private final int mNattKeepaliveDelaySec;
+
     private final boolean mIsIkeFragmentationSupported;
 
     private IkeSessionParams(
@@ -247,6 +257,7 @@
             int hardLifetimeSec,
             int softLifetimeSec,
             int dpdDelaySec,
+            int nattKeepaliveDelaySec,
             boolean isIkeFragmentationSupported) {
         mServerHostname = serverHostname;
         mNetwork = network;
@@ -273,6 +284,8 @@
 
         mDpdDelaySec = dpdDelaySec;
 
+        mNattKeepaliveDelaySec = nattKeepaliveDelaySec;
+
         mIsIkeFragmentationSupported = isIkeFragmentationSupported;
     }
 
@@ -341,6 +354,7 @@
         builder.setLifetimeSeconds(
                 in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
         builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
+        builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
 
         // Fragmentation policy is not configurable. IkeSessionParams will always be constructed to
         // support fragmentation.
@@ -386,6 +400,7 @@
         result.putInt(HARD_LIFETIME_SEC_KEY, mHardLifetimeSec);
         result.putInt(SOFT_LIFETIME_SEC_KEY, mSoftLifetimeSec);
         result.putInt(DPD_DELAY_SEC_KEY, mDpdDelaySec);
+        result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, mNattKeepaliveDelaySec);
         result.putBoolean(IS_IKE_FRAGMENT_SUPPORTED_KEY, mIsIkeFragmentationSupported);
 
         return result;
@@ -488,6 +503,18 @@
     }
 
     /**
+     * Retrieves the Network Address Translation Traversal (NATT) keepalive delay in seconds
+     *
+     * @hide
+     */
+    // Use "second" because smaller unit does not make sense for a NATT Keepalive delay.
+    @SuppressLint("MethodNameUnits")
+    @IntRange(from = IKE_NATT_KEEPALIVE_DELAY_SEC_MIN, to = IKE_NATT_KEEPALIVE_DELAY_SEC_MAX)
+    public int getNattKeepAliveDelaySeconds() {
+        return mNattKeepaliveDelaySec;
+    }
+
+    /**
      * Retrieves the relative retransmission timeout list in milliseconds
      *
      * <p>@see {@link Builder#setRetransmissionTimeoutsMillis(int[])}
@@ -1112,6 +1139,8 @@
 
         private int mDpdDelaySec = IKE_DPD_DELAY_SEC_DEFAULT;
 
+        private int mNattKeepaliveDelaySec = IKE_NATT_KEEPALIVE_DELAY_SEC_DEFAULT;
+
         private final boolean mIsIkeFragmentationSupported = true;
 
         /**
@@ -1517,6 +1546,29 @@
         }
 
         /**
+         * Sets the Network Address Translation Traversal (NATT) keepalive delay in seconds.
+         *
+         * @param nattKeepaliveDelaySeconds number of seconds between keepalive packet
+         *     transmissions. Defaults to 10 seconds. MUST be a value from 10 seconds to 3600
+         *     seconds, inclusive.
+         * @return Builder this, to facilitate chaining.
+         * @hide
+         */
+        @NonNull
+        public Builder setNattKeepAliveDelaySeconds(
+                @IntRange(
+                                from = IKE_NATT_KEEPALIVE_DELAY_SEC_MIN,
+                                to = IKE_NATT_KEEPALIVE_DELAY_SEC_MAX)
+                        int nattKeepaliveDelaySeconds) {
+            if (nattKeepaliveDelaySeconds < IKE_NATT_KEEPALIVE_DELAY_SEC_MIN
+                    || nattKeepaliveDelaySeconds > IKE_NATT_KEEPALIVE_DELAY_SEC_MAX) {
+                throw new IllegalArgumentException("Invalid NATT keepalive delay value");
+            }
+            mNattKeepaliveDelaySec = nattKeepaliveDelaySeconds;
+            return this;
+        }
+
+        /**
          * Sets the retransmission timeout list in milliseconds.
          *
          * <p>Configures the retransmission by providing an array of relative retransmission
@@ -1666,6 +1718,7 @@
                     mHardLifetimeSec,
                     mSoftLifetimeSec,
                     mDpdDelaySec,
+                    mNattKeepaliveDelaySec,
                     mIsIkeFragmentationSupported);
         }
 
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index 60f1c6c..1df255f 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -242,8 +242,6 @@
     @VisibleForTesting
     static final long TEMP_FAILURE_RETRY_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(5L);
 
-    @VisibleForTesting static final int NATT_KEEPALIVE_DELAY_SECONDS = 10;
-
     // Package private IKE exchange subtypes describe the specific function of a IKE
     // request/response exchange. It helps IkeSessionStateMachine to do message validation according
     // to the subtype specific rules.
@@ -3365,7 +3363,7 @@
                 new IkeNattKeepalive(
                         mContext,
                         mConnectivityManager,
-                        NATT_KEEPALIVE_DELAY_SECONDS,
+                        mIkeSessionParams.getNattKeepAliveDelaySeconds(),
                         (Inet4Address) mLocalAddress,
                         (Inet4Address) mRemoteAddress,
                         ((IkeUdpEncapSocket) mIkeSocket).getUdpEncapsulationSocket(),
diff --git a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java b/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java
index 7285f00..f08bdfb 100644
--- a/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java
+++ b/tests/iketests/src/java/android/net/ipsec/ike/IkeSessionParamsTest.java
@@ -52,6 +52,7 @@
 import android.net.ConnectivityManager;
 import android.net.InetAddresses;
 import android.net.Network;
+import android.net.SocketKeepalive;
 import android.net.eap.EapSessionConfig;
 import android.net.ipsec.ike.ike3gpp.Ike3gppExtension;
 import android.net.ipsec.ike.ike3gpp.Ike3gppExtension.Ike3gppCallback;
@@ -359,6 +360,43 @@
     }
 
     @Test
+    public void testBuildWithPskAndNattKeepaliveDelay() throws Exception {
+        final int nattKeepaliveDelay = 100;
+
+        IkeSessionParams sessionParams =
+                buildWithPskCommon(REMOTE_IPV4_HOST_ADDRESS)
+                        .setNattKeepAliveDelaySeconds(nattKeepaliveDelay)
+                        .build();
+
+        // Verify NATT keepalive delay
+        assertEquals(nattKeepaliveDelay, sessionParams.getNattKeepAliveDelaySeconds());
+    }
+
+    @Test
+    public void testNattKeepaliveRange() {
+        assertTrue(
+                SocketKeepalive.MIN_INTERVAL_SEC
+                        <= IkeSessionParams.IKE_NATT_KEEPALIVE_DELAY_SEC_MIN);
+        assertTrue(
+                SocketKeepalive.MAX_INTERVAL_SEC
+                        >= IkeSessionParams.IKE_NATT_KEEPALIVE_DELAY_SEC_MAX);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuildWithNattKeepaliveDelayTooShort() throws Exception {
+        final int lowNattKeepaliveDelay = 1;
+        new IkeSessionParams.Builder(mMockConnectManager)
+                .setNattKeepAliveDelaySeconds(lowNattKeepaliveDelay);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuildWithNattKeepaliveDelayTooLong() throws Exception {
+        final int highNattKeepaliveDelay = 9999;
+        new IkeSessionParams.Builder(mMockConnectManager)
+                .setNattKeepAliveDelaySeconds(highNattKeepaliveDelay);
+    }
+
+    @Test
     public void testBuildWithPskAndRetransmission() throws Exception {
         final int[] retransmissionTimeoutList = new int[] {1000, 2000, 3000, 4000};
 
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
index 4a7d8ea..ccd731d 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
@@ -339,6 +339,8 @@
 
     private static final byte[] COOKIE2_DATA = new byte[COOKIE2_DATA_LEN];
 
+    private static final int NATT_KEEPALIVE_DELAY = 20;
+
     static {
         new Random().nextBytes(COOKIE2_DATA);
     }
@@ -6051,4 +6053,26 @@
                 mIkeSessionStateMachine.mMobikeLocalInfo,
                 mIkeSessionStateMachine.getCurrentState());
     }
+
+    @Test
+    public void testNattKeepaliveDelayForHardwareKeepaliveImpl() throws Exception {
+        IkeSessionParams sessionParams =
+                buildIkeSessionParamsCommon()
+                        .setAuthPsk(mPsk)
+                        .setNattKeepAliveDelaySeconds(NATT_KEEPALIVE_DELAY)
+                        .build();
+
+        // Restart IkeSessionStateMachine with NATT Keepalive delay configured
+        setupFirstIkeSa();
+        mIkeSessionStateMachine.quitNow();
+        mIkeSessionStateMachine = makeAndStartIkeSession(sessionParams);
+
+        mIkeSessionStateMachine.openSession();
+        mLooper.dispatchAll();
+
+        mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, makeIkeInitResponse());
+        mLooper.dispatchAll();
+
+        verify(mMockSocketKeepalive).start(eq(NATT_KEEPALIVE_DELAY));
+    }
 }