Support encoding config payload wth ipv4 address

Bug: 140644755
Test: atest FrameworksIkeTests(new tests added)
Change-Id: Idb63dd01aff31b8d9239d10cfe062b6c7492f080
diff --git a/src/java/com/android/ike/ikev2/message/IkeConfigPayload.java b/src/java/com/android/ike/ikev2/message/IkeConfigPayload.java
index d79880a..766b5c1 100644
--- a/src/java/com/android/ike/ikev2/message/IkeConfigPayload.java
+++ b/src/java/com/android/ike/ikev2/message/IkeConfigPayload.java
@@ -42,6 +42,7 @@
  */
 public final class IkeConfigPayload extends IkePayload {
     private static final int CONFIG_HEADER_RESERVED_LEN = 3;
+    private static final int CONFIG_HEADER_LEN = 4;
 
     // TODO: Move these constant definitions to API
     @Retention(RetentionPolicy.SOURCE)
@@ -107,6 +108,10 @@
 
     /** This class represents common information of all Configuration Attributes. */
     public abstract static class ConfigAttribute {
+        private static final int ATTRIBUTE_TYPE_MASK = 0x7fff;
+
+        private static final int ATTRIBUTE_HEADER_LEN = 4;
+
         protected static final int VALUE_LEN_NOT_INCLUDED = 0;
         protected static final int IPV4_ADDRESS_LEN = 4;
         protected static final int IPV6_ADDRESS_LEN = 16;
@@ -153,9 +158,23 @@
             return configList;
         }
 
-        protected abstract boolean isLengthValid(int length);
+        /** Encode attribute to ByteBuffer. */
+        public void encodeAttributeToByteBuffer(ByteBuffer buffer) {
+            buffer.putShort((short) (attributeType & ATTRIBUTE_TYPE_MASK))
+                    .putShort((short) getValueLength());
+            encodeValueToByteBuffer(buffer);
+        }
 
-        // TODO: Add encoding method
+        /** Get attribute length. */
+        public int getAttributeLen() {
+            return ATTRIBUTE_HEADER_LEN + getValueLength();
+        }
+
+        protected abstract void encodeValueToByteBuffer(ByteBuffer buffer);
+
+        protected abstract int getValueLength();
+
+        protected abstract boolean isLengthValid(int length);
     }
 
     /**
@@ -192,6 +211,21 @@
         }
 
         @Override
+        protected void encodeValueToByteBuffer(ByteBuffer buffer) {
+            if (address == null) {
+                buffer.put(new byte[0]);
+                return;
+            }
+
+            buffer.put(address.getAddress());
+        }
+
+        @Override
+        protected int getValueLength() {
+            return address == null ? 0 : IPV4_ADDRESS_LEN;
+        }
+
+        @Override
         protected boolean isLengthValid(int length) {
             return length == IPV4_ADDRESS_LEN || length == VALUE_LEN_NOT_INCLUDED;
         }
@@ -227,8 +261,12 @@
      */
     @Override
     protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
-        // TODO: Implement it.
-        throw new UnsupportedOperationException();
+        encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
+        byteBuffer.put((byte) configType).put(new byte[CONFIG_HEADER_RESERVED_LEN]);
+
+        for (ConfigAttribute attr : recognizedAttributeList) {
+            attr.encodeAttributeToByteBuffer(byteBuffer);
+        }
     }
 
     /**
@@ -238,8 +276,13 @@
      */
     @Override
     protected int getPayloadLength() {
-        // TODO: Implement it.
-        throw new UnsupportedOperationException();
+        int len = GENERIC_HEADER_LENGTH + CONFIG_HEADER_LEN;
+
+        for (ConfigAttribute attr : recognizedAttributeList) {
+            len += attr.getAttributeLen();
+        }
+
+        return len;
     }
 
     /**
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeConfigPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeConfigPayloadTest.java
index 291d413..4cdd132 100644
--- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeConfigPayloadTest.java
+++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeConfigPayloadTest.java
@@ -20,13 +20,18 @@
 import static com.android.ike.ikev2.message.IkeConfigPayload.CONFIG_TYPE_REPLY;
 import static com.android.ike.ikev2.message.IkeConfigPayload.CONFIG_TYPE_REQUEST;
 import static com.android.ike.ikev2.message.IkePayload.PAYLOAD_TYPE_CP;
+import static com.android.ike.ikev2.message.IkePayload.PAYLOAD_TYPE_NOTIFY;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import com.android.ike.TestUtils;
 import com.android.ike.ikev2.exceptions.InvalidSyntaxException;
@@ -39,6 +44,7 @@
 
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
+import java.util.LinkedList;
 import java.util.List;
 
 public final class IkeConfigPayloadTest {
@@ -54,6 +60,17 @@
 
     private static final Inet4Address IPV4_ADDRESS =
             (Inet4Address) (InetAddressUtils.parseNumericAddress("10.10.10.1"));
+    private static final byte[] IPV4_ADDRESS_ATTRIBUTE_WITH_VALUE =
+            TestUtils.hexStringToByteArray("000100040a0a0a01");
+    private static final byte[] IPV4_ADDRESS_ATTRIBUTE_WITHOUT_VALUE =
+            TestUtils.hexStringToByteArray("00010000");
+
+    private static final byte[] IPV6_ADDRESS_ATTRIBUTE_WITHOUT_VALUE =
+            TestUtils.hexStringToByteArray("00080000");
+    private static final byte[] IPV4_DNS_ATTRIBUTE_WITHOUT_VALUE =
+            TestUtils.hexStringToByteArray("00030000");
+    private static final byte[] IPV6_DNS_ATTRIBUTE_WITHOUT_VALUE =
+            TestUtils.hexStringToByteArray("000a0000");
 
     private IkeConfigPayload verifyDecodeHeaderAndGetPayload(
             IkePayload payload, int expectedConfigType) {
@@ -86,8 +103,6 @@
                 (ConfigAttributeIpv4Address) recognizedAttributeList.get(0);
         assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, attributeIp4Address.attributeType);
         assertNull(attributeIp4Address.address);
-
-        // TODO: Verify decoded other types of attributes when they are supported.
     }
 
     @Test
@@ -109,19 +124,42 @@
                 (ConfigAttributeIpv4Address) recognizedAttributeList.get(0);
         assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, attributeIp4Address.attributeType);
         assertEquals(IPV4_ADDRESS, attributeIp4Address.address);
+    }
 
-        // TODO: Verify decoded other types of attributes when they are supported.
+    private ConfigAttribute makeMockAttribute(byte[] encodedAttribute) {
+        ConfigAttribute mockAttribute = mock(ConfigAttribute.class);
+
+        when(mockAttribute.getAttributeLen()).thenReturn(encodedAttribute.length);
+
+        doAnswer(
+                (invocation) -> {
+                    ByteBuffer buffer = (ByteBuffer) invocation.getArguments()[0];
+                    buffer.put(encodedAttribute);
+                    return null;
+                })
+                .when(mockAttribute)
+                .encodeAttributeToByteBuffer(any(ByteBuffer.class));
+
+        return mockAttribute;
     }
 
     @Test
-    public void testBuildOutboundConfig() throws Exception {
-        List<ConfigAttribute> mockAttributeList = mock(List.class);
+    public void testBuildAndEncodeOutboundConfig() throws Exception {
+        List<ConfigAttribute> mockAttributeList = new LinkedList<>();
+        mockAttributeList.add(makeMockAttribute(IPV4_ADDRESS_ATTRIBUTE_WITHOUT_VALUE));
+        mockAttributeList.add(makeMockAttribute(IPV6_ADDRESS_ATTRIBUTE_WITHOUT_VALUE));
+        mockAttributeList.add(makeMockAttribute(IPV4_DNS_ATTRIBUTE_WITHOUT_VALUE));
+        mockAttributeList.add(makeMockAttribute(IPV6_DNS_ATTRIBUTE_WITHOUT_VALUE));
         IkeConfigPayload configPayload = new IkeConfigPayload(false /*isReply*/, mockAttributeList);
 
         assertEquals(PAYLOAD_TYPE_CP, configPayload.payloadType);
         assertFalse(configPayload.isCritical);
         assertEquals(CONFIG_TYPE_REQUEST, configPayload.configType);
         assertEquals(mockAttributeList, configPayload.recognizedAttributeList);
+
+        ByteBuffer buffer = ByteBuffer.allocate(configPayload.getPayloadLength());
+        configPayload.encodeToByteBuffer(PAYLOAD_TYPE_NOTIFY, buffer);
+        assertArrayEquals(CONFIG_REQ_PAYLOAD, buffer.array());
     }
 
     @Test
@@ -154,5 +192,28 @@
         }
     }
 
-    // TODO: Testing encoding attributes
+    @Test
+    public void testEncodeIpv4AddressWithValue() throws Exception {
+        ConfigAttributeIpv4Address attributeIp4Address =
+                new ConfigAttributeIpv4Address(IPV4_ADDRESS);
+
+        assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, attributeIp4Address.attributeType);
+        assertEquals(IPV4_ADDRESS, attributeIp4Address.address);
+
+        ByteBuffer buffer = ByteBuffer.allocate(attributeIp4Address.getAttributeLen());
+        attributeIp4Address.encodeAttributeToByteBuffer(buffer);
+        assertArrayEquals(IPV4_ADDRESS_ATTRIBUTE_WITH_VALUE, buffer.array());
+    }
+
+    @Test
+    public void testEncodeIpv4AddressWithoutValue() throws Exception {
+        ConfigAttributeIpv4Address attributeIp4Address = new ConfigAttributeIpv4Address();
+
+        assertEquals(CONFIG_ATTR_INTERNAL_IP4_ADDRESS, attributeIp4Address.attributeType);
+        assertNull(attributeIp4Address.address);
+
+        ByteBuffer buffer = ByteBuffer.allocate(attributeIp4Address.getAttributeLen());
+        attributeIp4Address.encodeAttributeToByteBuffer(buffer);
+        assertArrayEquals(IPV4_ADDRESS_ATTRIBUTE_WITHOUT_VALUE, buffer.array());
+    }
 }