Mark ab/7061308 as merged in stage.
Bug: 180401296
Merged-In: I449496e24ddbb70e848a97b9f26bda5859a9a9db
Change-Id: I2c4ae09cc96c294cb190e9fb0ae100b14dc87c21
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index c7a4bd5..a3bfbce 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -27,6 +27,10 @@
// included in the bootclasspath, they could incorrectly be included in the SDK documentation even
// though they are not in the current.txt files.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
java_library {
name: "net-utils-device-common",
srcs: [
@@ -73,6 +77,7 @@
host_supported: true,
visibility: [
"//frameworks/libs/net/common/tests:__subpackages__",
+ "//frameworks/libs/net/client-libs/tests:__subpackages__",
],
static_libs: [
"kotlin-test"
@@ -149,7 +154,6 @@
name: "net-utils-services-common-srcs",
srcs: [
"device/android/net/NetworkFactory.java",
- "device/com/android/net/module/util/CollectionUtils.java",
],
visibility: [
"//frameworks/base/services/net",
@@ -163,6 +167,7 @@
":framework-annotations",
],
sdk_version: "system_current",
+ min_sdk_version: "30",
visibility: [
"//frameworks/base/services/net",
],
diff --git a/staticlibs/device/android/net/NetworkFactory.java b/staticlibs/device/android/net/NetworkFactory.java
index 55edd29..bcc6089 100644
--- a/staticlibs/device/android/net/NetworkFactory.java
+++ b/staticlibs/device/android/net/NetworkFactory.java
@@ -244,6 +244,12 @@
evalRequests();
}
+ /** @deprecated None of the implementors use the score, remove this method */
+ @Deprecated
+ public boolean acceptRequest(NetworkRequest request, int score) {
+ return acceptRequest(request);
+ }
+
/**
* Overridable function to provide complex filtering.
* Called for every request every time a new NetworkRequest is seen
@@ -263,7 +269,7 @@
*
* @return {@code true} to accept the request.
*/
- public boolean acceptRequest(NetworkRequest request, int score) {
+ public boolean acceptRequest(NetworkRequest request) {
return true;
}
@@ -357,8 +363,14 @@
protected void startNetwork() { }
protected void stopNetwork() { }
- // override to do fancier stuff
+ /** @deprecated none of the implementors use the score : migrate them */
+ @Deprecated
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
+ needNetworkFor(networkRequest);
+ }
+
+ // override to do fancier stuff
+ protected void needNetworkFor(NetworkRequest networkRequest) {
if (++mRefCount == 1) startNetwork();
}
diff --git a/staticlibs/device/com/android/net/module/util/HexDump.java b/staticlibs/device/com/android/net/module/util/HexDump.java
new file mode 100644
index 0000000..6d36487
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/HexDump.java
@@ -0,0 +1,236 @@
+/*
+ * 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 android.annotation.Nullable;
+
+/**
+ * Hex utility functions.
+ */
+public class HexDump {
+ private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F' };
+ private static final char[] HEX_LOWER_CASE_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ /**
+ * Dump the hex string corresponding to the specified byte array.
+ *
+ * @param array byte array to be dumped.
+ */
+ public static String dumpHexString(@Nullable byte[] array) {
+ if (array == null) return "(null)";
+ return dumpHexString(array, 0, array.length);
+ }
+
+ /**
+ * Dump the hex string corresponding to the specified byte array.
+ *
+ * @param array byte array to be dumped.
+ * @param offset the offset in array where dump should start.
+ * @param length the length of bytes to be dumped.
+ */
+ public static String dumpHexString(@Nullable byte[] array, int offset, int length) {
+ if (array == null) return "(null)";
+ StringBuilder result = new StringBuilder();
+
+ byte[] line = new byte[16];
+ int lineIndex = 0;
+
+ result.append("\n0x");
+ result.append(toHexString(offset));
+
+ for (int i = offset; i < offset + length; i++) {
+ if (lineIndex == 16) {
+ result.append(" ");
+
+ for (int j = 0; j < 16; j++) {
+ if (line[j] > ' ' && line[j] < '~') {
+ result.append(new String(line, j, 1));
+ } else {
+ result.append(".");
+ }
+ }
+
+ result.append("\n0x");
+ result.append(toHexString(i));
+ lineIndex = 0;
+ }
+
+ byte b = array[i];
+ result.append(" ");
+ result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
+ result.append(HEX_DIGITS[b & 0x0F]);
+
+ line[lineIndex++] = b;
+ }
+
+ if (lineIndex != 16) {
+ int count = (16 - lineIndex) * 3;
+ count++;
+ for (int i = 0; i < count; i++) {
+ result.append(" ");
+ }
+
+ for (int i = 0; i < lineIndex; i++) {
+ if (line[i] > ' ' && line[i] < '~') {
+ result.append(new String(line, i, 1));
+ } else {
+ result.append(".");
+ }
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Convert a byte to an uppercase hex string.
+ *
+ * @param b the byte to be converted.
+ */
+ public static String toHexString(byte b) {
+ return toHexString(toByteArray(b));
+ }
+
+ /**
+ * Convert a byte array to an uppercase hex string.
+ *
+ * @param array the byte array to be converted.
+ */
+ public static String toHexString(byte[] array) {
+ return toHexString(array, 0, array.length, true);
+ }
+
+ /**
+ * Convert a byte array to a hex string.
+ *
+ * @param array the byte array to be converted.
+ * @param upperCase whether the converted hex string should be uppercase or not.
+ */
+ public static String toHexString(byte[] array, boolean upperCase) {
+ return toHexString(array, 0, array.length, upperCase);
+ }
+
+ /**
+ * Convert a byte array to hex string.
+ *
+ * @param array the byte array to be converted.
+ * @param offset the offset in array where conversion should start.
+ * @param length the length of bytes to be converted.
+ */
+ public static String toHexString(byte[] array, int offset, int length) {
+ return toHexString(array, offset, length, true);
+ }
+
+ /**
+ * Convert a byte array to hex string.
+ *
+ * @param array the byte array to be converted.
+ * @param offset the offset in array where conversion should start.
+ * @param length the length of bytes to be converted.
+ * @param upperCase whether the converted hex string should be uppercase or not.
+ */
+ public static String toHexString(byte[] array, int offset, int length, boolean upperCase) {
+ char[] digits = upperCase ? HEX_DIGITS : HEX_LOWER_CASE_DIGITS;
+ char[] buf = new char[length * 2];
+
+ int bufIndex = 0;
+ for (int i = offset; i < offset + length; i++) {
+ byte b = array[i];
+ buf[bufIndex++] = digits[(b >>> 4) & 0x0F];
+ buf[bufIndex++] = digits[b & 0x0F];
+ }
+
+ return new String(buf);
+ }
+
+ /**
+ * Convert an integer to hex string.
+ *
+ * @param i the integer to be converted.
+ */
+ public static String toHexString(int i) {
+ return toHexString(toByteArray(i));
+ }
+
+ /**
+ * Convert a byte to byte array.
+ *
+ * @param b the byte to be converted.
+ */
+ public static byte[] toByteArray(byte b) {
+ byte[] array = new byte[1];
+ array[0] = b;
+ return array;
+ }
+
+ /**
+ * Convert an integer to byte array.
+ *
+ * @param i the integer to be converted.
+ */
+ public static byte[] toByteArray(int i) {
+ byte[] array = new byte[4];
+
+ array[3] = (byte) (i & 0xFF);
+ array[2] = (byte) ((i >> 8) & 0xFF);
+ array[1] = (byte) ((i >> 16) & 0xFF);
+ array[0] = (byte) ((i >> 24) & 0xFF);
+
+ return array;
+ }
+
+ private static int toByte(char c) {
+ if (c >= '0' && c <= '9') return (c - '0');
+ if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
+ if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
+
+ throw new RuntimeException("Invalid hex char '" + c + "'");
+ }
+
+ /**
+ * Convert a hex string to a byte array.
+ *
+ * @param hexString the string to be converted.
+ */
+ public static byte[] hexStringToByteArray(String hexString) {
+ int length = hexString.length();
+ byte[] buffer = new byte[length / 2];
+
+ for (int i = 0; i < length; i += 2) {
+ buffer[i / 2] =
+ (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i + 1)));
+ }
+
+ return buffer;
+ }
+
+ /**
+ * Convert a byte to hex string and append it to StringBuilder.
+ *
+ * @param sb StringBuilder instance.
+ * @param b the byte to be converted.
+ * @param upperCase whether the converted hex string should be uppercase or not.
+ */
+ public static StringBuilder appendByteAsHex(StringBuilder sb, byte b, boolean upperCase) {
+ char[] digits = upperCase ? HEX_DIGITS : HEX_LOWER_CASE_DIGITS;
+ sb.append(digits[(b >> 4) & 0xf]);
+ sb.append(digits[b & 0xf]);
+ return sb;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/Struct.java b/staticlibs/device/com/android/net/module/util/Struct.java
index 6d8d301..f11a5ac 100644
--- a/staticlibs/device/com/android/net/module/util/Struct.java
+++ b/staticlibs/device/com/android/net/module/util/Struct.java
@@ -27,9 +27,15 @@
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;
+import java.util.Arrays;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -97,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, // a 48-bits long MAC address
+ 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
}
/**
@@ -184,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());
}
@@ -221,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());
}
@@ -386,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());
}
@@ -459,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());
}
@@ -602,4 +638,83 @@
writeToByteBufferInternal(buffer, fieldInfos);
return output;
}
+
+ /** Convert the parsed Struct subclass object to byte array with native order. */
+ public final byte[] writeToBytes() {
+ return writeToBytes(ByteOrder.nativeOrder());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || this.getClass() != obj.getClass()) return false;
+
+ final FieldInfo[] fieldInfos = getClassFieldInfo(this.getClass());
+ for (int i = 0; i < fieldInfos.length; i++) {
+ try {
+ final Object value = fieldInfos[i].field.get(this);
+ final Object otherValue = fieldInfos[i].field.get(obj);
+
+ // Use Objects#deepEquals because the equals method on arrays does not check the
+ // contents of the array. The only difference between Objects#deepEquals and
+ // Objects#equals is that the former will call Arrays#deepEquals when comparing
+ // arrays. In turn, the only difference between Arrays#deepEquals is that it
+ // supports nested arrays. Struct does not currently support these, and if it did,
+ // Objects#deepEquals might be more correct.
+ if (!Objects.deepEquals(value, otherValue)) return false;
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException("Cannot access field: " + fieldInfos[i].field, e);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ 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);
+ }
+ // 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.
+ if (fieldInfos[i].field.getType() == byte[].class) {
+ values[i] = Arrays.hashCode((byte[]) value);
+ } else {
+ values[i] = value;
+ }
+ }
+ return Objects.hash(values);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ 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);
+ }
+ if (value == null) {
+ sb.append("null");
+ } 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());
+ }
+ if (i != fieldInfos.length - 1) sb.append(", ");
+ }
+ return sb.toString();
+ }
}
diff --git a/staticlibs/device/com/android/net/module/util/CollectionUtils.java b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
similarity index 79%
rename from staticlibs/device/com/android/net/module/util/CollectionUtils.java
rename to staticlibs/framework/com/android/net/module/util/CollectionUtils.java
index 74f738d..cb1e3e7 100644
--- a/staticlibs/device/com/android/net/module/util/CollectionUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
@@ -66,6 +66,28 @@
}
/**
+ * @return True if all elements satisfy the predicate, false otherwise.
+ * Note that means this always returns true for empty collections.
+ */
+ public static <T> boolean all(@NonNull Collection<T> elem, @NonNull Predicate<T> predicate) {
+ for (final T e : elem) {
+ if (!predicate.test(e)) return false;
+ }
+ 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) {
+ for (final T e : elem) {
+ if (predicate.test(e)) return true;
+ }
+ return false;
+ }
+
+ /**
* @return True if there exists at least one element in the sparse array for which
* condition {@code predicate}
*/
diff --git a/staticlibs/framework/com/android/net/module/util/NetUtils.java b/staticlibs/framework/com/android/net/module/util/NetUtils.java
index 4331b65..f08257a 100644
--- a/staticlibs/framework/com/android/net/module/util/NetUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/NetUtils.java
@@ -23,6 +23,7 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.Collection;
/**
@@ -67,4 +68,44 @@
}
return bestRoute;
}
+
+ /**
+ * Get InetAddress masked with prefixLength. Will never return null.
+ * @param address the IP address to mask with
+ * @param prefixLength the prefixLength used to mask the IP
+ */
+ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+ byte[] array = address.getAddress();
+ maskRawAddress(array, prefixLength);
+
+ InetAddress netPart = null;
+ try {
+ netPart = InetAddress.getByAddress(array);
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("getNetworkPart error - " + e.toString());
+ }
+ return netPart;
+ }
+
+ /**
+ * Masks a raw IP address byte array with the specified prefix length.
+ */
+ public static void maskRawAddress(byte[] array, int prefixLength) {
+ if (prefixLength < 0 || prefixLength > array.length * 8) {
+ throw new RuntimeException("IP address with " + array.length
+ + " bytes has invalid prefix length " + prefixLength);
+ }
+
+ int offset = prefixLength / 8;
+ int remainder = prefixLength % 8;
+ byte mask = (byte) (0xFF << (8 - remainder));
+
+ if (offset < array.length) array[offset] = (byte) (array[offset] & mask);
+
+ offset++;
+
+ for (; offset < array.length; offset++) {
+ array[offset] = 0;
+ }
+ }
}
diff --git a/staticlibs/device/com/android/net/module/util/NetworkCapabilitiesUtils.java b/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
similarity index 98%
rename from staticlibs/device/com/android/net/module/util/NetworkCapabilitiesUtils.java
rename to staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
index dd5a481..5a0200f 100644
--- a/staticlibs/device/com/android/net/module/util/NetworkCapabilitiesUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
@@ -23,7 +23,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
-import androidx.annotation.NonNull;
+import android.annotation.NonNull;
+
/**
* Utilities to examine {@link android.net.NetworkCapabilities}.
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkIdentityUtils.java b/staticlibs/framework/com/android/net/module/util/NetworkIdentityUtils.java
new file mode 100644
index 0000000..94e6017
--- /dev/null
+++ b/staticlibs/framework/com/android/net/module/util/NetworkIdentityUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Utilities to examine {@link android.net.NetworkIdentity}.
+ */
+public class NetworkIdentityUtils {
+ /**
+ * Scrub given IMSI on production builds.
+ */
+ @NonNull
+ public static String scrubSubscriberId(@Nullable String subscriberId) {
+ if (subscriberId != null) {
+ // TODO: parse this as MCC+MNC instead of hard-coding
+ return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
+ } else {
+ return "null";
+ }
+ }
+
+ /**
+ * Scrub given IMSI on production builds.
+ */
+ @Nullable
+ public static String[] scrubSubscriberIds(@Nullable String[] subscriberIds) {
+ if (subscriberIds == null) return null;
+ final String[] res = new String[subscriberIds.length];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = scrubSubscriberId(subscriberIds[i]);
+ }
+ return res;
+ }
+}
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index a227505..5f62186 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -16,7 +16,10 @@
package com.android.net.module.util;
+import android.net.InetAddresses;
+
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -51,8 +54,6 @@
(byte) 0xff, (byte) 0xff, (byte) 0xff,
};
- public static final int DEFAULT_LINK_MTU = 1500;
-
/**
* ARP constants.
*
@@ -98,7 +99,11 @@
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff);
public static final Inet4Address IPV4_ADDR_ANY = makeInet4Address(
(byte) 0, (byte) 0, (byte) 0, (byte) 0);
-
+ public static final Inet6Address IPV6_ADDR_ANY = makeInet6Address(new byte[]{
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0 });
/**
* IPv6 constants.
*
@@ -112,6 +117,12 @@
public static final int IPV6_SRC_ADDR_OFFSET = 8;
public static final int IPV6_DST_ADDR_OFFSET = 24;
public static final int IPV6_MIN_MTU = 1280;
+ public static final Inet6Address IPV6_ADDR_ALL_NODES_MULTICAST =
+ (Inet6Address) InetAddresses.parseNumericAddress("ff02::1");
+ public static final Inet6Address IPV6_ADDR_ALL_ROUTERS_MULTICAST =
+ (Inet6Address) InetAddresses.parseNumericAddress("ff02::2");
+ public static final Inet6Address IPV6_ADDR_ALL_HOSTS_MULTICAST =
+ (Inet6Address) InetAddresses.parseNumericAddress("ff02::3");
/**
* ICMPv6 constants.
@@ -182,6 +193,16 @@
}
}
+ /**
+ * Make an Inet6Address from 16 bytes in network byte order.
+ */
+ private static Inet6Address makeInet6Address(byte[] bytes) {
+ try {
+ return (Inet6Address) InetAddress.getByAddress(bytes);
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException("addr must be 16 bytes: this should never happen");
+ }
+ }
private NetworkStackConstants() {
throw new UnsupportedOperationException("This class is not to be instantiated");
}
diff --git a/staticlibs/framework/com/android/net/module/util/PermissionUtils.java b/staticlibs/framework/com/android/net/module/util/PermissionUtils.java
new file mode 100644
index 0000000..ce8a745
--- /dev/null
+++ b/staticlibs/framework/com/android/net/module/util/PermissionUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.content.Context;
+
+/**
+ * Collection of permission utilities.
+ * @hide
+ */
+public final class PermissionUtils {
+ /**
+ * Return true if the context has one of given permission.
+ */
+ public static boolean checkAnyPermissionOf(@NonNull Context context,
+ @NonNull String... permissions) {
+ for (String permission : permissions) {
+ if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Enforce permission check on the context that should have one of given permission.
+ */
+ public static void enforceAnyPermissionOf(@NonNull Context context,
+ @NonNull String... permissions) {
+ if (!checkAnyPermissionOf(context, permissions)) {
+ throw new SecurityException("Requires one of the following permissions: "
+ + String.join(", ", permissions) + ".");
+ }
+ }
+}
diff --git a/staticlibs/framework/com/android/net/module/util/ProxyUtils.java b/staticlibs/framework/com/android/net/module/util/ProxyUtils.java
index a7b8393..fdd7dca 100644
--- a/staticlibs/framework/com/android/net/module/util/ProxyUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/ProxyUtils.java
@@ -22,6 +22,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Collection of network common utilities.
@@ -30,6 +32,28 @@
*/
public final class ProxyUtils {
+ public static final int PROXY_VALID = 0;
+ public static final int PROXY_HOSTNAME_EMPTY = 1;
+ public static final int PROXY_HOSTNAME_INVALID = 2;
+ public static final int PROXY_PORT_EMPTY = 3;
+ public static final int PROXY_PORT_INVALID = 4;
+ public static final int PROXY_EXCLLIST_INVALID = 5;
+
+ // Hostname / IP REGEX validation
+ // Matches blank input, ips, and domain names
+ private static final String NAME_IP_REGEX =
+ "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
+ private static final Pattern HOSTNAME_PATTERN;
+ private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
+ private static final Pattern EXCLLIST_PATTERN;
+ private static final String EXCL_REGEX =
+ "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
+ private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
+ static {
+ HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
+ EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
+ }
+
/** Converts exclusion list from String to List. */
public static List<String> exclusionStringAsList(String exclusionList) {
if (exclusionList == null) {
@@ -45,4 +69,30 @@
}
return TextUtils.join(",", exclusionList);
}
+
+ /**
+ * Validate syntax of hostname, port and exclusion list entries
+ */
+ public static int validate(String hostname, String port, String exclList) {
+ Matcher match = HOSTNAME_PATTERN.matcher(hostname);
+ Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
+
+ if (!match.matches()) return PROXY_HOSTNAME_INVALID;
+
+ if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
+
+ if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
+
+ if (port.length() > 0) {
+ if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
+ int portVal = -1;
+ try {
+ portVal = Integer.parseInt(port);
+ } catch (NumberFormatException ex) {
+ return PROXY_PORT_INVALID;
+ }
+ if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
+ }
+ return PROXY_VALID;
+ }
}
diff --git a/staticlibs/native/OWNERS b/staticlibs/native/OWNERS
new file mode 100644
index 0000000..7655338
--- /dev/null
+++ b/staticlibs/native/OWNERS
@@ -0,0 +1 @@
+maze@google.com
diff --git a/staticlibs/native/bpf_syscall_wrappers/Android.bp b/staticlibs/native/bpf_syscall_wrappers/Android.bp
new file mode 100644
index 0000000..fa90655
--- /dev/null
+++ b/staticlibs/native/bpf_syscall_wrappers/Android.bp
@@ -0,0 +1,39 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_headers {
+ name: "bpf_syscall_wrappers",
+ vendor_available: false,
+ host_supported: false,
+ native_bridge_supported: true,
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ sdk_version: "30",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ ],
+ visibility: [
+ "//packages/modules/Connectivity/Tethering",
+ "//system/bpf/libbpf_android",
+ ],
+}
diff --git a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
new file mode 100644
index 0000000..72eebf3
--- /dev/null
+++ b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <linux/bpf.h>
+#include <linux/unistd.h>
+
+#ifdef BPF_FD_JUST_USE_INT
+ #define BPF_FD_TYPE int
+ #define BPF_FD_TO_U32(x) static_cast<__u32>(x)
+#else
+ #include <android-base/unique_fd.h>
+ #define BPF_FD_TYPE base::unique_fd&
+ #define BPF_FD_TO_U32(x) static_cast<__u32>((x).get())
+#endif
+
+#define ptr_to_u64(x) ((uint64_t)(uintptr_t)(x))
+
+namespace android {
+namespace bpf {
+
+/* Note: bpf_attr is a union which might have a much larger size then the anonymous struct portion
+ * of it that we are using. The kernel's bpf() system call will perform a strict check to ensure
+ * all unused portions are zero. It will fail with E2BIG if we don't fully zero bpf_attr.
+ */
+
+inline int bpf(int cmd, const bpf_attr& attr) {
+ return syscall(__NR_bpf, cmd, &attr, sizeof(attr));
+}
+
+inline int createMap(bpf_map_type map_type, uint32_t key_size, uint32_t value_size,
+ uint32_t max_entries, uint32_t map_flags) {
+ return bpf(BPF_MAP_CREATE, {
+ .map_type = map_type,
+ .key_size = key_size,
+ .value_size = value_size,
+ .max_entries = max_entries,
+ .map_flags = map_flags,
+ });
+}
+
+inline int writeToMapEntry(const BPF_FD_TYPE map_fd, const void* key, const void* value,
+ uint64_t flags) {
+ return bpf(BPF_MAP_UPDATE_ELEM, {
+ .map_fd = BPF_FD_TO_U32(map_fd),
+ .key = ptr_to_u64(key),
+ .value = ptr_to_u64(value),
+ .flags = flags,
+ });
+}
+
+inline int findMapEntry(const BPF_FD_TYPE map_fd, const void* key, void* value) {
+ return bpf(BPF_MAP_LOOKUP_ELEM, {
+ .map_fd = BPF_FD_TO_U32(map_fd),
+ .key = ptr_to_u64(key),
+ .value = ptr_to_u64(value),
+ });
+}
+
+inline int deleteMapEntry(const BPF_FD_TYPE map_fd, const void* key) {
+ return bpf(BPF_MAP_DELETE_ELEM, {
+ .map_fd = BPF_FD_TO_U32(map_fd),
+ .key = ptr_to_u64(key),
+ });
+}
+
+inline int getNextMapKey(const BPF_FD_TYPE map_fd, const void* key, void* next_key) {
+ return bpf(BPF_MAP_GET_NEXT_KEY, {
+ .map_fd = BPF_FD_TO_U32(map_fd),
+ .key = ptr_to_u64(key),
+ .next_key = ptr_to_u64(next_key),
+ });
+}
+
+inline int getFirstMapKey(const BPF_FD_TYPE map_fd, void* firstKey) {
+ return getNextMapKey(map_fd, NULL, firstKey);
+}
+
+inline int bpfFdPin(const BPF_FD_TYPE map_fd, const char* pathname) {
+ return bpf(BPF_OBJ_PIN, {
+ .pathname = ptr_to_u64(pathname),
+ .bpf_fd = BPF_FD_TO_U32(map_fd),
+ });
+}
+
+inline int bpfFdGet(const char* pathname, uint32_t flag) {
+ return bpf(BPF_OBJ_GET, {
+ .pathname = ptr_to_u64(pathname),
+ .file_flags = flag,
+ });
+}
+
+inline int mapRetrieve(const char* pathname, uint32_t flag) {
+ return bpfFdGet(pathname, flag);
+}
+
+inline int mapRetrieveRW(const char* pathname) {
+ return mapRetrieve(pathname, 0);
+}
+
+inline int mapRetrieveRO(const char* pathname) {
+ return mapRetrieve(pathname, BPF_F_RDONLY);
+}
+
+inline int mapRetrieveWO(const char* pathname) {
+ return mapRetrieve(pathname, BPF_F_WRONLY);
+}
+
+inline int retrieveProgram(const char* pathname) {
+ return bpfFdGet(pathname, BPF_F_RDONLY);
+}
+
+inline int attachProgram(bpf_attach_type type, const BPF_FD_TYPE prog_fd,
+ const BPF_FD_TYPE cg_fd) {
+ return bpf(BPF_PROG_ATTACH, {
+ .target_fd = BPF_FD_TO_U32(cg_fd),
+ .attach_bpf_fd = BPF_FD_TO_U32(prog_fd),
+ .attach_type = type,
+ });
+}
+
+inline int detachProgram(bpf_attach_type type, const BPF_FD_TYPE cg_fd) {
+ return bpf(BPF_PROG_DETACH, {
+ .target_fd = BPF_FD_TO_U32(cg_fd),
+ .attach_type = type,
+ });
+}
+
+} // namespace bpf
+} // namespace android
+
+#undef ptr_to_u64
+#undef BPF_FD_TO_U32
+#undef BPF_FD_TYPE
+#undef BPF_FD_JUST_USE_INT
diff --git a/staticlibs/native/netjniutils/Android.bp b/staticlibs/native/netjniutils/Android.bp
index 8417c52..d8e6a04 100644
--- a/staticlibs/native/netjniutils/Android.bp
+++ b/staticlibs/native/netjniutils/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_library_static {
name: "libnetjniutils",
srcs: ["netjniutils.cpp"],
diff --git a/staticlibs/tests/unit/Android.bp b/staticlibs/tests/unit/Android.bp
index c00202e..4ce4b0e 100644
--- a/staticlibs/tests/unit/Android.bp
+++ b/staticlibs/tests/unit/Android.bp
@@ -2,6 +2,10 @@
// Build NetworkStaticLibTests package
//########################################################################
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_library {
name: "NetworkStaticLibTestsLib",
srcs: ["src/**/*.java","src/**/*.kt"],
@@ -38,4 +42,3 @@
jarjar_rules: "jarjar-rules.txt",
test_suites: ["device-tests"],
}
-
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
new file mode 100644
index 0000000..0007742
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
@@ -0,0 +1,54 @@
+/*
+ * 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 androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CollectionUtilsTest {
+ @Test
+ fun testAny() {
+ assertTrue(CollectionUtils.any(listOf("A", "B", "C", "D", "E")) { it == "E" })
+ assertFalse(CollectionUtils.any(listOf("A", "B", "C", "D", "E")) { it == "F" })
+ assertTrue(CollectionUtils.any(listOf("AA", "BBB")) { it.length >= 3 })
+ assertFalse(CollectionUtils.any(listOf("A", "BB", "CCC")) { it.length >= 4 })
+ assertFalse(CollectionUtils.any(listOf("A", "BB", "CCC")) { it.length < 0 })
+ assertFalse(CollectionUtils.any(listOf<String>()) { true })
+ assertFalse(CollectionUtils.any(listOf<String>()) { false })
+ assertTrue(CollectionUtils.any(listOf("A")) { true })
+ assertFalse(CollectionUtils.any(listOf("A")) { false })
+ }
+
+ @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" })
+ assertFalse(CollectionUtils.all(listOf("A", "BB", "CCC")) { it.length > 2 })
+ assertTrue(CollectionUtils.all(listOf("A", "BB", "CCC")) { it.length >= 1 })
+ assertTrue(CollectionUtils.all(listOf("A", "BB", "CCC")) { it.length < 4 })
+ assertTrue(CollectionUtils.all(listOf<String>()) { true })
+ assertTrue(CollectionUtils.all(listOf<String>()) { false })
+ assertTrue(CollectionUtils.all(listOf(1)) { true })
+ assertFalse(CollectionUtils.all(listOf(1)) { false })
+ }
+}
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
new file mode 100644
index 0000000..5a15585
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/HexDumpTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class HexDumpTest {
+ @Test
+ public void testBytesToHexString() {
+ assertEquals("abcdef", HexDump.toHexString(
+ new byte[]{(byte) 0xab, (byte) 0xcd, (byte) 0xef}, false));
+ assertEquals("ABCDEF", HexDump.toHexString(
+ new byte[]{(byte) 0xab, (byte) 0xcd, (byte) 0xef}, true));
+ }
+
+ @Test
+ public void testNullArray() {
+ assertEquals("(null)", HexDump.dumpHexString(null));
+ }
+
+ @Test
+ public void testHexStringToByteArray() {
+ assertArrayEquals(new byte[]{(byte) 0xab, (byte) 0xcd, (byte) 0xef},
+ HexDump.hexStringToByteArray("abcdef"));
+ assertArrayEquals(new byte[]{(byte) 0xAB, (byte) 0xCD, (byte) 0xEF},
+ HexDump.hexStringToByteArray("ABCDEF"));
+ }
+
+ @Test
+ public void testIntegerToByteArray() {
+ assertArrayEquals(new byte[]{(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x04},
+ HexDump.toByteArray((int) 0xff000004));
+ }
+
+ @Test
+ public void testByteToByteArray() {
+ assertArrayEquals(new byte[]{(byte) 0x7f}, HexDump.toByteArray((byte) 0x7f));
+ }
+
+ @Test
+ public void testIntegerToHexString() {
+ assertEquals("FF000004", HexDump.toHexString((int) 0xff000004));
+ }
+
+ @Test
+ public void testByteToHexString() {
+ assertEquals("7F", HexDump.toHexString((byte) 0x7f));
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/NetworkIdentityUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/NetworkIdentityUtilsTest.kt
new file mode 100644
index 0000000..2904e12
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/NetworkIdentityUtilsTest.kt
@@ -0,0 +1,49 @@
+/*
+ * 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 androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.net.module.util.NetworkIdentityUtils.scrubSubscriberId
+import com.android.net.module.util.NetworkIdentityUtils.scrubSubscriberIds
+import com.android.testutils.assertContainsStringsExactly
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkIdentityUtilsTest {
+ @Test
+ fun testScrubSubscriberId() {
+ assertEquals("123456...", scrubSubscriberId("1234567890123"))
+ assertEquals("123456...", scrubSubscriberId("1234567"))
+ assertEquals("123...", scrubSubscriberId("123"))
+ assertEquals("...", scrubSubscriberId(""))
+ assertEquals("null", scrubSubscriberId(null))
+ }
+
+ @Test
+ fun testScrubSubscriberIds() {
+ assertContainsStringsExactly(scrubSubscriberIds(arrayOf("1234567", "", null))!!,
+ "123456...", "...", "null")
+ assertContainsStringsExactly(scrubSubscriberIds(arrayOf("12345"))!!, "12345...")
+ assertContainsStringsExactly(scrubSubscriberIds(arrayOf())!!)
+ assertNull(scrubSubscriberIds(null))
+ }
+}
\ No newline at end of file
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 47b34b9..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
@@ -20,8 +20,11 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.MacAddress;
@@ -36,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;
@@ -67,6 +72,15 @@
// PREF64 option, 2001:db8:3:4:5:6::/96, lifetime: 10064
private static final String OPT_PREF64 = "2750" + "20010db80003000400050006";
+ private static final byte[] TEST_PREFIX64 = new byte[]{
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, (byte) 0x00, (byte) 0x03,
+ (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) {
@@ -697,4 +711,320 @@
assertThrows(IllegalArgumentException.class,
() -> Struct.parse(BadMacAddressType.class, toByteBuffer("ffffffffffff")));
}
+
+ @Test
+ public void testStructToByteArrayRoundTrip() {
+ final SignedDataMessage littleEndianMsg = doParsingMessageTest(SIGNED_DATA,
+ SignedDataMessage.class, ByteOrder.LITTLE_ENDIAN);
+ assertArrayEquals(toByteBuffer(SIGNED_DATA).array(),
+ littleEndianMsg.writeToBytes(ByteOrder.LITTLE_ENDIAN));
+
+ final SignedDataMessage bigEndianMsg = doParsingMessageTest(SIGNED_DATA,
+ SignedDataMessage.class, ByteOrder.BIG_ENDIAN);
+ assertArrayEquals(toByteBuffer(SIGNED_DATA).array(),
+ bigEndianMsg.writeToBytes(ByteOrder.BIG_ENDIAN));
+
+ final SignedDataMessage nativeOrderMsg = ByteOrder.nativeOrder().equals(
+ ByteOrder.LITTLE_ENDIAN) ? littleEndianMsg : bigEndianMsg;
+ assertArrayEquals(toByteBuffer(SIGNED_DATA).array(),
+ nativeOrderMsg.writeToBytes());
+ }
+
+ @Test
+ public void testStructToByteArray() {
+ final SignedDataMessage msg = new SignedDataMessage((byte) -5, (short) 42, (int) 0xff000004,
+ (long) 0xff000004ff000005L);
+ final String leHexString = "fb" + "2a00" + "040000ff" + "050000ff040000ff";
+ final String beHexString = "fb" + "002a" + "ff000004" + "ff000004ff000005";
+ final String hexString = ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)
+ ? leHexString : beHexString;
+ 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;
+ @Field(order = 2, type = Type.U32) public final long u32;
+ @Field(order = 3, type = Type.U63) public final long u63;
+ @Field(order = 4, type = Type.U64) public final BigInteger u64;
+ @Field(order = 5, type = Type.S8) public final byte s8;
+ @Field(order = 6, type = Type.S16) public final short s16;
+ @Field(order = 7, type = Type.S32) public final int s32;
+ @Field(order = 8, type = Type.S64) public final long s64;
+ @Field(order = 9, type = Type.UBE16) public final int ube16;
+ @Field(order = 10, type = Type.UBE32) public final long ube32;
+ @Field(order = 11, type = Type.UBE63) public final long ube63;
+ @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 Inet4Address ipv4Address,
+ final Inet6Address ipv6Address) {
+ this.u8 = u8;
+ this.u16 = u16;
+ this.u32 = u32;
+ this.u63 = u63;
+ this.u64 = u64;
+ this.s8 = s8;
+ this.s16 = s16;
+ this.s32 = s32;
+ this.s64 = s64;
+ this.ube16 = ube16;
+ this.ube32 = ube32;
+ this.ube63 = ube63;
+ 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" + "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"
+ + "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"
+ + "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"
+ + "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,
+ ByteOrder.BIG_ENDIAN);
+
+ assertEquals(255, msg.u8);
+ assertEquals(65535, msg.u16);
+ assertEquals(4294967295L, msg.u32);
+ assertEquals(9223372036854775807L, msg.u63);
+ assertEquals(new BigInteger("18446744073709551615"), msg.u64);
+ assertEquals(127, msg.s8);
+ assertEquals(32767, msg.s16);
+ assertEquals(2147483647, msg.s32);
+ assertEquals(9223372036854775807L, msg.s64);
+ assertEquals(32767, msg.ube16);
+ assertEquals(2147483647, msg.ube32);
+ assertEquals(9223372036854775807L, msg.ube63);
+ 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(98, msg.getSize(FullTypeMessage.class));
+ assertArrayEquals(toByteBuffer(FULL_TYPE_DATA).array(),
+ msg.writeToBytes(ByteOrder.BIG_ENDIAN));
+
+ final FullTypeMessage msg1 = new FullTypeMessage((short) 0xff, (int) 0xffff,
+ (long) 0xffffffffL, (long) 0x7fffffffffffffffL,
+ 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"), TEST_IPV4_ADDRESS, TEST_IPV6_ADDRESS);
+ assertTrue(msg.equals(msg1));
+ }
+
+ static class FullTypeMessageWithDupType extends Struct {
+ @Field(order = 0, type = Type.U8) public final short u8;
+ @Field(order = 1, type = Type.U16) public final int u16;
+ @Field(order = 2, type = Type.U32) public final long u32;
+ @Field(order = 3, type = Type.S64) public final long u63; // old: U63, new: S64
+ @Field(order = 4, type = Type.UBE64) public final BigInteger u64; // old: U64, new: UBE64
+ @Field(order = 5, type = Type.S8) public final byte s8;
+ @Field(order = 6, type = Type.S16) public final short s16;
+ @Field(order = 7, type = Type.S32) public final int s32;
+ @Field(order = 8, type = Type.S64) public final long s64;
+ @Field(order = 9, type = Type.U16) public final int ube16; // old:UBE16, new: U16
+ @Field(order = 10, type = Type.UBE32) public final long ube32;
+ @Field(order = 11, type = Type.UBE63) public final long ube63;
+ @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 Inet4Address ipv4Address,
+ final Inet6Address ipv6Address) {
+ this.u8 = u8;
+ this.u16 = u16;
+ this.u32 = u32;
+ this.u63 = u63;
+ this.u64 = u64;
+ this.s8 = s8;
+ this.s16 = s16;
+ this.s32 = s32;
+ this.s64 = s64;
+ this.ube16 = ube16;
+ this.ube32 = ube32;
+ this.ube63 = ube63;
+ this.ube64 = ube64;
+ this.bytes = bytes;
+ this.eui48 = eui48;
+ this.ipv4Address = ipv4Address;
+ this.ipv6Address = ipv6Address;
+ }
+ }
+
+ @Test
+ public void testStructClass_notEqualWithDifferentClass() {
+ final FullTypeMessage msg = doParsingMessageTest(FULL_TYPE_DATA, FullTypeMessage.class,
+ ByteOrder.BIG_ENDIAN);
+ final FullTypeMessageWithDupType msg1 = doParsingMessageTest(FULL_TYPE_DATA,
+ FullTypeMessageWithDupType.class, ByteOrder.BIG_ENDIAN);
+
+ assertFalse(msg.equals(msg1));
+ }
+
+ @Test
+ public void testStructClass_notEqualWithDifferentValue() {
+ final FullTypeMessage msg = doParsingMessageTest(FULL_TYPE_DATA, FullTypeMessage.class,
+ ByteOrder.BIG_ENDIAN);
+
+ // With different MAC address.
+ final FullTypeMessage msg1 = doParsingMessageTest(FULL_TYPE_DATA_DIFF_MAC,
+ FullTypeMessage.class, ByteOrder.BIG_ENDIAN);
+ assertNotEquals(msg.eui48, msg1.eui48);
+ assertFalse(msg.equals(msg1));
+
+ // With different byte array.
+ final FullTypeMessage msg2 = doParsingMessageTest(FULL_TYPE_DATA, FullTypeMessage.class,
+ ByteOrder.BIG_ENDIAN);
+ msg2.bytes[5] = (byte) 42; // change one byte in the array.
+ assertFalse(msg.equals(msg2));
+
+ // With different Long primitive.
+ final FullTypeMessage msg3 = doParsingMessageTest(FULL_TYPE_DATA_DIFF_LONG,
+ FullTypeMessage.class, ByteOrder.BIG_ENDIAN);
+ assertNotEquals(msg.u63, msg3.u63);
+ assertFalse(msg.equals(msg3));
+
+ // With different Integer primitive.
+ final FullTypeMessage msg4 = doParsingMessageTest(FULL_TYPE_DATA_DIFF_INTEGER,
+ FullTypeMessage.class, ByteOrder.BIG_ENDIAN);
+ 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
+ public void testStructClass_toString() {
+ final String expected = "u8: 255, u16: 65535, u32: 4294967295,"
+ + " u63: 9223372036854775807, u64: 18446744073709551615, s8: 127, s16: 32767,"
+ + " s32: 2147483647, s64: 9223372036854775807, ube16: 32767, ube32: 2147483647,"
+ + " ube63: 9223372036854775807, ube64: 18446744073709551615,"
+ + " bytes: 0x20010DB80003000400050006,"
+ + " 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);
+ assertEquals(expected, msg.toString());
+ }
+
+ @Test
+ public void testStructClass_toStringWithNullMember() {
+ final String expected = "u8: 255, u16: 65535, u32: 4294967295,"
+ + " u63: 9223372036854775807, u64: null, s8: 127, s16: 32767,"
+ + " s32: 2147483647, s64: 9223372036854775807, ube16: 32767, ube32: 2147483647,"
+ + " ube63: 9223372036854775807, ube64: 18446744073709551615,"
+ + " 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 */, TEST_IPV4_ADDRESS, null /* ipv6Address */);
+ assertEquals(expected, msg.toString());
+ }
+
+ @Test
+ public void testStructClass_hashcode() {
+ final FullTypeMessage msg = doParsingMessageTest(FULL_TYPE_DATA, FullTypeMessage.class,
+ ByteOrder.BIG_ENDIAN);
+ final FullTypeMessage msg1 = doParsingMessageTest(FULL_TYPE_DATA, FullTypeMessage.class,
+ ByteOrder.BIG_ENDIAN);
+
+ assertNotEquals(0, msg.hashCode());
+ assertNotEquals(0, msg1.hashCode());
+ assertTrue(msg.equals(msg1));
+ assertEquals(msg.hashCode(), msg1.hashCode());
+ }
}