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