Merge "Support IPv4/6 address type in Struct."
diff --git a/staticlibs/device/com/android/net/module/util/HexDump.java b/staticlibs/device/com/android/net/module/util/HexDump.java
index d8c0fb5..6d36487 100644
--- a/staticlibs/device/com/android/net/module/util/HexDump.java
+++ b/staticlibs/device/com/android/net/module/util/HexDump.java
@@ -20,9 +20,6 @@
 
 /**
  * Hex utility functions.
- *
- * A copy from frameworks/base/core/java/com/android/internal/util/HexDump.java, to be shared
- * by multiple modules.
  */
 public class HexDump {
     private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
diff --git a/staticlibs/device/com/android/net/module/util/Struct.java b/staticlibs/device/com/android/net/module/util/Struct.java
index afc2e64..f11a5ac 100644
--- a/staticlibs/device/com/android/net/module/util/Struct.java
+++ b/staticlibs/device/com/android/net/module/util/Struct.java
@@ -27,6 +27,10 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -99,21 +103,23 @@
  */
 public class Struct {
     public enum Type {
-        U8,        // unsigned byte,  size = 1 byte
-        U16,       // unsigned short, size = 2 bytes
-        U32,       // unsigned int,   size = 4 bytes
-        U63,       // unsigned long(MSB: 0), size = 8 bytes
-        U64,       // unsigned long,  size = 8 bytes
-        S8,        // signed byte,    size = 1 byte
-        S16,       // signed short,   size = 2 bytes
-        S32,       // signed int,     size = 4 bytes
-        S64,       // signed long,    size = 8 bytes
-        UBE16,     // unsigned short in network order, size = 2 bytes
-        UBE32,     // unsigned int in network order,   size = 4 bytes
-        UBE63,     // unsigned long(MSB: 0) in network order, size = 8 bytes
-        UBE64,     // unsigned long in network order,  size = 8 bytes
-        ByteArray, // byte array with predefined length
-        EUI48,     // IEEE Extended Unique Identifier, a 48-bits long MAC address in network order
+        U8,          // unsigned byte,  size = 1 byte
+        U16,         // unsigned short, size = 2 bytes
+        U32,         // unsigned int,   size = 4 bytes
+        U63,         // unsigned long(MSB: 0), size = 8 bytes
+        U64,         // unsigned long,  size = 8 bytes
+        S8,          // signed byte,    size = 1 byte
+        S16,         // signed short,   size = 2 bytes
+        S32,         // signed int,     size = 4 bytes
+        S64,         // signed long,    size = 8 bytes
+        UBE16,       // unsigned short in network order, size = 2 bytes
+        UBE32,       // unsigned int in network order,   size = 4 bytes
+        UBE63,       // unsigned long(MSB: 0) in network order, size = 8 bytes
+        UBE64,       // unsigned long in network order,  size = 8 bytes
+        ByteArray,   // byte array with predefined length
+        EUI48,       // IEEE Extended Unique Identifier, a 48-bits long MAC address in network order
+        Ipv4Address, // IPv4 address in network order
+        Ipv6Address, // IPv6 address in network order
     }
 
     /**
@@ -186,6 +192,12 @@
             case EUI48:
                 if (fieldType == MacAddress.class) return;
                 break;
+            case Ipv4Address:
+                if (fieldType == Inet4Address.class) return;
+                break;
+            case Ipv6Address:
+                if (fieldType == Inet6Address.class) return;
+                break;
             default:
                 throw new IllegalArgumentException("Unknown type" + annotation.type());
         }
@@ -223,6 +235,12 @@
             case EUI48:
                 length = 6;
                 break;
+            case Ipv4Address:
+                length = 4;
+                break;
+            case Ipv6Address:
+                length = 16;
+                break;
             default:
                 throw new IllegalArgumentException("Unknown type" + annotation.type());
         }
@@ -388,6 +406,17 @@
                 buf.get(macAddress);
                 value = MacAddress.fromBytes(macAddress);
                 break;
+            case Ipv4Address:
+            case Ipv6Address:
+                final boolean isIpv6 = (fieldInfo.annotation.type() == Type.Ipv6Address);
+                final byte[] address = new byte[isIpv6 ? 16 : 4];
+                buf.get(address);
+                try {
+                    value = InetAddress.getByAddress(address);
+                } catch (UnknownHostException e) {
+                    throw new IllegalArgumentException("illegal length of IP address", e);
+                }
+                break;
             default:
                 throw new IllegalArgumentException("Unknown type:" + fieldInfo.annotation.type());
         }
@@ -461,6 +490,11 @@
                 final byte[] macAddress = ((MacAddress) value).toByteArray();
                 output.put(macAddress);
                 break;
+            case Ipv4Address:
+            case Ipv6Address:
+                final byte[] address = ((InetAddress) value).getAddress();
+                output.put(address);
+                break;
             default:
                 throw new IllegalArgumentException("Unknown type:" + fieldInfo.annotation.type());
         }
@@ -671,8 +705,11 @@
             }
             if (value == null) {
                 sb.append("null");
-            } else if (fieldInfos[i].field.getType() == byte[].class) {
+            } else if (fieldInfos[i].annotation.type() == Type.ByteArray) {
                 sb.append("0x").append(HexDump.toHexString((byte[]) value));
+            } else if (fieldInfos[i].annotation.type() == Type.Ipv4Address
+                    || fieldInfos[i].annotation.type() == Type.Ipv6Address) {
+                sb.append(((InetAddress) value).getHostAddress());
             } else {
                 sb.append(value.toString());
             }
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/HexDumpTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/HexDumpTest.java
index 7e6c7e2..5a15585 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/HexDumpTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/HexDumpTest.java
@@ -42,7 +42,7 @@
     }
 
     @Test
-    public void testHexStringToBytes() {
+    public void testHexStringToByteArray() {
         assertArrayEquals(new byte[]{(byte) 0xab, (byte) 0xcd, (byte) 0xef},
                 HexDump.hexStringToByteArray("abcdef"));
         assertArrayEquals(new byte[]{(byte) 0xAB, (byte) 0xCD, (byte) 0xEF},
@@ -50,13 +50,13 @@
     }
 
     @Test
-    public void testIntegerToBytes() {
+    public void testIntegerToByteArray() {
         assertArrayEquals(new byte[]{(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x04},
                 HexDump.toByteArray((int) 0xff000004));
     }
 
     @Test
-    public void testByteToBytes() {
+    public void testByteToByteArray() {
         assertArrayEquals(new byte[]{(byte) 0x7f}, HexDump.toByteArray((byte) 0x7f));
     }
 
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/StructTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/StructTest.java
index 253b911..b172e21 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/StructTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/StructTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.MacAddress;
 
@@ -38,6 +39,8 @@
 import org.junit.runner.RunWith;
 
 import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
@@ -74,6 +77,11 @@
             (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x05, (byte) 0x00, (byte) 0x06,
     };
 
+    private static final Inet4Address TEST_IPV4_ADDRESS =
+            (Inet4Address) InetAddresses.parseNumericAddress("192.168.100.1");
+    private static final Inet6Address TEST_IPV6_ADDRESS =
+            (Inet6Address) InetAddresses.parseNumericAddress("2001:db8:3:4:5:6:7:8");
+
     private <T> T doParsingMessageTest(final String hexString, final Class<T> clazz,
             final ByteOrder order) {
         final ByteBuffer buf = toByteBuffer(hexString);
@@ -733,6 +741,41 @@
         assertArrayEquals(toByteBuffer(hexString).array(), msg.writeToBytes());
     }
 
+    static class IpAddressMessage extends Struct {
+        @Field(order = 0, type = Type.Ipv4Address) public final Inet4Address ipv4Address;
+        @Field(order = 1, type = Type.Ipv6Address) public final Inet6Address ipv6Address;
+
+        IpAddressMessage(final Inet4Address ipv4Address, final Inet6Address ipv6Address) {
+            this.ipv4Address = ipv4Address;
+            this.ipv6Address = ipv6Address;
+        }
+    }
+
+    @Test
+    public void testIpAddressType() {
+        final IpAddressMessage msg = doParsingMessageTest("c0a86401"
+                + "20010db8000300040005000600070008", IpAddressMessage.class, ByteOrder.BIG_ENDIAN);
+
+        assertEquals(TEST_IPV4_ADDRESS, msg.ipv4Address);
+        assertEquals(TEST_IPV6_ADDRESS, msg.ipv6Address);
+
+        assertEquals(20, Struct.getSize(IpAddressMessage.class));
+        assertArrayEquals(toByteBuffer("c0a86401" + "20010db8000300040005000600070008").array(),
+                msg.writeToBytes(ByteOrder.BIG_ENDIAN));
+    }
+
+    static class WrongIpAddressType extends Struct {
+        @Field(order = 0, type = Type.Ipv4Address) public byte[] ipv4Address;
+        @Field(order = 1, type = Type.Ipv6Address) public byte[] ipv6Address;
+    }
+
+    @Test
+    public void testIncorrectType_IpAddressWithByteArray() {
+        assertThrows(IllegalArgumentException.class,
+                () -> Struct.parse(WrongIpAddressType.class,
+                                   toByteBuffer("c0a86401" + "20010db8000300040005000600070008")));
+    }
+
     static class FullTypeMessage extends Struct {
         @Field(order = 0, type = Type.U8) public final short u8;
         @Field(order = 1, type = Type.U16) public final int u16;
@@ -749,11 +792,14 @@
         @Field(order = 12, type = Type.UBE64) public final BigInteger ube64;
         @Field(order = 13, type = Type.ByteArray, arraysize = 12) public final byte[] bytes;
         @Field(order = 14, type = Type.EUI48) public final MacAddress eui48;
+        @Field(order = 15, type = Type.Ipv4Address) public final Inet4Address ipv4Address;
+        @Field(order = 16, type = Type.Ipv6Address) public final Inet6Address ipv6Address;
 
         FullTypeMessage(final short u8, final int u16, final long u32, final long u63,
                 final BigInteger u64, final byte s8, final short s16, final int s32, final long s64,
                 final int ube16, final long ube32, final long ube63, final BigInteger ube64,
-                final byte[] bytes, final MacAddress eui48) {
+                final byte[] bytes, final MacAddress eui48, final Inet4Address ipv4Address,
+                final Inet6Address ipv6Address) {
             this.u8 = u8;
             this.u16 = u16;
             this.u32 = u32;
@@ -769,25 +815,40 @@
             this.ube64 = ube64;
             this.bytes = bytes;
             this.eui48 = eui48;
+            this.ipv4Address = ipv4Address;
+            this.ipv6Address = ipv6Address;
         }
     }
 
     private static final String FULL_TYPE_DATA = "ff" + "ffff" + "ffffffff" + "7fffffffffffffff"
             + "ffffffffffffffff" + "7f" + "7fff" + "7fffffff" + "7fffffffffffffff" + "7fff"
             + "7fffffff" + "7fffffffffffffff" + "ffffffffffffffff" + "20010db80003000400050006"
-            + "001122334455";
+            + "001122334455" + "c0a86401" + "20010db8000300040005000600070008";
     private static final String FULL_TYPE_DATA_DIFF_MAC = "ff" + "ffff" + "ffffffff"
             + "7fffffffffffffff" + "ffffffffffffffff" + "7f" + "7fff" + "7fffffff"
             + "7fffffffffffffff" + "7fff" + "7fffffff" + "7fffffffffffffff" + "ffffffffffffffff"
-            + "20010db80003000400050006" + "112233445566";
+            + "20010db80003000400050006" + "112233445566"
+            + "c0a86401" + "20010db8000300040005000600070008";
     private static final String FULL_TYPE_DATA_DIFF_LONG = "ff" + "ffff" + "ffffffff"
             + "7ffffffffffffffe" + "ffffffffffffffff" + "7f" + "7fff" + "7fffffff"
             + "7fffffffffffffff" + "7fff" + "7fffffff" + "7fffffffffffffff" + "ffffffffffffffff"
-            + "20010db80003000400050006" + "001122334455";
+            + "20010db80003000400050006" + "001122334455"
+            + "c0a86401" + "20010db8000300040005000600070008";
     private static final String FULL_TYPE_DATA_DIFF_INTEGER = "ff" + "ffff" + "ffffffff"
             + "7fffffffffffffff" + "ffffffffffffffff" + "7f" + "7fff" + "7fffffff"
             + "7fffffffffffffff" + "7fff" + "ffffff7f" + "7fffffffffffffff" + "ffffffffffffffff"
-            + "20010db80003000400050006" + "001122334455";
+            + "20010db80003000400050006" + "001122334455"
+            + "c0a86401" + "20010db8000300040005000600070008";
+    private static final String FULL_TYPE_DATA_DIFF_IPV4 = "ff" + "ffff" + "ffffffff"
+            + "7fffffffffffffff" + "ffffffffffffffff" + "7f" + "7fff" + "7fffffff"
+            + "7fffffffffffffff" + "7fff" + "ffffff7f" + "7fffffffffffffff" + "ffffffffffffffff"
+            + "20010db80003000400050006" + "001122334455"
+            + "c0a81010" + "20010db8000300040005000600070008";
+    private static final String FULL_TYPE_DATA_DIFF_IPV6 = "ff" + "ffff" + "ffffffff"
+            + "7fffffffffffffff" + "ffffffffffffffff" + "7f" + "7fff" + "7fffffff"
+            + "7fffffffffffffff" + "7fff" + "ffffff7f" + "7fffffffffffffff" + "ffffffffffffffff"
+            + "20010db80003000400050006" + "001122334455"
+            + "c0a86401" + "20010db800030004000500060007000a";
     @Test
     public void testStructClass_equals() {
         final FullTypeMessage msg = doParsingMessageTest(FULL_TYPE_DATA, FullTypeMessage.class,
@@ -808,8 +869,10 @@
         assertEquals(new BigInteger("18446744073709551615"), msg.ube64);
         assertArrayEquals(TEST_PREFIX64, msg.bytes);
         assertEquals(MacAddress.fromString("00:11:22:33:44:55"), msg.eui48);
+        assertEquals(TEST_IPV4_ADDRESS, msg.ipv4Address);
+        assertEquals(TEST_IPV6_ADDRESS, msg.ipv6Address);
 
-        assertEquals(78, msg.getSize(FullTypeMessage.class));
+        assertEquals(98, msg.getSize(FullTypeMessage.class));
         assertArrayEquals(toByteBuffer(FULL_TYPE_DATA).array(),
                 msg.writeToBytes(ByteOrder.BIG_ENDIAN));
 
@@ -818,7 +881,7 @@
                 new BigInteger("18446744073709551615"), (byte) 0x7f, (short) 0x7fff,
                 (int) 0x7fffffff, (long) 0x7fffffffffffffffL, (int) 0x7fff, (long) 0x7fffffffL,
                 (long) 0x7fffffffffffffffL, new BigInteger("18446744073709551615"), TEST_PREFIX64,
-                MacAddress.fromString("00:11:22:33:44:55"));
+                MacAddress.fromString("00:11:22:33:44:55"), TEST_IPV4_ADDRESS, TEST_IPV6_ADDRESS);
         assertTrue(msg.equals(msg1));
     }
 
@@ -838,11 +901,14 @@
         @Field(order = 12, type = Type.UBE64) public final BigInteger ube64;
         @Field(order = 13, type = Type.ByteArray, arraysize = 12) public final byte[] bytes;
         @Field(order = 14, type = Type.EUI48) public final MacAddress eui48;
+        @Field(order = 15, type = Type.Ipv4Address) public final Inet4Address ipv4Address;
+        @Field(order = 16, type = Type.Ipv6Address) public final Inet6Address ipv6Address;
 
         FullTypeMessageWithDupType(final short u8, final int u16, final long u32, final long u63,
                 final BigInteger u64, final byte s8, final short s16, final int s32, final long s64,
                 final int ube16, final long ube32, final long ube63, final BigInteger ube64,
-                final byte[] bytes, final MacAddress eui48) {
+                final byte[] bytes, final MacAddress eui48, final Inet4Address ipv4Address,
+                final Inet6Address ipv6Address) {
             this.u8 = u8;
             this.u16 = u16;
             this.u32 = u32;
@@ -858,6 +924,8 @@
             this.ube64 = ube64;
             this.bytes = bytes;
             this.eui48 = eui48;
+            this.ipv4Address = ipv4Address;
+            this.ipv6Address = ipv6Address;
         }
     }
 
@@ -900,6 +968,17 @@
         assertNotEquals(msg.ube32, msg4.ube32);
         assertFalse(msg.equals(msg4));
 
+        // With different IPv4 address.
+        final FullTypeMessage msg5 = doParsingMessageTest(FULL_TYPE_DATA_DIFF_IPV4,
+                FullTypeMessage.class, ByteOrder.BIG_ENDIAN);
+        assertNotEquals(msg.ipv4Address, msg5.ipv4Address);
+        assertFalse(msg.equals(msg5));
+
+        // With different IPv6 address.
+        final FullTypeMessage msg6 = doParsingMessageTest(FULL_TYPE_DATA_DIFF_IPV6,
+                FullTypeMessage.class, ByteOrder.BIG_ENDIAN);
+        assertNotEquals(msg.ipv6Address, msg6.ipv6Address);
+        assertFalse(msg.equals(msg6));
     }
 
     @Test
@@ -909,7 +988,9 @@
                 + " s32: 2147483647, s64: 9223372036854775807, ube16: 32767, ube32: 2147483647,"
                 + " ube63: 9223372036854775807, ube64: 18446744073709551615,"
                 + " bytes: 0x20010DB80003000400050006,"
-                + " eui48: 00:11:22:33:44:55";
+                + " eui48: 00:11:22:33:44:55,"
+                + " ipv4Address: 192.168.100.1,"
+                + " ipv6Address: 2001:db8:3:4:5:6:7:8";
 
         final FullTypeMessage msg = doParsingMessageTest(FULL_TYPE_DATA, FullTypeMessage.class,
                 ByteOrder.BIG_ENDIAN);
@@ -922,14 +1003,15 @@
                 + " u63: 9223372036854775807, u64: null, s8: 127, s16: 32767,"
                 + " s32: 2147483647, s64: 9223372036854775807, ube16: 32767, ube32: 2147483647,"
                 + " ube63: 9223372036854775807, ube64: 18446744073709551615,"
-                + " bytes: null, eui48: null";
+                + " bytes: null, eui48: null, ipv4Address: 192.168.100.1,"
+                + " ipv6Address: null";
 
         final FullTypeMessage msg = new FullTypeMessage((short) 0xff, (int) 0xffff,
                 (long) 0xffffffffL, (long) 0x7fffffffffffffffL,
                 null /* u64 */, (byte) 0x7f, (short) 0x7fff,
                 (int) 0x7fffffff, (long) 0x7fffffffffffffffL, (int) 0x7fff, (long) 0x7fffffffL,
                 (long) 0x7fffffffffffffffL, new BigInteger("18446744073709551615"),
-                null /* bytes */, null /* eui48 */);
+                null /* bytes */, null /* eui48 */, TEST_IPV4_ADDRESS, null /* ipv6Address */);
         assertEquals(expected, msg.toString());
     }