Keep alive improvements

- Use new network capabilities instead of old network capabilities
  for fetching the keep alive timer, when updating the ike underlying
  network with onNetworkSetByUser().

Flag: Flag EXEMPT BugFix
Bug: 396209794
Test: atest com.android.internal.net.ipsec.test.ike.net.IkeConnectionControllerTest#testKeepaliveDelayUpdatesOnNetworkSwitchWithAutoKeeplive
Change-Id: Ib5eccc1b9ae43d16f434f6aed7d5f42c44c0f9e3
diff --git a/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java b/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java
index 847d27b..64c381a 100644
--- a/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java
+++ b/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java
@@ -182,6 +182,7 @@
     //Must only be touched on the IkeSessionStateMachine thread.
     private Network mUnderpinnedNetwork;
 
+    private int mKeepaliveDelaySeconds;
     private IkeNattKeepalive mIkeNattKeepalive;
 
     private static final SparseArray<String> NAT_STATUS_TO_STR;
@@ -213,6 +214,7 @@
         mUseCallerConfiguredNetwork = config.ikeParams.getConfiguredNetwork() != null;
         mIpVersion = config.ikeParams.getIpVersion();
         mEncapType = config.ikeParams.getEncapType();
+        mKeepaliveDelaySeconds = config.ikeParams.getNattKeepAliveDelaySeconds();
         mDscp = config.ikeParams.getDscp();
         mUnderpinnedNetwork = null;
 
@@ -637,6 +639,12 @@
         return Collections.unmodifiableSet(mIkeSaRecords);
     }
 
+    /** Returns the keepalive config */
+    @VisibleForTesting
+    public IkeAlarmConfig getKeepaliveAlarmConfig() {
+        return mKeepaliveAlarmConfig;
+    }
+
     /**
      * Updates the underlying network
      *
@@ -691,15 +699,7 @@
 
         mIpVersion = ipVersion;
         mEncapType = encapType;
-
-        if (keepaliveDelaySeconds == IkeSessionParams.NATT_KEEPALIVE_INTERVAL_AUTO) {
-            keepaliveDelaySeconds = getKeepaliveDelaySec(mIkeContext, mIkeParams, mNc);
-        }
-        final long keepaliveDelayMs = TimeUnit.SECONDS.toMillis(keepaliveDelaySeconds);
-        if (keepaliveDelayMs != mKeepaliveAlarmConfig.delayMs) {
-            mKeepaliveAlarmConfig = mKeepaliveAlarmConfig.buildCopyWithDelayMs(keepaliveDelayMs);
-            restartKeepaliveIfRunning();
-        }
+        mKeepaliveDelaySeconds = keepaliveDelaySeconds;
 
         // Switch to monitor a new network. This call is never expected to trigger a callback
         mNetworkCallback.setNetwork(network, linkProperties, networkCapabilities);
@@ -1233,6 +1233,23 @@
         mNetwork = network;
         mNc = networkCapabilities;
 
+        try {
+            if (mKeepaliveDelaySeconds == IkeSessionParams.NATT_KEEPALIVE_INTERVAL_AUTO) {
+                mKeepaliveDelaySeconds = getKeepaliveDelaySec(mIkeContext, mIkeParams, mNc);
+            }
+
+            final long keepaliveDelayMs = TimeUnit.SECONDS.toMillis(mKeepaliveDelaySeconds);
+
+            if (keepaliveDelayMs != mKeepaliveAlarmConfig.delayMs) {
+                mKeepaliveAlarmConfig =
+                        mKeepaliveAlarmConfig.buildCopyWithDelayMs(keepaliveDelayMs);
+                restartKeepaliveIfRunning();
+            }
+        } catch (IkeException e) {
+            mCallback.onError(wrapAsIkeException(e));
+            return;
+        }
+
         // If there is no local address on the Network, report a fatal error and return
         if (!hasLocalIpV4Address(linkProperties) && !linkProperties.hasGlobalIpv6Address()) {
             mCallback.onError(
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java
index 52324fd..85a95ba 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java
@@ -112,6 +112,7 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
 
 public class IkeConnectionControllerTest extends IkeSessionTestBase {
     @Rule public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
@@ -217,7 +218,9 @@
 
         mIkeConnectionCtrl = buildIkeConnectionCtrl();
         mIkeConnectionCtrl.setUp();
-        verify(mMockIkeParams).getNattKeepAliveDelaySeconds();
+        // getNattKeepAliveDelaySeconds is called once in IkeConnectionController#setUp() and once
+        // at constructor of IkeConnectionController
+        verify(mMockIkeParams, times(2)).getNattKeepAliveDelaySeconds();
         mIkeConnectionCtrl.registerIkeSaRecord(mMockIkeSaRecord);
     }
 
@@ -1152,10 +1155,14 @@
         IkeNetworkCallbackBase callback = enableMobilityAndReturnCb(true /* isDefaultNetwork */);
         onNetworkSetByUserWithDefaultParams(mIkeConnectionCtrl, newNetwork);
 
-        // hasIkeOption and getNattKeepAliveDelaySeconds were already called once by
-        // IkeConnectionController#setUp() so check they were called a second time
-        verify(mMockIkeParams, times(2)).hasIkeOption(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES);
-        verify(mMockIkeParams, times(2)).getNattKeepAliveDelaySeconds();
+        // hasIkeOption was already called once by IkeConnectionController#setUp() so check it was
+        // called second time
+        verify(mMockIkeParams, times(2)).hasIkeOption(
+                IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES);
+        // getNattKeepAliveDelaySeconds was already called once each by
+        // IkeConnectionController#setUp() and at constructor of IkeConnectionController, so check
+        // it was called third time
+        verify(mMockIkeParams, times(3)).getNattKeepAliveDelaySeconds();
         verifyNetworkAndAddressesAfterMobilityEvent(
                 newNetwork, UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS, callback);
         verify(mMockConnectionCtrlCb).onUnderlyingNetworkUpdated();
@@ -1782,4 +1789,46 @@
         mIkeConnectionCtrl.dump(new PrintWriter(stringWriter), "");
         assertFalse(stringWriter.toString().isEmpty());
     }
+
+    private Network createMockNetwork(int transportType) throws Exception {
+        Network network = mock(Network.class);
+        NetworkCapabilities caps = mock(NetworkCapabilities.class);
+        when(caps.hasTransport(transportType)).thenReturn(true);
+        when(mMockConnectManager.getNetworkCapabilities(network)).thenReturn(caps);
+
+        setupLocalAddressForNetwork(network, LOCAL_ADDRESS);
+        setupRemoteAddressForNetwork(network, REMOTE_ADDRESS);
+
+        return network;
+    }
+
+    @Test
+    public void testKeepaliveDelayUpdatesOnNetworkSwitchWithAutoKeeplive() throws Exception {
+        mIkeConnectionCtrl.enableMobility();
+
+        when(mMockIkeParams.getNattKeepAliveDelaySeconds()).thenReturn(200);
+        doReturn(true).when(mMockIkeParams).hasIkeOption(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES);
+
+        // Migrate IKE to a mocked Wifi netwpork
+        onNetworkSetByUserWithDefaultParams(mIkeConnectionCtrl, createMockNetwork(TRANSPORT_WIFI));
+
+        // Validate the keep alive time set for wifi
+        assertEquals(
+                TimeUnit.SECONDS.toMillis(AUTO_KEEPALIVE_DELAY_SEC_WIFI),
+                mIkeConnectionCtrl.getKeepaliveAlarmConfig().delayMs);
+
+        final int carrierConfigCellDelaySeconds = 100;
+        doReturn(carrierConfigCellDelaySeconds)
+                .when(mIkeContext)
+                .getDeviceConfigPropertyInt(anyString(), anyInt(), anyInt(), anyInt());
+
+        // Migrate IKE to a mocked cell netwpork
+        onNetworkSetByUserWithDefaultParams(
+                mIkeConnectionCtrl, createMockNetwork(TRANSPORT_CELLULAR));
+
+        // Validate the keep alive time set for celll
+        assertEquals(
+                TimeUnit.SECONDS.toMillis(carrierConfigCellDelaySeconds),
+                mIkeConnectionCtrl.getKeepaliveAlarmConfig().delayMs);
+    }
 }