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);
+ }
}