Merge "Add bit mask constants for the flags used in the ND packet."
diff --git a/staticlibs/device/com/android/net/module/util/Struct.java b/staticlibs/device/com/android/net/module/util/Struct.java
index f11a5ac..d95c303 100644
--- a/staticlibs/device/com/android/net/module/util/Struct.java
+++ b/staticlibs/device/com/android/net/module/util/Struct.java
@@ -17,6 +17,7 @@
 package com.android.net.module.util;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.MacAddress;
 
 import java.lang.annotation.ElementType;
@@ -428,6 +429,15 @@
         return value;
     }
 
+    @Nullable
+    private Object getFieldValue(@NonNull java.lang.reflect.Field field) {
+        try {
+            return field.get(this);
+        } catch (IllegalAccessException e) {
+            throw new IllegalStateException("Cannot access field: " + field, e);
+        }
+    }
+
     private static void putFieldValue(final ByteBuffer output, final FieldInfo fieldInfo,
             final Object value) throws BufferUnderflowException {
         switch (fieldInfo.annotation.type()) {
@@ -484,6 +494,7 @@
                 output.put(bigIntegerToU64Bytes((BigInteger) value, output.order(), Type.UBE64));
                 break;
             case ByteArray:
+                checkByteArraySize((byte[]) value, fieldInfo);
                 output.put((byte[]) value);
                 break;
             case EUI48:
@@ -595,12 +606,23 @@
         return size;
     }
 
+    // Check whether the actual size of byte array matches the array size declared in
+    // annotation. For other annotation types, the actual length of field could be always
+    // deduced from annotation correctly.
+    private static void checkByteArraySize(@Nullable final byte[] array,
+            @NonNull final FieldInfo fieldInfo) {
+        Objects.requireNonNull(array, "null byte array for field " + fieldInfo.field.getName());
+        int annotationArraySize = fieldInfo.annotation.arraysize();
+        if (array.length == annotationArraySize) return;
+        throw new IllegalStateException("byte array actual length: "
+                + array.length + " doesn't match the declared array size: " + annotationArraySize);
+    }
+
     private void writeToByteBufferInternal(final ByteBuffer output, final FieldInfo[] fieldInfos) {
         for (FieldInfo fi : fieldInfos) {
+            final Object value = getFieldValue(fi.field);
             try {
-                putFieldValue(output, fi, fi.field.get(this));
-            } catch (IllegalAccessException e) {
-                throw new IllegalArgumentException("Fail to get the field value from instance", e);
+                putFieldValue(output, fi, value);
             } catch (BufferUnderflowException e) {
                 throw new IllegalArgumentException("Fail to fill raw data to ByteBuffer", e);
             }
@@ -673,12 +695,7 @@
         final FieldInfo[] fieldInfos = getClassFieldInfo(this.getClass());
         final Object[] values = new Object[fieldInfos.length];
         for (int i = 0; i < fieldInfos.length; i++) {
-            final Object value;
-            try {
-                value = fieldInfos[i].field.get(this);
-            } catch (IllegalAccessException e) {
-                throw new IllegalStateException("Cannot access field: " + fieldInfos[i].field, e);
-            }
+            final Object value = getFieldValue(fieldInfos[i].field);
             // For byte array field, put the hash code generated based on the array content into
             // the Object array instead of the reference to byte array, which might change and cause
             // to get a different hash code even with the exact same elements.
@@ -697,12 +714,7 @@
         final FieldInfo[] fieldInfos = getClassFieldInfo(this.getClass());
         for (int i = 0; i < fieldInfos.length; i++) {
             sb.append(fieldInfos[i].field.getName()).append(": ");
-            final Object value;
-            try {
-                value = fieldInfos[i].field.get(this);
-            } catch (IllegalAccessException e) {
-                throw new IllegalStateException("Cannot access field: " + fieldInfos[i].field, e);
-            }
+            final Object value = getFieldValue(fieldInfos[i].field);
             if (value == null) {
                 sb.append("null");
             } else if (fieldInfos[i].annotation.type() == Type.ByteArray) {
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 b172e21..df74398 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
@@ -1027,4 +1027,48 @@
         assertTrue(msg.equals(msg1));
         assertEquals(msg.hashCode(), msg1.hashCode());
     }
+
+    static class InvalidByteArray extends Struct {
+        @Field(order = 0, type = Type.ByteArray, arraysize = 12) public byte[] bytes;
+    }
+
+    @Test
+    public void testStructClass_WrongByteArraySize() {
+        final InvalidByteArray msg = doParsingMessageTest("20010db80003000400050006",
+                InvalidByteArray.class, ByteOrder.BIG_ENDIAN);
+
+        // Actual byte array size doesn't match the size declared in the annotation.
+        msg.bytes = new byte[16];
+        assertThrows(IllegalStateException.class, () -> msg.writeToBytes());
+
+        final ByteBuffer output = ByteBuffer.allocate(Struct.getSize(InvalidByteArray.class));
+        output.order(ByteOrder.LITTLE_ENDIAN);
+        assertThrows(IllegalStateException.class, () -> msg.writeToByteBuffer(output));
+    }
+
+    @Test
+    public void testStructClass_NullByteArray() {
+        final InvalidByteArray msg = doParsingMessageTest("20010db80003000400050006",
+                InvalidByteArray.class, ByteOrder.BIG_ENDIAN);
+
+        msg.bytes = null;
+        assertThrows(NullPointerException.class, () -> msg.writeToBytes());
+
+        final ByteBuffer output = ByteBuffer.allocate(Struct.getSize(InvalidByteArray.class));
+        output.order(ByteOrder.LITTLE_ENDIAN);
+        assertThrows(NullPointerException.class, () -> msg.writeToByteBuffer(output));
+    }
+
+    @Test
+    public void testStructClass_ParsingByteArrayAfterInitialization() {
+        InvalidByteArray msg = new InvalidByteArray();
+        msg.bytes = new byte[]{(byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03};
+
+        // Although bytes member has been initialized with the length different with
+        // annotation size, parsing from ByteBuffer will get bytes member have the
+        // reference to byte array with correct size.
+        msg = doParsingMessageTest("20010db80003000400050006", InvalidByteArray.class,
+                ByteOrder.BIG_ENDIAN);
+        assertArrayEquals(TEST_PREFIX64, msg.bytes);
+    }
 }