Merge "[NETD-BPF#1] Move libnetdutils to framework/libs/net/..."
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index a9689f1..0585c09 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -70,6 +70,7 @@
"androidx.annotation_annotation",
"framework-annotations-lib",
],
+ lint: { strict_updatability_linting: true },
}
java_defaults {
@@ -83,6 +84,19 @@
],
}
+java_library {
+ name: "net-utils-dnspacket-common",
+ srcs: [
+ "framework/**/DnsPacket.java",
+ "framework/**/DnsPacketUtils.java",
+ ],
+ sdk_version: "module_current",
+ visibility: [
+ "//packages/services/Iwlan:__subpackages__",
+ ],
+ libs: ["framework-annotations-lib"],
+}
+
filegroup {
name: "net-utils-framework-common-srcs",
srcs: ["framework/**/*.java"],
@@ -90,7 +104,6 @@
visibility: [
"//frameworks/base",
"//packages/modules/Connectivity:__subpackages__",
- "//frameworks/base/packages/Connectivity/framework",
],
}
@@ -118,6 +131,7 @@
"com.android.tethering",
"//apex_available:platform",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
@@ -145,6 +159,7 @@
"com.android.tethering",
"//apex_available:platform",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
@@ -169,6 +184,7 @@
"com.android.tethering",
"//apex_available:platform",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
@@ -199,6 +215,7 @@
"com.android.tethering",
"//apex_available:platform",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
@@ -229,6 +246,7 @@
"//frameworks/libs/net/common/device",
"//packages/modules/Wifi/framework/tests:__subpackages__",
],
+ lint: { strict_updatability_linting: true },
}
filegroup {
name: "net-utils-services-common-srcs",
@@ -259,7 +277,9 @@
],
visibility: [
"//frameworks/base/services/net",
+ "//packages/modules/Connectivity/tests:__subpackages__",
],
+ lint: { strict_updatability_linting: true },
}
// Use a filegroup and not a library for telephony sources, as framework-annotations cannot be
@@ -339,4 +359,5 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ lint: { strict_updatability_linting: true },
}
diff --git a/staticlibs/framework/com/android/net/module/util/DnsPacket.java b/staticlibs/framework/com/android/net/module/util/DnsPacket.java
index 5ac731c..080781c 100644
--- a/staticlibs/framework/com/android/net/module/util/DnsPacket.java
+++ b/staticlibs/framework/com/android/net/module/util/DnsPacket.java
@@ -18,12 +18,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.text.TextUtils;
+
+import com.android.net.module.util.DnsPacketUtils.DnsRecordParser;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
-import java.text.DecimalFormat;
-import java.text.FieldPosition;
import java.util.ArrayList;
import java.util.List;
@@ -60,6 +59,11 @@
public final int rcode;
private final int[] mRecordCount;
+ /* If this bit in the 'flags' field is set to 0, the DNS message corresponding to this
+ * header is a query; otherwise, it is a response.
+ */
+ private static final int FLAGS_SECTION_QR_BIT = 15;
+
/**
* Create a new DnsHeader from a positioned ByteBuffer.
*
@@ -80,6 +84,14 @@
}
/**
+ * Determines if the DNS message corresponding to this header is a response, as defined in
+ * RFC 1035 Section 4.1.1.
+ */
+ public boolean isResponse() {
+ return (flags & (1 << FLAGS_SECTION_QR_BIT)) != 0;
+ }
+
+ /**
* Get record count by type.
*/
public int getRecordCount(int type) {
@@ -95,12 +107,8 @@
*/
public class DnsRecord {
private static final int MAXNAMESIZE = 255;
- private static final int MAXLABELSIZE = 63;
- private static final int MAXLABELCOUNT = 128;
public static final int NAME_NORMAL = 0;
public static final int NAME_COMPRESSION = 0xC0;
- private final DecimalFormat mByteFormat = new DecimalFormat();
- private final FieldPosition mPos = new FieldPosition(0);
private static final String TAG = "DnsRecord";
@@ -118,12 +126,13 @@
* advanced to the end of the DNS header record.
* This is meant to chain with other methods reading a DNS response in sequence.
*
- * @param ByteBuffer input of record, must be in network byte order
+ * @param buf ByteBuffer input of record, must be in network byte order
* (which is the default).
*/
DnsRecord(int recordType, @NonNull ByteBuffer buf)
throws BufferUnderflowException, ParseException {
- dName = parseName(buf, 0 /* Parse depth */);
+ dName = DnsRecordParser.parseName(buf, 0 /* Parse depth */,
+ /* isNameCompressionSupported= */ true);
if (dName.length() > MAXNAMESIZE) {
throw new ParseException(
"Parse name fail, name size is too long: " + dName.length());
@@ -150,66 +159,6 @@
return (mRdata == null) ? null : mRdata.clone();
}
- /**
- * Convert label from {@code byte[]} to {@code String}
- *
- * Follows the same conversion rules of the native code (ns_name.c in libc)
- */
- private String labelToString(@NonNull byte[] label) {
- final StringBuffer sb = new StringBuffer();
- for (int i = 0; i < label.length; ++i) {
- int b = Byte.toUnsignedInt(label[i]);
- // Control characters and non-ASCII characters.
- if (b <= 0x20 || b >= 0x7f) {
- // Append the byte as an escaped decimal number, e.g., "\19" for 0x13.
- sb.append('\\');
- mByteFormat.format(b, sb, mPos);
- } else if (b == '"' || b == '.' || b == ';' || b == '\\'
- || b == '(' || b == ')' || b == '@' || b == '$') {
- // Append the byte as an escaped character, e.g., "\:" for 0x3a.
- sb.append('\\');
- sb.append((char) b);
- } else {
- // Append the byte as a character, e.g., "a" for 0x61.
- sb.append((char) b);
- }
- }
- return sb.toString();
- }
-
- private String parseName(@NonNull ByteBuffer buf, int depth) throws
- BufferUnderflowException, ParseException {
- if (depth > MAXLABELCOUNT) {
- throw new ParseException("Failed to parse name, too many labels");
- }
- final int len = Byte.toUnsignedInt(buf.get());
- final int mask = len & NAME_COMPRESSION;
- if (0 == len) {
- return "";
- } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION) {
- throw new ParseException("Parse name fail, bad label type");
- } else if (mask == NAME_COMPRESSION) {
- // Name compression based on RFC 1035 - 4.1.4 Message compression
- final int offset = ((len & ~NAME_COMPRESSION) << 8) + Byte.toUnsignedInt(buf.get());
- final int oldPos = buf.position();
- if (offset >= oldPos - 2) {
- throw new ParseException("Parse compression name fail, invalid compression");
- }
- buf.position(offset);
- final String pointed = parseName(buf, depth + 1);
- buf.position(oldPos);
- return pointed;
- } else {
- final byte[] label = new byte[len];
- buf.get(label);
- final String head = labelToString(label);
- if (head.length() > MAXLABELSIZE) {
- throw new ParseException("Parse name fail, invalid label length");
- }
- final String tail = parseName(buf, depth + 1);
- return TextUtils.isEmpty(tail) ? head : head + "." + tail;
- }
- }
}
public static final int QDSECTION = 0;
@@ -224,7 +173,10 @@
protected final List<DnsRecord>[] mRecords;
protected DnsPacket(@NonNull byte[] data) throws ParseException {
- if (null == data) throw new ParseException("Parse header failed, null input data");
+ if (null == data) {
+ throw new ParseException("Parse header failed, null input data");
+ }
+
final ByteBuffer buffer;
try {
buffer = ByteBuffer.wrap(data);
diff --git a/staticlibs/framework/com/android/net/module/util/DnsPacketUtils.java b/staticlibs/framework/com/android/net/module/util/DnsPacketUtils.java
new file mode 100644
index 0000000..3448cad
--- /dev/null
+++ b/staticlibs/framework/com/android/net/module/util/DnsPacketUtils.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 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 com.android.net.module.util.DnsPacket.DnsRecord.NAME_COMPRESSION;
+import static com.android.net.module.util.DnsPacket.DnsRecord.NAME_NORMAL;
+
+import android.annotation.NonNull;
+import android.text.TextUtils;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+
+/**
+ * Utilities for decoding the contents of a DnsPacket.
+ *
+ * @hide
+ */
+public final class DnsPacketUtils {
+ /**
+ * Reads the passed ByteBuffer from its current position and decodes a DNS record.
+ */
+ public static class DnsRecordParser {
+ private static final int MAXLABELSIZE = 63;
+ private static final int MAXLABELCOUNT = 128;
+
+ private static final DecimalFormat sByteFormat = new DecimalFormat();
+ private static final FieldPosition sPos = new FieldPosition(0);
+
+ /**
+ * Convert label from {@code byte[]} to {@code String}
+ *
+ * <p>Follows the same conversion rules of the native code (ns_name.c in libc).
+ */
+ private static String labelToString(@NonNull byte[] label) {
+ final StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < label.length; ++i) {
+ int b = Byte.toUnsignedInt(label[i]);
+ // Control characters and non-ASCII characters.
+ if (b <= 0x20 || b >= 0x7f) {
+ // Append the byte as an escaped decimal number, e.g., "\19" for 0x13.
+ sb.append('\\');
+ sByteFormat.format(b, sb, sPos);
+ } else if (b == '"' || b == '.' || b == ';' || b == '\\' || b == '(' || b == ')'
+ || b == '@' || b == '$') {
+ // Append the byte as an escaped character, e.g., "\:" for 0x3a.
+ sb.append('\\');
+ sb.append((char) b);
+ } else {
+ // Append the byte as a character, e.g., "a" for 0x61.
+ sb.append((char) b);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Parses the domain / target name of a DNS record.
+ *
+ * As described in RFC 1035 Section 4.1.3, the NAME field of a DNS Resource Record always
+ * supports Name Compression, whereas domain names contained in the RDATA payload of a DNS
+ * record may or may not support Name Compression, depending on the record TYPE. Moreover,
+ * even if Name Compression is supported, its usage is left to the implementation.
+ */
+ public static String parseName(ByteBuffer buf, int depth,
+ boolean isNameCompressionSupported) throws
+ BufferUnderflowException, DnsPacket.ParseException {
+ if (depth > MAXLABELCOUNT) {
+ throw new DnsPacket.ParseException("Failed to parse name, too many labels");
+ }
+ final int len = Byte.toUnsignedInt(buf.get());
+ final int mask = len & NAME_COMPRESSION;
+ if (0 == len) {
+ return "";
+ } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION
+ || (!isNameCompressionSupported && mask == NAME_COMPRESSION)) {
+ throw new DnsPacket.ParseException("Parse name fail, bad label type: " + mask);
+ } else if (mask == NAME_COMPRESSION) {
+ // Name compression based on RFC 1035 - 4.1.4 Message compression
+ final int offset = ((len & ~NAME_COMPRESSION) << 8) + Byte.toUnsignedInt(buf.get());
+ final int oldPos = buf.position();
+ if (offset >= oldPos - 2) {
+ throw new DnsPacket.ParseException(
+ "Parse compression name fail, invalid compression");
+ }
+ buf.position(offset);
+ final String pointed = parseName(buf, depth + 1, isNameCompressionSupported);
+ buf.position(oldPos);
+ return pointed;
+ } else {
+ final byte[] label = new byte[len];
+ buf.get(label);
+ final String head = labelToString(label);
+ if (head.length() > MAXLABELSIZE) {
+ throw new DnsPacket.ParseException("Parse name fail, invalid label length");
+ }
+ final String tail = parseName(buf, depth + 1, isNameCompressionSupported);
+ return TextUtils.isEmpty(tail) ? head : head + "." + tail;
+ }
+ }
+
+ private DnsRecordParser() {}
+ }
+
+ private DnsPacketUtils() {}
+}
diff --git a/staticlibs/tests/unit/Android.bp b/staticlibs/tests/unit/Android.bp
index 07a8200..21e8c64 100644
--- a/staticlibs/tests/unit/Android.bp
+++ b/staticlibs/tests/unit/Android.bp
@@ -30,7 +30,8 @@
"//packages/modules/Connectivity/tests:__subpackages__",
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
"//packages/modules/NetworkStack/tests/integration",
- ]
+ ],
+ lint: { strict_updatability_linting: true },
}
android_test {
@@ -46,4 +47,5 @@
],
jarjar_rules: "jarjar-rules.txt",
test_suites: ["device-tests"],
+ lint: { strict_updatability_linting: true },
}
diff --git a/staticlibs/tests/unit/lint-baseline.xml b/staticlibs/tests/unit/lint-baseline.xml
deleted file mode 100644
index e3a6c1e..0000000
--- a/staticlibs/tests/unit/lint-baseline.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
- <issue
- id="NewApi"
- message="Call requires API level R (current min is 29): `android.app.AppOpsManager#noteOp`"
- errorLine1=" when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,"
- errorLine2=" ~~~~~~">
- <location
- file="frameworks/libs/net/common/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java"
- line="109"
- column="26"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level R (current min is 29): `android.app.AppOpsManager#noteOp`"
- errorLine1=" when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),"
- errorLine2=" ~~~~~~">
- <location
- file="frameworks/libs/net/common/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java"
- line="111"
- column="26"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level R (current min is 29): `android.app.AppOpsManager#noteOp`"
- errorLine1=" when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),"
- errorLine2=" ~~~~~~">
- <location
- file="frameworks/libs/net/common/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java"
- line="114"
- column="26"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level R (current min is 29): `android.net.NetworkCapabilities()`"
- errorLine1=" val nc = NetworkCapabilities()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/libs/net/common/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt"
- line="93"
- column="18"/>
- </issue>
-
-</issues>
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt
index f4a7d10..0067931 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt
@@ -18,13 +18,15 @@
import android.util.Log
import com.android.testutils.tryTest
-import kotlin.test.assertFailsWith
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
import kotlin.test.fail
-private val TAG = CleanupTest::class.toString()
+private val TAG = CleanupTest::class.simpleName
@RunWith(JUnit4::class)
class CleanupTest {
@@ -34,69 +36,140 @@
@Test
fun testNotThrow() {
var x = 1
- tryTest {
+ val result = tryTest {
x = 2
Log.e(TAG, "Do nothing")
+ 6
} cleanup {
- assert(x == 2)
+ assertTrue(x == 2)
x = 3
Log.e(TAG, "Do nothing")
}
- assert(x == 3)
+ assertTrue(x == 3)
+ assertTrue(result == 6)
}
@Test
fun testThrowTry() {
var x = 1
- assertFailsWith<TestException1> {
+ val thrown = assertFailsWith<TestException1> {
tryTest {
x = 2
throw TestException1()
x = 4
} cleanup {
- assert(x == 2)
+ assertTrue(x == 2)
x = 3
Log.e(TAG, "Do nothing")
}
}
- assert(x == 3)
+ assertTrue(thrown.suppressedExceptions.isEmpty())
+ assertTrue(x == 3)
}
@Test
fun testThrowCleanup() {
var x = 1
- assertFailsWith<TestException2> {
+ val thrown = assertFailsWith<TestException2> {
tryTest {
x = 2
Log.e(TAG, "Do nothing")
} cleanup {
- assert(x == 2)
+ assertTrue(x == 2)
x = 3
throw TestException2()
x = 4
}
}
- assert(x == 3)
+ assertTrue(thrown.suppressedExceptions.isEmpty())
+ assertTrue(x == 3)
}
@Test
fun testThrowBoth() {
var x = 1
- try {
+ val thrown = assertFailsWith<TestException1> {
tryTest {
x = 2
throw TestException1()
x = 3
} cleanup {
- assert(x == 2)
+ assertTrue(x == 2)
x = 4
throw TestException2()
x = 5
}
- fail("Expected failure with TestException1")
- } catch (e: TestException1) {
- assert(e.suppressedExceptions[0] is TestException2)
}
- assert(x == 4)
+ assertTrue(thrown.suppressedExceptions[0] is TestException2)
+ assertTrue(x == 4)
+ }
+
+ @Test
+ fun testReturn() {
+ val resultIfSuccess = 11
+ val resultIfException = 12
+ fun doTestReturn(crash: Boolean) = tryTest {
+ if (crash) throw RuntimeException() else resultIfSuccess
+ }.catch<RuntimeException> {
+ resultIfException
+ } cleanup {}
+
+ assertTrue(6 == tryTest { 6 } cleanup { Log.e(TAG, "tested") })
+ assertEquals(resultIfSuccess, doTestReturn(crash = false))
+ assertEquals(resultIfException, doTestReturn(crash = true))
+ }
+
+ @Test
+ fun testCatch() {
+ var x = 1
+ tryTest {
+ x = 2
+ throw TestException1()
+ x = 3
+ }.catch<TestException1> {
+ x = 4
+ }.catch<TestException2> {
+ x = 5
+ } cleanup {
+ assertTrue(x == 4)
+ x = 6
+ }
+ assertTrue(x == 6)
+ }
+
+ @Test
+ fun testNotCatch() {
+ var x = 1
+ assertFailsWith<TestException1> {
+ tryTest {
+ x = 2
+ throw TestException1()
+ }.catch<TestException2> {
+ fail("Caught TestException2 instead of TestException1")
+ } cleanup {
+ assertTrue(x == 2)
+ x = 3
+ }
+ }
+ assertTrue(x == 3)
+ }
+
+ @Test
+ fun testThrowInCatch() {
+ var x = 1
+ val thrown = assertFailsWith<TestException2> {
+ tryTest {
+ x = 2
+ throw TestException1()
+ }.catch<TestException1> {
+ x = 3
+ throw TestException2()
+ } cleanup {
+ assertTrue(x == 3)
+ x = 4
+ }
+ }
+ assertTrue(x == 4)
+ assertTrue(thrown.suppressedExceptions.isEmpty())
}
}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java
index ba4e679..83abfa1 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java
@@ -35,14 +35,16 @@
@Test
public void testNotThrow() {
final AtomicInteger x = new AtomicInteger(1);
- testAndCleanup(() -> {
+ final int a = testAndCleanup(() -> {
x.compareAndSet(1, 2);
Log.e(TAG, "Do nothing");
+ return 6;
}, () -> {
x.compareAndSet(2, 3);
Log.e(TAG, "Do nothing");
});
assertEquals(3, x.get());
+ assertEquals(6, a);
}
@Test
diff --git a/staticlibs/tests/unit/src/com/android/module/util/DnsPacketTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketTest.java
similarity index 100%
rename from staticlibs/tests/unit/src/com/android/module/util/DnsPacketTest.java
rename to staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketTest.java
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketUtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketUtilsTest.java
new file mode 100644
index 0000000..48777ac
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketUtilsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 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 com.android.net.module.util.DnsPacketUtils.DnsRecordParser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsPacketUtilsTest {
+
+ /**
+ * Verifies that the compressed NAME field in the answer section of the DNS message is parsed
+ * successfully when name compression is permitted. Additionally, verifies that a
+ * {@link DnsPacket.ParseException} is thrown in a hypothetical scenario where name compression
+ * is not expected.
+ */
+ @Test
+ public void testParsingAnswerSectionNameCompressed() throws Exception {
+ final byte[] v4blobNameCompressedAnswer = new byte[] {
+ /* Header */
+ 0x55, 0x66, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ (byte) 0xc0, 0x0c, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x01, 0x2b, /* TTL */
+ 0x00, 0x04, /* Data length */
+ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
+ };
+ final int answerOffsetBytePosition = 32;
+ final ByteBuffer nameCompressedBuf = ByteBuffer.wrap(v4blobNameCompressedAnswer);
+
+ nameCompressedBuf.position(answerOffsetBytePosition);
+ assertThrows(DnsPacket.ParseException.class, () -> DnsRecordParser.parseName(
+ nameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */false));
+
+ nameCompressedBuf.position(answerOffsetBytePosition);
+ String domainName = DnsRecordParser.parseName(
+ nameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */true);
+ assertEquals(domainName, "www.google.com");
+ }
+
+ /**
+ * Verifies that an uncompressed NAME field in the answer section of the DNS message is parsed
+ * successfully irrespective of whether name compression is permitted.
+ */
+ @Test
+ public void testParsingAnswerSectionNoNameCompression() throws Exception {
+ final byte[] v4blobNoNameCompression = new byte[] {
+ /* Header */
+ 0x55, 0x66, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x01, 0x2b, /* TTL */
+ 0x00, 0x04, /* Data length */
+ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
+ };
+ final int answerOffsetBytePosition = 32;
+ final ByteBuffer notNameCompressedBuf = ByteBuffer.wrap(v4blobNoNameCompression);
+
+ notNameCompressedBuf.position(answerOffsetBytePosition);
+ String domainName = DnsRecordParser.parseName(
+ notNameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */ true);
+ assertEquals(domainName, "www.google.com");
+
+ notNameCompressedBuf.position(answerOffsetBytePosition);
+ domainName = DnsRecordParser.parseName(
+ notNameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */ false);
+ assertEquals(domainName, "www.google.com");
+ }
+}
diff --git a/staticlibs/tests/unit/src/android/net/util/IpRangeTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/IpRangeTest.java
similarity index 98%
rename from staticlibs/tests/unit/src/android/net/util/IpRangeTest.java
rename to staticlibs/tests/unit/src/com/android/net/module/util/IpRangeTest.java
index 677db69..f44b17d 100644
--- a/staticlibs/tests/unit/src/android/net/util/IpRangeTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/IpRangeTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.net.module.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -28,8 +28,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.net.module.util.IpRange;
-
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/LinkPropertiesUtilsTest.java
similarity index 99%
rename from staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
rename to staticlibs/tests/unit/src/com/android/net/module/util/LinkPropertiesUtilsTest.java
index 45493bd..3d2d6eb 100644
--- a/staticlibs/tests/unit/src/android/net/util/LinkPropertiesUtilsTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/LinkPropertiesUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.net.module.util;
import static com.android.testutils.MiscAsserts.assertSameElements;
@@ -32,7 +32,6 @@
import androidx.test.runner.AndroidJUnit4;
-import com.android.net.module.util.LinkPropertiesUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java
index 3fb1e92..84018a5 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java
@@ -42,6 +42,8 @@
import android.os.UserHandle;
import android.os.UserManager;
+import androidx.annotation.RequiresApi;
+
import com.android.testutils.DevSdkIgnoreRule;
import org.junit.Assert;
@@ -56,6 +58,7 @@
import java.util.HashMap;
/** Unit tests for {@link LocationPermissionChecker}. */
+@RequiresApi(Build.VERSION_CODES.R)
public class LocationPermissionCheckerTest {
@Rule
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(
diff --git a/staticlibs/tests/unit/src/android/net/util/MacAddressUtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/MacAddressUtilsTest.java
similarity index 96%
rename from staticlibs/tests/unit/src/android/net/util/MacAddressUtilsTest.java
rename to staticlibs/tests/unit/src/com/android/net/module/util/MacAddressUtilsTest.java
index 8988571..2550756 100644
--- a/staticlibs/tests/unit/src/android/net/util/MacAddressUtilsTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/MacAddressUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.net.module.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -24,8 +24,6 @@
import androidx.test.runner.AndroidJUnit4;
-import com.android.net.module.util.MacAddressUtils;
-
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/staticlibs/tests/unit/src/android/net/util/NetUtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/NetUtilsTest.java
similarity index 97%
rename from staticlibs/tests/unit/src/android/net/util/NetUtilsTest.java
rename to staticlibs/tests/unit/src/com/android/net/module/util/NetUtilsTest.java
index d523e14..902c18e 100644
--- a/staticlibs/tests/unit/src/android/net/util/NetUtilsTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/NetUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.net.module.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -27,8 +27,6 @@
import androidx.test.runner.AndroidJUnit4;
-import com.android.net.module.util.NetUtils;
-
import org.junit.Test;
import org.junit.runner.RunWith;
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 5f15c6a..f78c74e 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
@@ -16,6 +16,7 @@
package com.android.net.module.util
+import android.annotation.TargetApi
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_CBS
import android.net.NetworkCapabilities.NET_CAPABILITY_EIMS
@@ -29,6 +30,7 @@
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE
+import android.os.Build
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.net.module.util.NetworkCapabilitiesUtils.RESTRICTED_CAPABILITIES
@@ -88,7 +90,9 @@
assertTrue(bits contentEquals unpackBits(packedBits))
}
- @Test
+ // NetworkCapabilities constructor and Builder are not available until R. Mark TargetApi to
+ // ignore the linter error since it's used in only unit test.
+ @Test @TargetApi(Build.VERSION_CODES.R)
fun testInferRestrictedCapability() {
val nc = NetworkCapabilities()
// Default capabilities don't have restricted capability.
diff --git a/staticlibs/testutils/Android.bp b/staticlibs/testutils/Android.bp
index 9fd30f7..1be64c1 100644
--- a/staticlibs/testutils/Android.bp
+++ b/staticlibs/testutils/Android.bp
@@ -38,28 +38,38 @@
"net-utils-device-common-netlink",
"modules-utils-build_system",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
- // Consider using net-tests-utils instead if writing device code.
- // That library has a lot more useful tools into it for users that
- // work on Android and includes this lib.
- name: "net-tests-utils-host-device-common",
- srcs: [
- "hostdevice/**/*.java",
- "hostdevice/**/*.kt",
- ],
- host_supported: true,
- visibility: [
- "//frameworks/libs/net/common/tests:__subpackages__",
- "//frameworks/libs/net/client-libs/tests:__subpackages__",
- ],
- libs: [
- "jsr305",
- ],
- static_libs: [
- "kotlin-test"
- ]
+ // Consider using net-tests-utils instead if writing device code.
+ // That library has a lot more useful tools into it for users that
+ // work on Android and includes this lib.
+ name: "net-tests-utils-host-device-common",
+ srcs: [
+ "hostdevice/**/*.java",
+ "hostdevice/**/*.kt",
+ ],
+ host_supported: true,
+ visibility: [
+ "//frameworks/libs/net/common/tests:__subpackages__",
+ "//frameworks/libs/net/client-libs/tests:__subpackages__",
+ ],
+ // There are downstream branches using an old version of Kotlin
+ // that used to reserve the right to make breaking changes to the
+ // Result type and disallowed returning an instance of it.
+ // Later versions allowed this and there was never a change,
+ // so no matter the version returning Result is always fine,
+ // but on sc-mainline-prod the compiler rejects it without
+ // the following flag.
+ kotlincflags: ["-Xallow-result-return-type"],
+ libs: [
+ "jsr305",
+ ],
+ static_libs: [
+ "kotlin-test"
+ ],
+ lint: { strict_updatability_linting: true },
}
java_test_host {
diff --git a/staticlibs/testutils/app/connectivitychecker/Android.bp b/staticlibs/testutils/app/connectivitychecker/Android.bp
index 79a4343..58f22db 100644
--- a/staticlibs/testutils/app/connectivitychecker/Android.bp
+++ b/staticlibs/testutils/app/connectivitychecker/Android.bp
@@ -30,4 +30,5 @@
"net-tests-utils",
],
host_required: ["net-tests-utils-host-common"],
+ lint: { strict_updatability_linting: true },
}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/ContextUtils.kt b/staticlibs/testutils/devicetests/com/android/testutils/ContextUtils.kt
new file mode 100644
index 0000000..814a75b
--- /dev/null
+++ b/staticlibs/testutils/devicetests/com/android/testutils/ContextUtils.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+@file:JvmName("ContextUtils")
+
+package com.android.testutils
+
+import android.content.Context
+import android.os.UserHandle
+import org.mockito.AdditionalAnswers.delegatesTo
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import java.util.function.BiConsumer
+
+// Helper function so that Java doesn't have to pass a method that returns Unit
+fun mockContextAsUser(context: Context, functor: BiConsumer<Context, UserHandle>? = null) =
+ mockContextAsUser(context) { c, h -> functor?.accept(c, h) }
+
+/**
+ * Return a context with assigned user and delegate to original context.
+ *
+ * @param context the mock context to set up createContextAsUser on. After this function
+ * is called, client code can call createContextAsUser and expect a context that
+ * will return the correct user and userId.
+ *
+ * @param functor additional code to run on the created context-as-user instances, for example to
+ * set up further mocks on these contexts.
+ */
+fun mockContextAsUser(context: Context, functor: ((Context, UserHandle) -> Unit)? = null) {
+ doAnswer { invocation ->
+ val asUserContext = mock(Context::class.java, delegatesTo<Context>(context))
+ val user = invocation.arguments[0] as UserHandle
+ val userId = user.identifier
+ doReturn(user).`when`(asUserContext).user
+ doReturn(userId).`when`(asUserContext).userId
+ functor?.let { it(asUserContext, user) }
+ asUserContext
+ }.`when`(context).createContextAsUser(any(UserHandle::class.java), anyInt() /* flags */)
+}
diff --git a/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt b/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt
index 769d980..1b67f68 100644
--- a/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt
+++ b/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt
@@ -19,8 +19,17 @@
package com.android.testutils
import com.android.testutils.ExceptionUtils.ThrowingRunnable
+import com.android.testutils.ExceptionUtils.ThrowingSupplier
import javax.annotation.CheckReturnValue
+@CheckReturnValue
+fun <T> tryTest(block: () -> T) = TryExpr(
+ try {
+ Result.success(block())
+ } catch (e: Throwable) {
+ Result.failure(e)
+ })
+
/**
* Utility to do cleanup in tests without replacing exceptions with those from a finally block.
*
@@ -51,6 +60,12 @@
* } cleanup {
* cleanup code
* }
+ * Catch blocks can be added with the following syntax :
+ * tryTest {
+ * testing code
+ * }.catch<ExceptionType> { it ->
+ * do something to it
+ * }
*
* Java doesn't allow this kind of syntax, so instead a function taking 2 lambdas is provided.
* testAndCleanup(() -> {
@@ -59,12 +74,26 @@
* cleanup code
* });
*/
-class ExceptionCleanupBlock(val originalException: Exception?) {
- inline infix fun cleanup(block: () -> Unit) {
+
+// Some downstream branches have an older kotlin that doesn't know about value classes.
+// TODO : Change this to "value class" when aosp no longer merges into such branches.
+@Suppress("INLINE_CLASS_DEPRECATED")
+inline class TryExpr<T>(val result: Result<T>) {
+ inline infix fun <reified E : Throwable> catch(block: (E) -> T): TryExpr<T> {
+ val originalException = result.exceptionOrNull()
+ if (originalException !is E) return this
+ return TryExpr(try {
+ Result.success(block(originalException))
+ } catch (e: Exception) {
+ Result.failure(e)
+ })
+ }
+
+ inline infix fun cleanup(block: () -> Unit): T {
try {
block()
- if (null != originalException) throw originalException
- } catch (e: Exception) {
+ } catch (e: Throwable) {
+ val originalException = result.exceptionOrNull()
if (null == originalException) {
throw e
} else {
@@ -72,24 +101,18 @@
throw originalException
}
}
+ return result.getOrThrow()
}
}
-@CheckReturnValue
-inline fun tryTest(block: () -> Unit): ExceptionCleanupBlock {
- try {
- block()
- } catch (e: Exception) {
- return ExceptionCleanupBlock(e)
- }
- return ExceptionCleanupBlock(null)
-}
-
// Java support
-fun testAndCleanup(tryBlock: ThrowingRunnable, cleanupBlock: ThrowingRunnable) {
- tryTest {
- tryBlock.run()
+fun <T> testAndCleanup(tryBlock: ThrowingSupplier<T>, cleanupBlock: ThrowingRunnable): T {
+ return tryTest {
+ tryBlock.get()
} cleanup {
cleanupBlock.run()
}
}
+fun testAndCleanup(tryBlock: ThrowingRunnable, cleanupBlock: ThrowingRunnable) {
+ return testAndCleanup(ThrowingSupplier { tryBlock.run() }, cleanupBlock)
+}