Merge "Rename StringNetworkSpecifier to Ethernet"
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index a3bfbce..03296e7 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -60,6 +60,9 @@
"//packages/modules/CaptivePortalLogin",
"//frameworks/libs/net/common/tests:__subpackages__",
],
+ static_libs: [
+ "net-utils-framework-common",
+ ],
libs: [
"androidx.annotation_annotation",
],
@@ -146,6 +149,7 @@
"//packages/modules/NetworkStack:__subpackages__",
"//packages/modules/CaptivePortalLogin",
"//frameworks/libs/net/common/tests:__subpackages__",
+ "//frameworks/libs/net/common/device",
"//packages/modules/Wifi/framework/tests:__subpackages__",
]
}
diff --git a/staticlibs/device/com/android/net/module/util/Ipv6Utils.java b/staticlibs/device/com/android/net/module/util/Ipv6Utils.java
new file mode 100644
index 0000000..73c2431
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/Ipv6Utils.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util;
+
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+
+import static com.android.net.module.util.IpUtils.icmpv6Checksum;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
+
+import android.net.MacAddress;
+
+import com.android.net.module.util.structs.EthernetHeader;
+import com.android.net.module.util.structs.Icmpv6Header;
+import com.android.net.module.util.structs.Ipv6Header;
+import com.android.net.module.util.structs.NaHeader;
+import com.android.net.module.util.structs.RaHeader;
+import com.android.net.module.util.structs.RsHeader;
+
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Utilities for IPv6 packets.
+ */
+public class Ipv6Utils {
+ /**
+ * Build a generic ICMPv6 packet(e.g., packet used in the neighbor discovery protocol).
+ */
+ public static ByteBuffer buildIcmpv6Packet(final MacAddress srcMac, final MacAddress dstMac,
+ final Inet6Address srcIp, final Inet6Address dstIp, short type, short code,
+ final ByteBuffer... options) {
+ final int etherHeaderLen = Struct.getSize(EthernetHeader.class);
+ final int ipv6HeaderLen = Struct.getSize(Ipv6Header.class);
+ final int icmpv6HeaderLen = Struct.getSize(Icmpv6Header.class);
+ int payloadLen = 0;
+ for (ByteBuffer option: options) {
+ payloadLen += option.limit();
+ }
+ final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv6HeaderLen
+ + icmpv6HeaderLen + payloadLen);
+ final EthernetHeader ethHeader =
+ new EthernetHeader(dstMac, srcMac, (short) ETHER_TYPE_IPV6);
+ final Ipv6Header ipv6Header =
+ new Ipv6Header((int) 0x60000000 /* version, traffic class, flowlabel */,
+ icmpv6HeaderLen + payloadLen /* payload length */,
+ (byte) IPPROTO_ICMPV6 /* next header */, (byte) 0xff /* hop limit */, srcIp, dstIp);
+ final Icmpv6Header icmpv6Header = new Icmpv6Header(type, code, (short) 0 /* checksum */);
+
+ ethHeader.writeToByteBuffer(packet);
+ ipv6Header.writeToByteBuffer(packet);
+ icmpv6Header.writeToByteBuffer(packet);
+ for (ByteBuffer option : options) {
+ packet.put(option);
+ // in case option might be reused by caller, restore the position and
+ // limit of bytebuffer.
+ option.clear();
+ }
+ packet.flip();
+
+ // Populate the ICMPv6 checksum field.
+ packet.putShort(etherHeaderLen + ipv6HeaderLen + 2, icmpv6Checksum(packet,
+ etherHeaderLen /* ipOffset */,
+ (int) (etherHeaderLen + ipv6HeaderLen) /* transportOffset */,
+ (short) (icmpv6HeaderLen + payloadLen) /* transportLen */));
+ return packet;
+ }
+
+ /**
+ * Build the ICMPv6 packet payload including payload header and specific options.
+ */
+ private static ByteBuffer[] buildIcmpv6Payload(final ByteBuffer payloadHeader,
+ final ByteBuffer... options) {
+ final ByteBuffer[] payload = new ByteBuffer[options.length + 1];
+ payload[0] = payloadHeader;
+ System.arraycopy(options, 0, payload, 1, options.length);
+ return payload;
+ }
+
+ /**
+ * Build an ICMPv6 Router Advertisement packet from the required specified parameters.
+ */
+ public static ByteBuffer buildRaPacket(final MacAddress srcMac, final MacAddress dstMac,
+ final Inet6Address srcIp, final Inet6Address dstIp, final byte flags,
+ final int lifetime, final long reachableTime, final long retransTimer,
+ final ByteBuffer... options) {
+ final RaHeader raHeader = new RaHeader((byte) 0 /* hopLimit, unspecified */,
+ flags, lifetime, reachableTime, retransTimer);
+ final ByteBuffer[] payload = buildIcmpv6Payload(
+ ByteBuffer.wrap(raHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options);
+ return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp,
+ (byte) ICMPV6_ROUTER_ADVERTISEMENT /* type */, (byte) 0 /* code */, payload);
+ }
+
+ /**
+ * Build an ICMPv6 Neighbor Advertisement packet from the required specified parameters.
+ */
+ public static ByteBuffer buildNaPacket(final MacAddress srcMac, final MacAddress dstMac,
+ final Inet6Address srcIp, final Inet6Address dstIp, final int flags,
+ final Inet6Address target, final ByteBuffer... options) {
+ final NaHeader naHeader = new NaHeader(flags, target);
+ final ByteBuffer[] payload = buildIcmpv6Payload(
+ ByteBuffer.wrap(naHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options);
+ return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp,
+ (byte) ICMPV6_NEIGHBOR_ADVERTISEMENT /* type */, (byte) 0 /* code */, payload);
+ }
+
+ /**
+ * Build an ICMPv6 Router Solicitation packet from the required specified parameters.
+ */
+ public static ByteBuffer buildRsPacket(final MacAddress srcMac, final MacAddress dstMac,
+ final Inet6Address srcIp, final Inet6Address dstIp, final ByteBuffer... options) {
+ final RsHeader rsHeader = new RsHeader((int) 0 /* reserved */);
+ final ByteBuffer[] payload = buildIcmpv6Payload(
+ ByteBuffer.wrap(rsHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options);
+ return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp,
+ (byte) ICMPV6_ROUTER_SOLICITATION /* type */, (byte) 0 /* code */, payload);
+ }
+}
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/device/com/android/net/module/util/structs/EthernetHeader.java b/staticlibs/device/com/android/net/module/util/structs/EthernetHeader.java
new file mode 100644
index 0000000..92ef8a7
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/EthernetHeader.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import android.net.MacAddress;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * L2 ethernet header as per IEEE 802.3. Does not include a 802.1Q tag.
+ *
+ * 0 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Destination |
+ * +- -+
+ * | Ethernet |
+ * +- -+
+ * | Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source |
+ * +- -+
+ * | Ethernet |
+ * +- -+
+ * | Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | EtherType |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class EthernetHeader extends Struct {
+ @Field(order = 0, type = Type.EUI48)
+ public final MacAddress dstMac;
+ @Field(order = 1, type = Type.EUI48)
+ public final MacAddress srcMac;
+ @Field(order = 2, type = Type.U16)
+ public final int etherType;
+
+ public EthernetHeader(final MacAddress dstMac, final MacAddress srcMac,
+ final int etherType) {
+ this.dstMac = dstMac;
+ this.srcMac = srcMac;
+ this.etherType = etherType;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/Icmpv6Header.java b/staticlibs/device/com/android/net/module/util/structs/Icmpv6Header.java
new file mode 100644
index 0000000..c82ae02
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/Icmpv6Header.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * ICMPv6 header as per https://tools.ietf.org/html/rfc4443.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Code | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class Icmpv6Header extends Struct {
+ @Field(order = 0, type = Type.U8)
+ public short type;
+ @Field(order = 1, type = Type.U8)
+ public short code;
+ @Field(order = 2, type = Type.S16)
+ public short checksum;
+
+ public Icmpv6Header(final short type, final short code, final short checksum) {
+ this.type = type;
+ this.code = code;
+ this.checksum = checksum;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/Ipv6Header.java b/staticlibs/device/com/android/net/module/util/structs/Ipv6Header.java
new file mode 100644
index 0000000..a14e064
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/Ipv6Header.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.net.Inet6Address;
+
+/**
+ * L3 IPv6 header as per https://tools.ietf.org/html/rfc8200.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Version| Traffic Class | Flow Label |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Payload Length | Next Header | Hop Limit |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | |
+ * + Source Address +
+ * | |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | |
+ * + Destination Address +
+ * | |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class Ipv6Header extends Struct {
+ @Field(order = 0, type = Type.S32)
+ public int vtf;
+ @Field(order = 1, type = Type.U16)
+ public int payloadLength;
+ @Field(order = 2, type = Type.S8)
+ public byte nextHeader;
+ @Field(order = 3, type = Type.U8)
+ public short hopLimit;
+ @Field(order = 4, type = Type.Ipv6Address)
+ public Inet6Address srcIp;
+ @Field(order = 5, type = Type.Ipv6Address)
+ public Inet6Address dstIp;
+
+ public Ipv6Header(final int vtf, final int payloadLength, final byte nextHeader,
+ final short hopLimit, final Inet6Address srcIp, final Inet6Address dstIp) {
+ this.vtf = vtf;
+ this.payloadLength = payloadLength;
+ this.nextHeader = nextHeader;
+ this.hopLimit = hopLimit;
+ this.srcIp = srcIp;
+ this.dstIp = dstIp;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/LlaOption.java b/staticlibs/device/com/android/net/module/util/structs/LlaOption.java
new file mode 100644
index 0000000..fbaccab
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/LlaOption.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import android.net.MacAddress;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * ICMPv6 source/target link-layer address option, as per https://tools.ietf.org/html/rfc4861.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Link-Layer Address ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class LlaOption extends Struct {
+ @Field(order = 0, type = Type.S8)
+ public final byte type;
+ @Field(order = 1, type = Type.S8)
+ public final byte length; // Length in 8-byte units
+ @Field(order = 2, type = Type.EUI48)
+ // Link layer address length and format varies on different link layers, which is not
+ // guaranteed to be a 6-byte MAC address. However, Struct only supports 6-byte MAC
+ // addresses type(EUI-48) for now.
+ public final MacAddress linkLayerAddress;
+
+ LlaOption(final byte type, final byte length, final MacAddress linkLayerAddress) {
+ this.type = type;
+ this.length = length;
+ this.linkLayerAddress = linkLayerAddress;
+ }
+
+ /**
+ * Build a target link-layer address option from the required specified parameters.
+ */
+ public static ByteBuffer build(final byte type, final MacAddress linkLayerAddress) {
+ final LlaOption option = new LlaOption(type, (byte) 1 /* option len */, linkLayerAddress);
+ return ByteBuffer.wrap(option.writeToBytes(ByteOrder.BIG_ENDIAN));
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/MtuOption.java b/staticlibs/device/com/android/net/module/util/structs/MtuOption.java
new file mode 100644
index 0000000..34bc21c
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/MtuOption.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * ICMPv6 MTU option, as per https://tools.ietf.org/html/rfc4861.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MTU |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class MtuOption extends Struct {
+ @Field(order = 0, type = Type.S8)
+ public final byte type;
+ @Field(order = 1, type = Type.S8)
+ public final byte length; // Length in 8-byte units
+ @Field(order = 2, type = Type.S16)
+ public final short reserved;
+ @Field(order = 3, type = Type.U32)
+ public final long mtu;
+
+ MtuOption(final byte type, final byte length, final short reserved,
+ final long mtu) {
+ this.type = type;
+ this.length = length;
+ this.reserved = reserved;
+ this.mtu = mtu;
+ }
+
+ /**
+ * Build a MTU option from the required specified parameters.
+ */
+ public static ByteBuffer build(final long mtu) {
+ final MtuOption option = new MtuOption((byte) ICMPV6_ND_OPTION_MTU,
+ (byte) 1 /* option len */, (short) 0 /* reserved */, mtu);
+ return ByteBuffer.wrap(option.writeToBytes(ByteOrder.BIG_ENDIAN));
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/NaHeader.java b/staticlibs/device/com/android/net/module/util/structs/NaHeader.java
new file mode 100644
index 0000000..571d67b
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/NaHeader.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.net.Inet6Address;
+
+/**
+ * ICMPv6 Neighbor Advertisement header, follow {@link Icmpv6Header}, as per
+ * https://tools.ietf.org/html/rfc4861. This does not contain any option.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Code | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|S|O| Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | |
+ * + Target Address +
+ * | |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Options ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-
+ */
+public class NaHeader extends Struct {
+ @Field(order = 0, type = Type.S32)
+ public int flags; // Router flag, Solicited flag, Override flag and 29 Reserved bits.
+ @Field(order = 1, type = Type.Ipv6Address)
+ public Inet6Address target;
+
+ public NaHeader(final int flags, final Inet6Address target) {
+ this.flags = flags;
+ this.target = target;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/PrefixInformationOption.java b/staticlibs/device/com/android/net/module/util/structs/PrefixInformationOption.java
new file mode 100644
index 0000000..1bdee29
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/PrefixInformationOption.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
+
+import android.annotation.NonNull;
+import android.net.IpPrefix;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * ICMPv6 prefix information option, as per https://tools.ietf.org/html/rfc4861.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Prefix Length |L|A| Reserved1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Valid Lifetime |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Preferred Lifetime |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved2 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | |
+ * + Prefix +
+ * | |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class PrefixInformationOption extends Struct {
+ @Field(order = 0, type = Type.S8)
+ public final byte type;
+ @Field(order = 1, type = Type.S8)
+ public final byte length; // Length in 8-byte units
+ @Field(order = 2, type = Type.S8)
+ public final byte prefixLen;
+ @Field(order = 3, type = Type.S8)
+ // On-link flag, Autonomous address configuration flag, 6-reserved bits
+ public final byte flags;
+ @Field(order = 4, type = Type.U32)
+ public final long validLifetime;
+ @Field(order = 5, type = Type.U32)
+ public final long preferredLifetime;
+ @Field(order = 6, type = Type.S32)
+ public final int reserved;
+ @Field(order = 7, type = Type.ByteArray, arraysize = 16)
+ public final byte[] prefix;
+
+ PrefixInformationOption(final byte type, final byte length, final byte prefixLen,
+ final byte flags, final long validLifetime, final long preferredLifetime,
+ final int reserved, @NonNull final byte[] prefix) {
+ this.type = type;
+ this.length = length;
+ this.prefixLen = prefixLen;
+ this.flags = flags;
+ this.validLifetime = validLifetime;
+ this.preferredLifetime = preferredLifetime;
+ this.reserved = reserved;
+ this.prefix = prefix;
+ }
+
+ /**
+ * Build a Prefix Information option from the required specified parameters.
+ */
+ public static ByteBuffer build(final IpPrefix prefix, final byte flags,
+ final long validLifetime, final long preferredLifetime) {
+ final PrefixInformationOption option = new PrefixInformationOption(
+ (byte) ICMPV6_ND_OPTION_PIO, (byte) 4 /* option len */,
+ (byte) prefix.getPrefixLength(), flags, validLifetime, preferredLifetime,
+ (int) 0, prefix.getRawAddress());
+ return ByteBuffer.wrap(option.writeToBytes(ByteOrder.BIG_ENDIAN));
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/RaHeader.java b/staticlibs/device/com/android/net/module/util/structs/RaHeader.java
new file mode 100644
index 0000000..31a5cb7
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/RaHeader.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * ICMPv6 Router Advertisement header, follow [Icmpv6Header], as per
+ * https://tools.ietf.org/html/rfc4861. This does not contain any option.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Code | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Cur Hop Limit |M|O| Reserved | Router Lifetime |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reachable Time |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Retrans Timer |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Options ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-
+ */
+public class RaHeader extends Struct {
+ @Field(order = 0, type = Type.S8)
+ public final byte hopLimit;
+ // "Managed address configuration", "Other configuration" bits, and 6 reserved bits
+ @Field(order = 1, type = Type.S8)
+ public final byte flags;
+ @Field(order = 2, type = Type.U16)
+ public final int lifetime;
+ @Field(order = 3, type = Type.U32)
+ public final long reachableTime;
+ @Field(order = 4, type = Type.U32)
+ public final long retransTimer;
+
+ public RaHeader(final byte hopLimit, final byte flags, final int lifetime,
+ final long reachableTime, final long retransTimer) {
+ this.hopLimit = hopLimit;
+ this.flags = flags;
+ this.lifetime = lifetime;
+ this.reachableTime = reachableTime;
+ this.retransTimer = retransTimer;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/RdnssOption.java b/staticlibs/device/com/android/net/module/util/structs/RdnssOption.java
new file mode 100644
index 0000000..b7c2b0c
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/RdnssOption.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_RDNSS;
+
+import android.net.InetAddresses;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+
+/**
+ * IPv6 RA recursive DNS server option, as per https://tools.ietf.org/html/rfc8106.
+ * This should be followed by a series of DNSv6 server addresses.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Lifetime |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * : Addresses of IPv6 Recursive DNS Servers :
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class RdnssOption extends Struct {
+ @Field(order = 0, type = Type.S8)
+ public final byte type;
+ @Field(order = 1, type = Type.S8)
+ public final byte length; // Length in 8-byte units
+ @Field(order = 2, type = Type.S16)
+ public final short reserved;
+ @Field(order = 3, type = Type.U32)
+ public final long lifetime;
+
+ RdnssOption(final byte type, final byte length, final short reserved, final long lifetime) {
+ this.type = type;
+ this.length = length;
+ this.reserved = reserved;
+ this.lifetime = lifetime;
+ }
+
+ /**
+ * Build a RDNSS option from the required specified Inet6Address parameters.
+ */
+ public static ByteBuffer build(final long lifetime, final Inet6Address... servers) {
+ final byte length = (byte) (1 + 2 * servers.length);
+ final RdnssOption option = new RdnssOption((byte) ICMPV6_ND_OPTION_RDNSS,
+ length, (short) 0, lifetime);
+ final ByteBuffer buffer = ByteBuffer.allocate(length * 8);
+ option.writeToByteBuffer(buffer);
+ for (Inet6Address server : servers) {
+ buffer.put(server.getAddress());
+ }
+ buffer.flip();
+ return buffer;
+ }
+
+ /**
+ * Build a RDNSS option from the required specified String parameters.
+ *
+ * @throws IllegalArgumentException if {@code servers} does not contain only numeric addresses.
+ */
+ public static ByteBuffer build(final long lifetime, final String... servers) {
+ final Inet6Address[] serverArray = new Inet6Address[servers.length];
+ for (int i = 0; i < servers.length; i++) {
+ serverArray[i] = (Inet6Address) InetAddresses.parseNumericAddress(servers[i]);
+ }
+ return build(lifetime, serverArray);
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/RsHeader.java b/staticlibs/device/com/android/net/module/util/structs/RsHeader.java
new file mode 100644
index 0000000..0b51ff2
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/RsHeader.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * ICMPv6 Router Solicitation header, follow [Icmpv6Header], as per
+ * https://tools.ietf.org/html/rfc4861. This does not contain any option.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Code | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Options ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-
+ */
+public class RsHeader extends Struct {
+ @Field(order = 0, type = Type.S32)
+ public final int reserved;
+
+ public RsHeader(final int reserved) {
+ this.reserved = reserved;
+ }
+}
diff --git a/staticlibs/framework/com/android/net/module/util/CollectionUtils.java b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
index e5bb58d..2223443 100644
--- a/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
@@ -38,6 +38,13 @@
}
/**
+ * @return True if the collection is null or 0-length.
+ */
+ public static <T> boolean isEmpty(@Nullable Collection<T> collection) {
+ return collection == null || collection.isEmpty();
+ }
+
+ /**
* Returns an int array from the given Integer list.
*/
@NonNull
@@ -76,15 +83,26 @@
return true;
}
+
/**
* @return True if any element satisfies the predicate, false otherwise.
* Note that means this always returns false for empty collections.
*/
public static <T> boolean any(@NonNull Collection<T> elem, @NonNull Predicate<T> predicate) {
+ return indexOf(elem, predicate) >= 0;
+ }
+
+ /**
+ * @return The index of the first element that matches the predicate, or -1 if none.
+ */
+ @Nullable
+ public static <T> int indexOf(@NonNull Collection<T> elem, @NonNull Predicate<T> predicate) {
+ int idx = 0;
for (final T e : elem) {
- if (predicate.test(e)) return true;
+ if (predicate.test(e)) return idx;
+ idx++;
}
- return false;
+ return -1;
}
/**
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java b/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
index 5a0200f..3de78c6 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
@@ -78,4 +78,31 @@
}
return transports[0];
}
+
+ /**
+ * Unpacks long value into an array of bits.
+ */
+ public static int[] unpackBits(long val) {
+ int size = Long.bitCount(val);
+ int[] result = new int[size];
+ int index = 0;
+ int bitPos = 0;
+ while (val != 0) {
+ if ((val & 1) == 1) result[index++] = bitPos;
+ val = val >>> 1;
+ bitPos++;
+ }
+ return result;
+ }
+
+ /**
+ * Packs array of bits into a long value.
+ */
+ public static long packBits(int[] bits) {
+ long packed = 0;
+ for (int b : bits) {
+ packed |= (1L << b);
+ }
+ return packed;
+ }
}
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index 5f62186..499297c 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -148,9 +148,18 @@
public static final int ICMPV6_ND_OPTION_RDNSS = 25;
public static final int ICMPV6_ND_OPTION_PREF64 = 38;
-
public static final int ICMPV6_RA_HEADER_LEN = 16;
+ public static final int NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER = 1 << 31;
+ public static final int NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED = 1 << 30;
+ public static final int NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE = 1 << 29;
+
+ public static final byte ROUTER_ADVERTISEMENT_FLAG_MANAGED_ADDRESS = (byte) (1 << 7);
+ public static final byte ROUTER_ADVERTISEMENT_FLAG_OTHER = (byte) (1 << 6);
+
+ public static final byte PIO_FLAG_ON_LINK = (byte) (1 << 7);
+ public static final byte PIO_FLAG_AUTONOMOUS = (byte) (1 << 6);
+
/**
* UDP constants.
*
diff --git a/staticlibs/hostdevice/com/android/testutils/MiscAsserts.kt b/staticlibs/hostdevice/com/android/testutils/MiscAsserts.kt
index 09126d7..91fb1cd 100644
--- a/staticlibs/hostdevice/com/android/testutils/MiscAsserts.kt
+++ b/staticlibs/hostdevice/com/android/testutils/MiscAsserts.kt
@@ -32,10 +32,18 @@
assertEquals(0, len, "Expected empty array, but length was $len")
}
+fun <T> assertEmpty(ts: List<T>) = ts.size.let { len ->
+ assertEquals(0, len, "Expected empty list, but length was $len")
+}
+
fun <T> assertLength(expected: Int, got: Array<T>) = got.size.let { len ->
assertEquals(expected, len, "Expected array of length $expected, but was $len for $got")
}
+fun <T> assertLength(expected: Int, got: List<T>) = got.size.let { len ->
+ assertEquals(expected, len, "Expected list of length $expected, but was $len for $got")
+}
+
// Bridge method to help write this in Java. If you're writing Kotlin, consider using
// kotlin.test.assertFailsWith instead, as that method is reified and inlined.
fun <T : Exception> assertThrows(expected: Class<T>, block: ThrowingRunnable): T {
@@ -103,3 +111,12 @@
!Modifier.isStatic(it.modifiers) && !Modifier.isTransient(it.modifiers)
}.size)
}
+
+fun assertSameElements(expected: List<String?>, actual: List<String?>) {
+ val expectedSet: HashSet<String?> = HashSet<String?>(expected)
+ assertEquals(expectedSet.size.toLong(), expected.size.toLong(),
+ "expected list contains duplicates")
+ val actualSet: HashSet<String?> = HashSet<String?>(actual)
+ assertEquals(actualSet.size.toLong(), actual.size.toLong(), "actual list contains duplicates")
+ assertEquals(expectedSet, actualSet)
+}
diff --git a/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java b/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
index e47c864..45493bd 100644
--- a/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
+++ b/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
@@ -16,6 +16,8 @@
package android.net.util;
+import static com.android.testutils.MiscAsserts.assertSameElements;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -40,7 +42,6 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.function.Function;
@@ -247,14 +248,6 @@
assertEquals(linkAddr2, results.added.get(0));
}
- private void assertSameElements(List<String> expected, List<String> actual) {
- HashSet<String> expectedSet = new HashSet(expected);
- assertEquals("expected list contains duplicates", expectedSet.size(), expected.size());
- HashSet<String> actualSet = new HashSet(actual);
- assertEquals("actual list contains duplicates", actualSet.size(), actual.size());
- assertEquals(expectedSet, actualSet);
- }
-
private void assertCompareOrUpdateResult(CompareOrUpdateResult result,
List<String> expectedAdded, List<String> expectedRemoved,
List<String> expectedUpdated) {
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
index 0007742..0886426 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
@@ -20,6 +20,7 @@
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
+import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@@ -40,6 +41,15 @@
}
@Test
+ fun testIndexOf() {
+ assertEquals(4, CollectionUtils.indexOf(listOf("A", "B", "C", "D", "E")) { it == "E" })
+ assertEquals(0, CollectionUtils.indexOf(listOf("A", "B", "C", "D", "E")) { it == "A" })
+ assertEquals(1, CollectionUtils.indexOf(listOf("AA", "BBB", "CCCC")) { it.length >= 3 })
+ assertEquals(1, CollectionUtils.indexOf(listOf("AA", null, "CCCC")) { it == null })
+ assertEquals(1, CollectionUtils.indexOf(listOf(null, "CCCC")) { it != null })
+ }
+
+ @Test
fun testAll() {
assertFalse(CollectionUtils.all(listOf("A", "B", "C", "D", "E")) { it != "E" })
assertTrue(CollectionUtils.all(listOf("A", "B", "C", "D", "E")) { it != "F" })
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt
index e94d132..df2f459 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt
@@ -26,11 +26,14 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport
+import com.android.net.module.util.NetworkCapabilitiesUtils.packBits
+import com.android.net.module.util.NetworkCapabilitiesUtils.unpackBits
import org.junit.Test
import org.junit.runner.RunWith
import java.lang.IllegalArgumentException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -58,4 +61,21 @@
getDisplayTransport(intArrayOf())
}
}
-}
\ No newline at end of file
+
+ @Test
+ fun testBitPackingTestCase() {
+ runBitPackingTestCase(0, intArrayOf())
+ runBitPackingTestCase(1, intArrayOf(0))
+ runBitPackingTestCase(3, intArrayOf(0, 1))
+ runBitPackingTestCase(4, intArrayOf(2))
+ runBitPackingTestCase(63, intArrayOf(0, 1, 2, 3, 4, 5))
+ runBitPackingTestCase(Long.MAX_VALUE.inv(), intArrayOf(63))
+ runBitPackingTestCase(Long.MAX_VALUE.inv() + 1, intArrayOf(0, 63))
+ runBitPackingTestCase(Long.MAX_VALUE.inv() + 2, intArrayOf(1, 63))
+ }
+
+ fun runBitPackingTestCase(packedBits: Long, bits: IntArray) {
+ assertEquals(packedBits, packBits(bits))
+ assertTrue(bits contentEquals unpackBits(packedBits))
+ }
+}
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);
+ }
}