Support converting IkeConfigRequest to/from PersistableBundle

Bug: 163604823
Test: FrameworksVcnTests(add new tests)
Change-Id: I9a0f7ad91de41749fdf05629bf86bdb010ba13fb
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
index 7f0c328..f54cc03b 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -16,22 +16,31 @@
 
 package android.net.vcn.persistablebundleutils;
 
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
 import android.net.eap.EapSessionConfig;
 import android.net.ipsec.ike.IkeSaProposal;
 import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeSessionParams.ConfigRequestIpv4PcscfServer;
+import android.net.ipsec.ike.IkeSessionParams.ConfigRequestIpv6PcscfServer;
 import android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
 import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
 import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
 import android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
 import android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
+import android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest;
 import android.os.PersistableBundle;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.vcn.util.PersistableBundleUtils;
 
+import java.net.InetAddress;
 import java.security.PrivateKey;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
@@ -53,6 +62,7 @@
     private static final String REMOTE_ID_KEY = "REMOTE_ID_KEY";
     private static final String LOCAL_AUTH_KEY = "LOCAL_AUTH_KEY";
     private static final String REMOTE_AUTH_KEY = "REMOTE_AUTH_KEY";
+    private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY";
     private static final String RETRANS_TIMEOUTS_KEY = "RETRANS_TIMEOUTS_KEY";
     private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY";
     private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY";
@@ -89,13 +99,21 @@
         result.putPersistableBundle(
                 REMOTE_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getRemoteAuthConfig()));
 
+        final List<ConfigRequest> reqList = new ArrayList<>();
+        for (IkeConfigRequest req : params.getConfigurationRequests()) {
+            reqList.add(new ConfigRequest(req));
+        }
+        final PersistableBundle configReqListBundle =
+                PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle);
+        result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle);
+
         result.putIntArray(RETRANS_TIMEOUTS_KEY, params.getRetransmissionTimeoutsMillis());
         result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
         result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
         result.putInt(DPD_DELAY_SEC_KEY, params.getDpdDelaySeconds());
         result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, params.getNattKeepAliveDelaySeconds());
 
-        // TODO: Handle configuration requests and IKE options.
+        // TODO: Handle IKE options.
 
         return result;
     }
@@ -136,7 +154,33 @@
         builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
         builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
 
-        // TODO: Handle configuration requests and IKE options.
+        final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
+        Objects.requireNonNull(configReqListBundle, "Config request list was null");
+        final List<ConfigRequest> reqList =
+                PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
+        for (ConfigRequest req : reqList) {
+            switch (req.type) {
+                case ConfigRequest.IPV4_P_CSCF_ADDRESS:
+                    if (req.address == null) {
+                        builder.addPcscfServerRequest(AF_INET);
+                    } else {
+                        builder.addPcscfServerRequest(req.address);
+                    }
+                    break;
+                case ConfigRequest.IPV6_P_CSCF_ADDRESS:
+                    if (req.address == null) {
+                        builder.addPcscfServerRequest(AF_INET6);
+                    } else {
+                        builder.addPcscfServerRequest(req.address);
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unrecognized config request type: " + req.type);
+            }
+        }
+
+        // TODO: Handle IKE options.
 
         return builder.build();
     }
@@ -389,4 +433,54 @@
             builder.setAuthEap(serverCaCert, eapConfig);
         }
     }
+
+    private static final class ConfigRequest {
+        private static final int IPV4_P_CSCF_ADDRESS = 1;
+        private static final int IPV6_P_CSCF_ADDRESS = 2;
+
+        private static final String TYPE_KEY = "type";
+        private static final String ADDRESS_KEY = "address";
+
+        public final int type;
+
+        // Null when it is an empty request
+        @Nullable public final InetAddress address;
+
+        ConfigRequest(IkeConfigRequest config) {
+            if (config instanceof ConfigRequestIpv4PcscfServer) {
+                type = IPV4_P_CSCF_ADDRESS;
+                address = ((ConfigRequestIpv4PcscfServer) config).getAddress();
+            } else if (config instanceof ConfigRequestIpv6PcscfServer) {
+                type = IPV6_P_CSCF_ADDRESS;
+                address = ((ConfigRequestIpv6PcscfServer) config).getAddress();
+            } else {
+                throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
+            }
+        }
+
+        ConfigRequest(PersistableBundle in) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+
+            type = in.getInt(TYPE_KEY);
+
+            String addressStr = in.getString(ADDRESS_KEY);
+            if (addressStr == null) {
+                address = null;
+            } else {
+                address = InetAddresses.parseNumericAddress(addressStr);
+            }
+        }
+
+        @NonNull
+        public PersistableBundle toPersistableBundle() {
+            final PersistableBundle result = new PersistableBundle();
+
+            result.putInt(TYPE_KEY, type);
+            if (address != null) {
+                result.putString(ADDRESS_KEY, address.getHostAddress());
+            }
+
+            return result;
+        }
+    }
 }
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 720d44a..625f52c 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -16,6 +16,8 @@
 
 package android.net.vcn.persistablebundleutils;
 
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
 import static android.telephony.TelephonyManager.APPTYPE_USIM;
 
 import static org.junit.Assert.assertEquals;
@@ -38,6 +40,8 @@
 
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
 import java.security.cert.CertificateFactory;
@@ -104,6 +108,23 @@
     }
 
     @Test
+    public void testEncodeRecodeParamsWithConfigRequests() throws Exception {
+        final Inet4Address ipv4Address =
+                (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.100");
+        final Inet6Address ipv6Address =
+                (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
+
+        final IkeSessionParams params =
+                createBuilderMinimum()
+                        .addPcscfServerRequest(AF_INET)
+                        .addPcscfServerRequest(AF_INET6)
+                        .addPcscfServerRequest(ipv4Address)
+                        .addPcscfServerRequest(ipv6Address)
+                        .build();
+        verifyPersistableBundleEncodeDecodeIsLossless(params);
+    }
+
+    @Test
     public void testEncodeRecodeParamsWithAuthPsk() throws Exception {
         final IkeSessionParams params = createBuilderMinimum().setAuthPsk("psk".getBytes()).build();
         verifyPersistableBundleEncodeDecodeIsLossless(params);