Merge "Move LocationPermissionChecker to libs/net"
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
index d895cfa..92ef8a7 100644
--- a/staticlibs/device/com/android/net/module/util/structs/EthernetHeader.java
+++ b/staticlibs/device/com/android/net/module/util/structs/EthernetHeader.java
@@ -40,7 +40,7 @@
* +- -+
* | Address |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |1 0 0 0 0 1 1 0 1 1 0 1 1 1 0 1|
+ * | EtherType |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
public class EthernetHeader extends Struct {
diff --git a/staticlibs/device/com/android/net/module/util/structs/Ipv6Header.java b/staticlibs/device/com/android/net/module/util/structs/Ipv6Header.java
index 3ad8157..a14e064 100644
--- a/staticlibs/device/com/android/net/module/util/structs/Ipv6Header.java
+++ b/staticlibs/device/com/android/net/module/util/structs/Ipv6Header.java
@@ -53,7 +53,7 @@
@Field(order = 0, type = Type.S32)
public int vtf;
@Field(order = 1, type = Type.U16)
- public int length;
+ public int payloadLength;
@Field(order = 2, type = Type.S8)
public byte nextHeader;
@Field(order = 3, type = Type.U8)
@@ -63,10 +63,10 @@
@Field(order = 5, type = Type.Ipv6Address)
public Inet6Address dstIp;
- public Ipv6Header(final int vtf, final int length, final byte nextHeader, final short hopLimit,
- final Inet6Address srcIp, final 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.length = length;
+ this.payloadLength = payloadLength;
this.nextHeader = nextHeader;
this.hopLimit = hopLimit;
this.srcIp = srcIp;
diff --git a/staticlibs/device/com/android/net/module/util/structs/LlaOption.java b/staticlibs/device/com/android/net/module/util/structs/LlaOption.java
index 2cdeefb..fbaccab 100644
--- a/staticlibs/device/com/android/net/module/util/structs/LlaOption.java
+++ b/staticlibs/device/com/android/net/module/util/structs/LlaOption.java
@@ -36,14 +36,16 @@
*/
public class LlaOption extends Struct {
@Field(order = 0, type = Type.S8)
- // 1 for Source Link-layer Address; 2 for Target Link-layer Address.
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;
- public LlaOption(final byte type, final byte length, final MacAddress linkLayerAddress) {
+ LlaOption(final byte type, final byte length, final MacAddress linkLayerAddress) {
this.type = type;
this.length = length;
this.linkLayerAddress = linkLayerAddress;
@@ -54,6 +56,6 @@
*/
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.nativeOrder()));
+ 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
index a35b576..34bc21c 100644
--- a/staticlibs/device/com/android/net/module/util/structs/MtuOption.java
+++ b/staticlibs/device/com/android/net/module/util/structs/MtuOption.java
@@ -46,7 +46,7 @@
@Field(order = 3, type = Type.U32)
public final long mtu;
- public MtuOption(final byte type, final byte length, final short reserved,
+ MtuOption(final byte type, final byte length, final short reserved,
final long mtu) {
this.type = type;
this.length = length;
@@ -60,6 +60,6 @@
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.nativeOrder()));
+ 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
index 8101b06..571d67b 100644
--- a/staticlibs/device/com/android/net/module/util/structs/NaHeader.java
+++ b/staticlibs/device/com/android/net/module/util/structs/NaHeader.java
@@ -23,7 +23,7 @@
import java.net.Inet6Address;
/**
- * ICMPv6 Neighbor Advertisement header, follow [Icmpv6Header], as per
+ * 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
diff --git a/staticlibs/device/com/android/net/module/util/structs/PrefixInformationOption.java b/staticlibs/device/com/android/net/module/util/structs/PrefixInformationOption.java
index 7dc0192..1bdee29 100644
--- a/staticlibs/device/com/android/net/module/util/structs/PrefixInformationOption.java
+++ b/staticlibs/device/com/android/net/module/util/structs/PrefixInformationOption.java
@@ -70,7 +70,7 @@
@Field(order = 7, type = Type.ByteArray, arraysize = 16)
public final byte[] prefix;
- public PrefixInformationOption(final byte type, final byte length, final byte prefixLen,
+ 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;
@@ -92,6 +92,6 @@
(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.nativeOrder()));
+ return ByteBuffer.wrap(option.writeToBytes(ByteOrder.BIG_ENDIAN));
}
}
diff --git a/staticlibs/device/com/android/net/module/util/structs/RdnssOption.java b/staticlibs/device/com/android/net/module/util/structs/RdnssOption.java
index de55e66..b7c2b0c 100644
--- a/staticlibs/device/com/android/net/module/util/structs/RdnssOption.java
+++ b/staticlibs/device/com/android/net/module/util/structs/RdnssOption.java
@@ -53,8 +53,7 @@
@Field(order = 3, type = Type.U32)
public final long lifetime;
- public RdnssOption(final byte type, final byte length, final short reserved,
- 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;
@@ -79,6 +78,8 @@
/**
* 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];
diff --git a/staticlibs/devicetests/com/android/testutils/CompatUtil.kt b/staticlibs/devicetests/com/android/testutils/CompatUtil.kt
new file mode 100644
index 0000000..4bb90a8
--- /dev/null
+++ b/staticlibs/devicetests/com/android/testutils/CompatUtil.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.testutils
+
+import android.net.NetworkSpecifier
+import android.os.Build
+
+/**
+ * Test utility to create [NetworkSpecifier]s on different SDK versions.
+ */
+object CompatUtil {
+ @JvmStatic
+ fun makeTestNetworkSpecifier(ifName: String): NetworkSpecifier {
+ // Until R, there was no TestNetworkSpecifier, StringNetworkSpecifier was used instead
+ if (isDevSdkInRange(minExclusive = null, maxInclusive = Build.VERSION_CODES.R)) {
+ makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
+ }
+ // TestNetworkSpecifier is not part of the SDK in some branches using this utility
+ // TODO: replace with a direct call to the constructor
+ return makeNetworkSpecifierInternal("android.net.TestNetworkSpecifier", ifName)
+ }
+
+ @JvmStatic
+ fun makeEthernetNetworkSpecifier(ifName: String): NetworkSpecifier {
+ // Until R, there was no EthernetNetworkSpecifier, StringNetworkSpecifier was used instead
+ if (isDevSdkInRange(minExclusive = null, maxInclusive = Build.VERSION_CODES.R)) {
+ makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
+ }
+ // EthernetNetworkSpecifier is not part of the SDK in some branches using this utility
+ // TODO: replace with a direct call to the constructor
+ return makeNetworkSpecifierInternal("android.net.EthernetNetworkSpecifier", ifName)
+ }
+
+ private fun makeNetworkSpecifierInternal(clazz: String, specifier: String): NetworkSpecifier {
+ // StringNetworkSpecifier was removed after R (and was hidden API before that)
+ return Class.forName(clazz)
+ .getConstructor(String::class.java).newInstance(specifier) as NetworkSpecifier
+ }
+}
\ No newline at end of file
diff --git a/staticlibs/devicetests/com/android/testutils/TestNetworkTracker.kt b/staticlibs/devicetests/com/android/testutils/TestNetworkTracker.kt
index 4bd9ae8..5b97861 100644
--- a/staticlibs/devicetests/com/android/testutils/TestNetworkTracker.kt
+++ b/staticlibs/devicetests/com/android/testutils/TestNetworkTracker.kt
@@ -23,7 +23,6 @@
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
-import android.net.StringNetworkSpecifier
import android.net.TestNetworkInterface
import android.net.TestNetworkManager
import android.os.Binder
@@ -68,7 +67,7 @@
// Test networks do not have NOT_VPN or TRUSTED capabilities by default
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .setNetworkSpecifier(StringNetworkSpecifier(iface.interfaceName))
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(iface.interfaceName))
.build()
networkCallback = object : NetworkCallback() {
override fun onAvailable(network: Network) {
diff --git a/staticlibs/framework/com/android/net/module/util/CollectionUtils.java b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
index 115f19d..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
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/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);
+ }
}